Merge branch 'hotfix/v2.6.1' into develop

# Conflicts:
#	packages/client/src/pages/PortalSettings/Layout/Section/Header/index.js
#	packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/Stepper/SelectUsersStep/index.js
#	packages/client/src/pages/PortalSettings/categories/data-import/components/AddEmailsStep/AccountsTable/RowView/UsersRowContent.tsx
#	packages/client/src/pages/PortalSettings/utils/settingsTree.js
This commit is contained in:
Alexey Safronov 2024-07-29 16:13:17 +04:00
commit 602ce53411
74 changed files with 932 additions and 445 deletions

View File

@ -10,13 +10,16 @@
"ExpectUsers": "Expect users", "ExpectUsers": "Expect users",
"FeedLinkWasDeleted": "Link was deleted", "FeedLinkWasDeleted": "Link was deleted",
"FeedLocationLabel": "Folder «{{folderTitle}}»", "FeedLocationLabel": "Folder «{{folderTitle}}»",
"FeedLocationLabelFrom": "from «{{folderTitle}}»",
"FeedLocationRoomLabel": "Room «{{folderTitle}}»", "FeedLocationRoomLabel": "Room «{{folderTitle}}»",
"FileConverted": "File converted.", "FileConverted": "File converted.",
"FileCopied": "Files copied.", "FileCopied": "Files copied.",
"FileCopiedTo": "Files copied to «{{folderTitle}}»",
"FileCreated": "File created.", "FileCreated": "File created.",
"FileDeleted": "Files removed.", "FileDeleted": "Files removed.",
"FileExtension": "File extension", "FileExtension": "File extension",
"FileMoved": "Files moved.", "FileMoved": "Files moved.",
"FileMovedTo": "File moved to «{{folderTitle}}»",
"FileRenamed": "File renamed.", "FileRenamed": "File renamed.",
"FilesEmptyScreenText": "See file and folder details here", "FilesEmptyScreenText": "See file and folder details here",
"FileUploaded": "Files added.", "FileUploaded": "Files added.",

View File

@ -424,6 +424,7 @@ export default inject(
isRestore, isRestore,
isPanelVisible, isPanelVisible,
id, id,
currentFolderId: currentFolderIdProp,
}: FilesSelectorProps, }: FilesSelectorProps,
) => { ) => {
const { id: selectedId, parentId, rootFolderType } = selectedFolderStore; const { id: selectedId, parentId, rootFolderType } = selectedFolderStore;
@ -551,7 +552,7 @@ export default inject(
getIcon, getIcon,
roomsFolderId, roomsFolderId,
currentFolderId: folderId, currentFolderId: folderId || currentFolderIdProp,
filesSettings, filesSettings,
}; };
}, },

View File

@ -29,4 +29,8 @@ import { ModalDialog } from "@docspace/shared/components/modal-dialog";
export const StyledModal = styled(ModalDialog)` export const StyledModal = styled(ModalDialog)`
user-select: none; user-select: none;
.modal-body {
padding-top: 20px;
}
`; `;

View File

@ -24,8 +24,15 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
export const DEFAULT_MIN_COLUMN_SIZE = 110; import styled from "styled-components";
export const SETTINGS_SIZE = 24; import { ModalDialog } from "@docspace/shared/components/modal-dialog";
export const CONTAINER_MARGIN = 25;
export const MIN_SIZE_FIRST_COLUMN = 210; export const StyledModalDialog = styled(ModalDialog)`
export const TABLE_HEADER_HEIGHT = 40; .modal-body {
padding: 0;
}
.search-input {
margin: 16px 16px 12px;
}
`;

View File

@ -24,23 +24,32 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { import {
ModalDialog, ModalDialog,
ModalDialogType, ModalDialogType,
} from "@docspace/shared/components/modal-dialog"; } from "@docspace/shared/components/modal-dialog";
import { observer, inject } from "mobx-react";
import { useState, useEffect, useTransition } from "react";
import { getGroupMembersInRoom } from "@docspace/shared/api/groups"; import { getGroupMembersInRoom } from "@docspace/shared/api/groups";
import { useTranslation } from "react-i18next";
import { InputSize } from "@docspace/shared/components/text-input"; import { InputSize } from "@docspace/shared/components/text-input";
import { SearchInput } from "@docspace/shared/components/search-input"; import { SearchInput } from "@docspace/shared/components/search-input";
import GroupMember from "./GroupMember"; import {
TGroup,
TGroupMemberInvitedInRoom,
} from "@docspace/shared/api/groups/types";
import EmptyContainer from "./EmptyContainer"; import EmptyContainer from "./EmptyContainer";
import GroupMembersList from "./sub-components/GroupMembersList/GroupMembersList";
import { StyledModalDialog } from "./EditGroupMembersDialog.styled";
import { ModalBodyLoader } from "./sub-components/ModalBodyLoader/ModalBodyLoader";
import { MIN_LOADER_TIMER } from "@docspace/shared/selectors/Files/FilesSelector.constants";
interface EditGroupMembersProps { interface EditGroupMembersProps {
visible: boolean; visible: boolean;
setVisible: (visible: boolean) => void; setVisible: (visible: boolean) => void;
group: any; group: TGroup;
infoPanelSelection: any; infoPanelSelection: any;
} }
@ -53,33 +62,63 @@ const EditGroupMembers = ({
const { t } = useTranslation(["Common"]); const { t } = useTranslation(["Common"]);
const [searchValue, setSearchValue] = useState<string>(""); const [searchValue, setSearchValue] = useState<string>("");
const onChangeSearchValue = (newValue: string) => { const [total, setTotal] = useState(0);
setSearchValue(newValue); const [groupMembers, setGroupMembers] = useState<
}; TGroupMemberInvitedInRoom[] | null
const onClearSearch = () => onChangeSearchValue(""); >(null);
const [isSearchResultLoading, setIsSearchResultLoading] = useState(false);
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
const [groupMembers, setGroupMembers] = useState<any[] | null>(null); const onChangeSearchValue = (value: string) => {
const filteredGroupMembers = groupMembers?.filter((groupMember) => setIsSearchResultLoading(true);
groupMember.user.displayName.includes(searchValue), setSearchValue(value.trim());
); };
const [, startTransition] = useTransition();
const onClearSearch = () => onChangeSearchValue("");
const onClose = () => setVisible(false); const onClose = () => setVisible(false);
const isSearchListEmpty = const loadNextPage = async (startIndex: number) => {
filteredGroupMembers && !filteredGroupMembers.length; const startLoadingTime = new Date();
const hasMembers = filteredGroupMembers && filteredGroupMembers.length !== 0;
try {
setIsNextPageLoading(true);
const filter = { startIndex, count: 100, filterValue: searchValue };
const data = await getGroupMembersInRoom(
infoPanelSelection.id,
group.id,
filter,
);
setTotal(data.total);
if (startIndex === 0 || !groupMembers) {
setGroupMembers(data.items);
} else {
setGroupMembers([...groupMembers, ...data.items]);
}
} catch (e) {
console.error(e);
} finally {
const nowDate = new Date();
const diff = Math.abs(nowDate.getTime() - startLoadingTime.getTime());
if (diff < MIN_LOADER_TIMER) {
setTimeout(() => {
setIsSearchResultLoading(false);
}, MIN_LOADER_TIMER - diff);
} else {
setIsSearchResultLoading(false);
}
setIsNextPageLoading(false);
// setIsSearchResultLoading(false);
}
};
useEffect(() => { useEffect(() => {
const fetchGroup = async () => { loadNextPage(0);
if (!group) return; // eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchValue]);
getGroupMembersInRoom(infoPanelSelection.id, group.id)!
.then((data: any) => startTransition(() => setGroupMembers(data.items)))
.catch((err: any) => console.error(err));
};
fetchGroup();
}, [group, infoPanelSelection.id]);
if (!infoPanelSelection?.isRoom) { if (!infoPanelSelection?.isRoom) {
onClose(); onClose();
@ -87,7 +126,7 @@ const EditGroupMembers = ({
} }
return ( return (
<ModalDialog <StyledModalDialog
visible={visible} visible={visible}
onClose={onClose} onClose={onClose}
displayType={ModalDialogType.aside} displayType={ModalDialogType.aside}
@ -95,29 +134,40 @@ const EditGroupMembers = ({
<ModalDialog.Header>{group.name}</ModalDialog.Header> <ModalDialog.Header>{group.name}</ModalDialog.Header>
<ModalDialog.Body> <ModalDialog.Body>
<SearchInput {!groupMembers ? (
className="search-input" <ModalBodyLoader withSearch />
placeholder={t("PeopleTranslations:SearchByGroupMembers")} ) : (
value={searchValue} <>
onChange={onChangeSearchValue} <SearchInput
onClearSearch={onClearSearch} className="search-input"
size={InputSize.base} placeholder={t("PeopleTranslations:SearchByGroupMembers")}
/> value={searchValue}
onChange={onChangeSearchValue}
onClearSearch={onClearSearch}
size={InputSize.base}
/>
<div style={{ height: "12px", width: "100%" }} /> {isSearchResultLoading ? (
<ModalBodyLoader withSearch={false} />
{isSearchListEmpty && <EmptyContainer />} ) : !groupMembers.length ? (
<EmptyContainer />
{hasMembers && ) : (
filteredGroupMembers.map(({ user, ...rest }) => ( <GroupMembersList
<GroupMember t={t} key={user.id} user={{ ...user, ...rest }} /> members={groupMembers}
))} loadNextPage={loadNextPage}
hasNextPage={groupMembers.length < total}
total={total}
isNextPageLoading={isNextPageLoading}
/>
)}
</>
)}
</ModalDialog.Body> </ModalDialog.Body>
</ModalDialog> </StyledModalDialog>
); );
}; };
export default inject(({ infoPanelStore, userStore, dialogsStore }) => ({ export default inject(({ infoPanelStore, userStore, dialogsStore }: any) => ({
infoPanelSelection: infoPanelStore.infoPanelSelection, infoPanelSelection: infoPanelStore.infoPanelSelection,
selfId: userStore.user.id, selfId: userStore.user.id,
group: dialogsStore.editMembersGroup, group: dialogsStore.editMembersGroup,

View File

@ -31,7 +31,7 @@ export const GroupMember = styled.div<{ isExpect: boolean }>`
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
padding: 8px 0; padding: 8px 16px;
.avatar { .avatar {
min-width: 32px; min-width: 32px;

View File

@ -24,39 +24,47 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { useState } from "react";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import { isMobile, isMobileOnly } from "react-device-detect";
import AtReactSvgUrl from "PUBLIC_DIR/images/@.react.svg?url"; import AtReactSvgUrl from "PUBLIC_DIR/images/@.react.svg?url";
// import { StyledUser } from "../../styles/members";
import { Avatar } from "@docspace/shared/components/avatar"; import { Avatar } from "@docspace/shared/components/avatar";
import { ComboBox } from "@docspace/shared/components/combobox"; import { ComboBox } from "@docspace/shared/components/combobox";
import DefaultUserPhotoUrl from "PUBLIC_DIR/images/default_user_photo_size_82-82.png"; import DefaultUserPhotoUrl from "PUBLIC_DIR/images/default_user_photo_size_82-82.png";
import { isMobileOnly, isMobile } from "react-device-detect";
import { decode } from "he"; import { decode } from "he";
import { filterUserRoleOptions } from "SRC_DIR/helpers"; import { filterUserRoleOptions } from "SRC_DIR/helpers";
import { Text } from "@docspace/shared/components/text"; import { Text } from "@docspace/shared/components/text";
import * as Styled from "./index.styled";
import { getUserRoleOptionsByUserAccess } from "@docspace/shared/utils/room-members/getUserRoleOptionsByUserAccess"; import { getUserRoleOptionsByUserAccess } from "@docspace/shared/utils/room-members/getUserRoleOptionsByUserAccess";
import { getUserRoleOptionsByRoomType } from "@docspace/shared/utils/room-members/getUserRoleOptionsByRoomType"; import { getUserRoleOptionsByRoomType } from "@docspace/shared/utils/room-members/getUserRoleOptionsByRoomType";
import { updateRoomMemberRole } from "@docspace/shared/api/rooms"; import { updateRoomMemberRole } from "@docspace/shared/api/rooms";
import { toastr } from "@docspace/shared/components/toast"; import { toastr } from "@docspace/shared/components/toast";
import { useState } from "react";
import { HelpButton } from "@docspace/shared/components/help-button"; import { HelpButton } from "@docspace/shared/components/help-button";
import { getUserRoleOptions } from "@docspace/shared/utils/room-members/getUserRoleOptions"; import { getUserRoleOptions } from "@docspace/shared/utils/room-members/getUserRoleOptions";
import { ShareAccessRights } from "@docspace/shared/enums"; import { ShareAccessRights } from "@docspace/shared/enums";
import { getUserRole, getUserTypeLabel } from "@docspace/shared/utils/common"; import { getUserRole } from "@docspace/shared/utils/common";
import { TGroupMemberInvitedInRoom } from "@docspace/shared/api/groups/types";
import * as Styled from "./index.styled";
interface GroupMemberProps { interface GroupMemberProps {
t: any; member: TGroupMemberInvitedInRoom;
user: any;
infoPanelSelection: any; infoPanelSelection: any;
} }
const GroupMember = ({ t, user, infoPanelSelection }: GroupMemberProps) => { const GroupMember = ({ member, infoPanelSelection }: GroupMemberProps) => {
const { user } = member;
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation("Common");
const userRole = user.isOwner const userRole = user.isOwner
? getUserRoleOptions(t).portalAdmin ? getUserRoleOptions(t).portalAdmin
: getUserRoleOptionsByUserAccess(t, user.userAccess || user.groupAccess); : getUserRoleOptionsByUserAccess(
t,
member.userAccess || member.groupAccess,
);
const fullRoomRoleOptions = getUserRoleOptionsByRoomType( const fullRoomRoleOptions = getUserRoleOptionsByRoomType(
t, t,
@ -86,7 +94,7 @@ const GroupMember = ({ t, user, infoPanelSelection }: GroupMemberProps) => {
else else
selectedUserRoleCBOption = getUserRoleOptionsByUserAccess( selectedUserRoleCBOption = getUserRoleOptionsByUserAccess(
t, t,
user.userAccess || user.groupAccess, member.userAccess || member.groupAccess,
); );
const availableUserRoleCBOptions = filterUserRoleOptions( const availableUserRoleCBOptions = filterUserRoleOptions(
@ -101,7 +109,7 @@ const GroupMember = ({ t, user, infoPanelSelection }: GroupMemberProps) => {
notify: false, notify: false,
sharingMessage: "", sharingMessage: "",
}) })
.then(() => (user.userAccess = userRoleOption.access)) .then(() => (member.userAccess = userRoleOption.access))
.catch((err) => toastr.error(err)) .catch((err) => toastr.error(err))
.finally(() => setIsLoading(false)); .finally(() => setIsLoading(false));
}; };
@ -134,8 +142,8 @@ const GroupMember = ({ t, user, infoPanelSelection }: GroupMemberProps) => {
</div> </div>
<div className="individual-rights-tooltip"> <div className="individual-rights-tooltip">
{user.userAccess && {member.userAccess &&
user.userAccess !== user.groupAccess && member.userAccess !== member.groupAccess &&
!user.isOwner && ( !user.isOwner && (
<HelpButton <HelpButton
place="left" place="left"
@ -152,7 +160,7 @@ const GroupMember = ({ t, user, infoPanelSelection }: GroupMemberProps) => {
{userRole && userRoleOptions && ( {userRole && userRoleOptions && (
<div className="role-wrapper"> <div className="role-wrapper">
{user.canEditAccess && !user.isOwner ? ( {member.canEditAccess && !user.isOwner ? (
<ComboBox <ComboBox
className="role-combobox" className="role-combobox"
selectedOption={userRole} selectedOption={userRole}
@ -180,6 +188,6 @@ const GroupMember = ({ t, user, infoPanelSelection }: GroupMemberProps) => {
); );
}; };
export default inject(({ infoPanelStore }) => ({ export default inject(({ infoPanelStore }: any) => ({
infoPanelSelection: infoPanelStore.infoPanelSelection, infoPanelSelection: infoPanelStore.infoPanelSelection,
}))(observer(GroupMember)); }))(observer(GroupMember));

View File

@ -0,0 +1,126 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React, { memo, useCallback } from "react";
import { useTheme } from "styled-components";
import { areEqual, FixedSizeList as List } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import InfiniteLoader from "react-window-infinite-loader";
import { Scrollbar } from "@docspace/shared/components/scrollbar";
import { RowLoader } from "@docspace/shared/skeletons/selector";
import { TGroupMemberInvitedInRoom } from "@docspace/shared/api/groups/types";
import GroupMember from "../GroupMember";
const ROW_HEIGHT = 48;
const SEARCH_WITH_PADDING_HEIGHT = 60;
const Row = memo(
({
data,
index,
style,
}: {
data: TGroupMemberInvitedInRoom[];
index: number;
style: React.CSSProperties;
}) => {
const member = data[index];
return (
<div style={style}>
{member ? (
<GroupMember member={member} />
) : (
<RowLoader isMultiSelect={false} isContainer isUser />
)}
</div>
);
},
areEqual,
);
Row.displayName = "Row";
interface GroupMembersListProps {
members: TGroupMemberInvitedInRoom[];
loadNextPage: (startIndex: number) => void;
hasNextPage: boolean;
total: number;
isNextPageLoading: boolean;
}
const GroupMembersList = ({
members,
loadNextPage,
hasNextPage,
total,
isNextPageLoading,
}: GroupMembersListProps) => {
const { interfaceDirection } = useTheme();
const itemCount = hasNextPage ? members.length + 1 : members.length;
const isItemLoaded = useCallback(
(index: number) => {
return !hasNextPage || index < itemCount - 1;
},
[hasNextPage, itemCount],
);
const loadMoreItems = isNextPageLoading ? () => {} : loadNextPage;
return (
<AutoSizer>
{({ height, width }) => (
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={total}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
ref={ref}
direction={interfaceDirection}
height={height - SEARCH_WITH_PADDING_HEIGHT}
width={width}
itemCount={itemCount}
itemSize={ROW_HEIGHT}
itemData={members}
outerElementType={Scrollbar}
onItemsRendered={onItemsRendered}
>
{Row}
</List>
)}
</InfiniteLoader>
)}
</AutoSizer>
);
};
export default GroupMembersList;

View File

@ -0,0 +1,44 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { SearchLoader } from "@docspace/shared/skeletons/selector";
import React from "react";
import { MemberLoader } from "@docspace/shared/skeletons/info-panel/body/views/MembersLoader";
interface ModalBodyLoaderProps {
withSearch: boolean;
}
export const ModalBodyLoader = ({ withSearch }: ModalBodyLoaderProps) => {
return (
<div style={{ paddingTop: withSearch ? "16px" : "0" }}>
{withSearch && <SearchLoader />}
<div style={{ paddingInline: "16px" }}>
<MemberLoader count={25} />
</div>
</div>
);
};

View File

@ -24,7 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { useState, useEffect } from "react"; import { useState } from "react";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import { Aside } from "@docspace/shared/components/aside"; import { Aside } from "@docspace/shared/components/aside";
@ -83,20 +83,12 @@ const ChangeRoomOwner = (props) => {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useEffect(() => { const onChangeRoomOwner = async (
document.addEventListener("keyup", onKeyUp, false); user,
selectedAccess,
return () => { newFooterInputValue,
document.removeEventListener("keyup", onKeyUp, false); isChecked,
}; ) => {
}, []);
const onKeyUp = (e) => {
if (e.keyCode === 27) onClose();
if (e.keyCode === 13 || e.which === 13) onChangeRoomOwner();
};
const onChangeRoomOwner = async (user, isChecked) => {
if (showBackButton) { if (showBackButton) {
setRoomParams && setRoomParams(user[0]); setRoomParams && setRoomParams(user[0]);
} else { } else {

View File

@ -96,14 +96,8 @@ const StyledFileRow = styled(Row)`
.password-input { .password-input {
position: absolute; position: absolute;
top: 48px; top: 48px;
${(props) => left: 0px;
props.theme.interfaceDirection === "rtl" right: 0px;
? css`
right: 16px;
`
: css`
left: 16px;
`}
max-width: 470px; max-width: 470px;
width: calc(100% - 16px); width: calc(100% - 16px);
display: flex; display: flex;

View File

@ -33,6 +33,9 @@ import { useTheme } from "styled-components";
const StyledLoadErrorIcon = styled(LoadErrorIcon)` const StyledLoadErrorIcon = styled(LoadErrorIcon)`
outline: none !important; outline: none !important;
path {
fill: ${(props) => props.theme.filesPanels.upload.iconColor};
}
`; `;
const ErrorFileUpload = ({ const ErrorFileUpload = ({

View File

@ -0,0 +1,27 @@
import { getLanguage } from "@docspace/shared/utils";
import { translations } from "./autoGeneratedTranslations";
export function loadLanguagePath(homepage, fixedNS = null) {
return (lng, ns) => {
const language = getLanguage(lng instanceof Array ? lng[0] : lng);
const lngCollection = translations?.get(language);
const data = lngCollection?.get(`${fixedNS || ns}`);
if (!data) return `/locales/${language}/${fixedNS || ns}.json`;
let path = data?.split("/");
const length = path?.length;
const isCommonPath = path[length - 1].indexOf("Common") > -1;
path = `/${path[length - 3]}/${path[length - 2]}/${path[length - 1]}`;
if (ns.length > 0 && ns[0] === "Common" && isCommonPath) {
return `/static${path}`;
}
return path;
};
}

View File

@ -25,12 +25,10 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { authStore } from "@docspace/shared/store"; import { authStore } from "@docspace/shared/store";
import { getLanguage } from "@docspace/shared/utils";
import { toCommunityHostname } from "@docspace/shared/utils/common"; import { toCommunityHostname } from "@docspace/shared/utils/common";
import { CategoryType } from "./constants"; import { CategoryType } from "./constants";
import { FolderType, ShareAccessRights } from "@docspace/shared/enums"; import { FolderType } from "@docspace/shared/enums";
import { translations } from "./autoGeneratedTranslations";
// import router from "SRC_DIR/router"; // import router from "SRC_DIR/router";
import i18n from "../i18n"; import i18n from "../i18n";
@ -54,31 +52,6 @@ export const setDocumentTitle = (subTitle = "") => {
document.title = title; document.title = title;
}; };
export function loadLanguagePath(homepage, fixedNS = null) {
return (lng, ns) => {
const language = getLanguage(lng instanceof Array ? lng[0] : lng);
const lngCollection = translations?.get(language);
const data = lngCollection?.get(`${fixedNS || ns}`);
if (!data) return `/locales/${language}/${fixedNS || ns}.json`;
let path = data?.split("/");
const length = path?.length;
const isCommonPath = path[length - 1].indexOf("Common") > -1;
path = `/${path[length - 3]}/${path[length - 2]}/${path[length - 1]}`;
if (ns.length > 0 && ns[0] === "Common" && isCommonPath) {
return `/static${path}`;
}
return path;
};
}
export const checkIfModuleOld = (link) => { export const checkIfModuleOld = (link) => {
if ( if (
!link || !link ||

View File

@ -31,7 +31,7 @@ import config from "PACKAGE_FILE";
import { LANGUAGE } from "@docspace/shared/constants"; import { LANGUAGE } from "@docspace/shared/constants";
import { getCookie } from "@docspace/shared/utils"; import { getCookie } from "@docspace/shared/utils";
import { loadLanguagePath } from "./helpers/utils"; import { loadLanguagePath } from "./helpers/language-helpers";
const newInstance = i18n.createInstance(); const newInstance = i18n.createInstance();

View File

@ -32,7 +32,7 @@ import FilesFilter from "@docspace/shared/api/files/filter";
import RoomsFilter from "@docspace/shared/api/rooms/filter"; import RoomsFilter from "@docspace/shared/api/rooms/filter";
import { getGroup } from "@docspace/shared/api/groups"; import { getGroup } from "@docspace/shared/api/groups";
import { getUserById } from "@docspace/shared/api/people"; import { getUserById } from "@docspace/shared/api/people";
import { MEDIA_VIEW_URL } from "@docspace/shared/constants"; import { CREATED_FORM_KEY, MEDIA_VIEW_URL } from "@docspace/shared/constants";
import { import {
Events, Events,
@ -78,6 +78,7 @@ const useFiles = ({
scrollToTop, scrollToTop,
selectedFolderStore, selectedFolderStore,
wsCreatedPDFForm,
}) => { }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { id } = useParams(); const { id } = useParams();
@ -292,7 +293,15 @@ const useFiles = ({
); );
} else { } else {
const folderId = filter.folder; const folderId = filter.folder;
return fetchFiles(folderId, filter); return fetchFiles(folderId, filter)?.finally(() => {
const data = sessionStorage.getItem(CREATED_FORM_KEY);
if (data) {
wsCreatedPDFForm({
data,
});
sessionStorage.removeItem(CREATED_FORM_KEY);
}
});
} }
} }
@ -306,7 +315,7 @@ const useFiles = ({
const isFormRoom = const isFormRoom =
selectedFolderStore.roomType === RoomsType.FormRoom || selectedFolderStore.roomType === RoomsType.FormRoom ||
selectedFolderStore.type === FolderType.FormRoom; selectedFolderStore.parentRoomType === FolderType.FormRoom;
const payload = { const payload = {
extension: "pdf", extension: "pdf",

View File

@ -122,7 +122,8 @@ const StyledHistoryBlockMessage = styled.div`
overflow: hidden; overflow: hidden;
} }
.folder-label { .folder-label,
.source-folder-label {
max-width: 100%; max-width: 100%;
color: ${(props) => props.theme.infoPanel.history.locationIconColor}; color: ${(props) => props.theme.infoPanel.history.locationIconColor};
text-overflow: ellipsis; text-overflow: ellipsis;
@ -130,6 +131,10 @@ const StyledHistoryBlockMessage = styled.div`
overflow: hidden; overflow: hidden;
} }
.source-folder-label {
color: ${(props) => props.theme.infoPanel.history.messageColor};
}
.old-role { .old-role {
color: ${(props) => props.theme.infoPanel.history.oldRoleColor}; color: ${(props) => props.theme.infoPanel.history.oldRoleColor};
font-weight: 600; font-weight: 600;

View File

@ -40,24 +40,38 @@ const HistoryMainTextFolderInfo = ({
feed, feed,
selectedFolderId, selectedFolderId,
}: HistoryMainTextFolderInfoProps) => { }: HistoryMainTextFolderInfoProps) => {
if ( const {
feed.data.parentId === selectedFolderId || parentId,
feed.data.toFolderId === selectedFolderId toFolderId,
) parentTitle,
parentType,
fromParentType,
fromParentTitle,
} = feed.data;
if (parentId === selectedFolderId || toFolderId === selectedFolderId)
return null; return null;
const destination = if (!parentTitle) return null;
feed.data.parentType === 0 || feed.data.toParentType === 0
? t("FeedLocationLabel", { const isFolder = parentType === 0;
folderTitle: feed.data.parentTitle || feed.data.toFolderTitle, const isFromFolder = fromParentType === 0;
})
: t("FeedLocationRoomLabel", { const destination = isFolder
folderTitle: feed.data.parentTitle || feed.data.toFolderTitle, ? t("FeedLocationLabel", { folderTitle: parentTitle })
}); : t("FeedLocationRoomLabel", { folderTitle: parentTitle });
const sourceDestination = isFromFolder
? t("FeedLocationLabelFrom", { folderTitle: fromParentTitle })
: t("FeedLocationRoomLabel", { folderTitle: parentTitle });
const className = isFromFolder ? "source-folder-label" : "folder-label";
return ( return (
<StyledHistoryBlockMessage className="message"> <StyledHistoryBlockMessage className="message">
<span className="folder-label">{destination}</span> <span className={className}>
{isFromFolder ? sourceDestination : destination}
</span>
</StyledHistoryBlockMessage> </StyledHistoryBlockMessage>
); );
}; };

View File

@ -114,7 +114,7 @@ export default inject<TStore>(({ dialogsStore, infoPanelStore }) => {
const { infoPanelSelection } = infoPanelStore; const { infoPanelSelection } = infoPanelStore;
const { setLinkParams, setEditLinkPanelIsVisible } = dialogsStore; const { setLinkParams, setEditLinkPanelIsVisible } = dialogsStore;
const { id, roomType, security } = infoPanelSelection!; const { id, roomType, security } = infoPanelSelection!;
const { EditRoom } = security!; const { EditRoom } = security || {};
const isFormRoom = roomType === RoomsType.FormRoom; const isFormRoom = roomType === RoomsType.FormRoom;

View File

@ -18,8 +18,18 @@ export const useFeedTranslation = (
case "FileRenamed": case "FileRenamed":
return t("InfoPanel:FileRenamed"); return t("InfoPanel:FileRenamed");
case "FileMoved": case "FileMoved":
if (feed.data.fromParentTitle) {
return t("InfoPanel:FileMovedTo", {
folderTitle: feed.data.parentTitle,
});
}
return t("InfoPanel:FileMoved"); return t("InfoPanel:FileMoved");
case "FileCopied": case "FileCopied":
if (feed.data.fromParentTitle) {
return t("InfoPanel:FileCopiedTo", {
folderTitle: feed.data.parentTitle,
});
}
return t("InfoPanel:FileCopied"); return t("InfoPanel:FileCopied");
case "FileDeleted": case "FileDeleted":
return t("InfoPanel:FileDeleted"); return t("InfoPanel:FileDeleted");

View File

@ -61,6 +61,8 @@ class GroupsTableHeader extends React.Component {
]; ];
const columns = props.getColumns(defaultColumns); const columns = props.getColumns(defaultColumns);
const tableColumns = columns.map((c) => c.enable && c.key);
this.setTableColumns(tableColumns);
this.state = { columns }; this.state = { columns };
} }
@ -77,7 +79,7 @@ class GroupsTableHeader extends React.Component {
this.setState({ columns }); this.setState({ columns });
const tableColumns = columns.map((c) => c.enable && c.key); const tableColumns = columns.map((c) => c.enable && c.key);
localStorage.setItem(`${TABLE_COLUMNS}=${this.props.userId}`, tableColumns); this.setTableColumns(tableColumns);
const event = new Event(Events.CHANGE_COLUMN); const event = new Event(Events.CHANGE_COLUMN);
window.dispatchEvent(event); window.dispatchEvent(event);
@ -103,6 +105,10 @@ class GroupsTableHeader extends React.Component {
navigate(`${location.pathname}?${newFilter.toUrlParams()}`); navigate(`${location.pathname}?${newFilter.toUrlParams()}`);
}; };
setTableColumns = (tableColumns) => {
localStorage.setItem(`${TABLE_COLUMNS}=${this.props.userId}`, tableColumns);
};
render() { render() {
const { columns } = this.state; const { columns } = this.state;
const { const {

View File

@ -102,6 +102,8 @@ class InsideGroupTableHeader extends React.Component {
}); });
const columns = props.getColumns(defaultColumns); const columns = props.getColumns(defaultColumns);
const tableColumns = columns.map((c) => c.enable && c.key);
this.setTableColumns(tableColumns);
this.state = { columns }; this.state = { columns };
} }
@ -118,7 +120,7 @@ class InsideGroupTableHeader extends React.Component {
this.setState({ columns }); this.setState({ columns });
const tableColumns = columns.map((c) => c.enable && c.key); const tableColumns = columns.map((c) => c.enable && c.key);
localStorage.setItem(`${TABLE_COLUMNS}=${this.props.userId}`, tableColumns); this.setTableColumns(tableColumns);
const event = new Event(Events.CHANGE_COLUMN); const event = new Event(Events.CHANGE_COLUMN);
@ -156,6 +158,10 @@ class InsideGroupTableHeader extends React.Component {
navigate(`${location.pathname}?${newFilter.toUrlParams()}`); navigate(`${location.pathname}?${newFilter.toUrlParams()}`);
}; };
setTableColumns = (tableColumns) => {
localStorage.setItem(`${TABLE_COLUMNS}=${this.props.userId}`, tableColumns);
};
render() { render() {
const { columns } = this.state; const { columns } = this.state;
const { const {

View File

@ -160,6 +160,7 @@ const PureHome = (props) => {
getFolderModel, getFolderModel,
scrollToTop, scrollToTop,
isEmptyGroups, isEmptyGroups,
wsCreatedPDFForm,
} = props; } = props;
//console.log(t("ComingSoon")) //console.log(t("ComingSoon"))
@ -206,6 +207,7 @@ const PureHome = (props) => {
scrollToTop, scrollToTop,
selectedFolderStore, selectedFolderStore,
wsCreatedPDFForm,
}); });
const { showUploadPanel } = useOperations({ const { showUploadPanel } = useOperations({
@ -500,6 +502,7 @@ export default inject(
removeTagsFromRoom, removeTagsFromRoom,
getRooms, getRooms,
scrollToTop, scrollToTop,
wsCreatedPDFForm,
} = filesStore; } = filesStore;
const { updateProfileCulture } = peopleStore.targetUserStore; const { updateProfileCulture } = peopleStore.targetUserStore;
@ -564,8 +567,15 @@ export default inject(
const { usersStore, groupsStore, viewAs: accountsViewAs } = peopleStore; const { usersStore, groupsStore, viewAs: accountsViewAs } = peopleStore;
const { getUsersList: fetchPeople } = usersStore; const { getUsersList: fetchPeople } = usersStore;
const { getGroups: fetchGroups, fetchGroup, groups } = groupsStore; const {
const isEmptyGroups = (groups && groups.length === 0) || !Boolean(groups); getGroups: fetchGroups,
fetchGroup,
groups,
groupsIsFiltered,
} = groupsStore;
const isEmptyGroups =
!groupsIsFiltered &&
((groups && groups.length === 0) || !Boolean(groups));
if (!firstLoad) { if (!firstLoad) {
if (isLoading) { if (isLoading) {
@ -679,6 +689,7 @@ export default inject(
getFolderModel, getFolderModel,
scrollToTop, scrollToTop,
isEmptyGroups, isEmptyGroups,
wsCreatedPDFForm,
}; };
}, },
)(observer(Home)); )(observer(Home));

View File

@ -73,8 +73,6 @@ export const HeaderContainer = styled.div`
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
color: ${(props) => props.theme.client.settings.headerTitleColor}; color: ${(props) => props.theme.client.settings.headerTitleColor};
display: flex;
align-items: center;
} }
} }
.action-wrapper { .action-wrapper {
@ -371,17 +369,10 @@ const SectionHeaderContent = (props) => {
<LoaderSectionHeader /> <LoaderSectionHeader />
) : ( ) : (
<HeaderContainer> <HeaderContainer>
{!isCategoryOrHeader && arrayOfParams[0] && ( {!isCategoryOrHeader &&
<IconButton arrayOfParams[0] &&
iconName={ArrowPathReactSvgUrl} (isMobile() ||
size="17" window.location.href.indexOf("/javascript-sdk/") > -1) && (
isFill={true}
onClick={onBackToParent}
className="arrow-button"
/>
)}
<Headline type="content" truncate={true}>
{isMobile() && isServicePage && (
<IconButton <IconButton
iconName={ArrowPathReactSvgUrl} iconName={ArrowPathReactSvgUrl}
size="17" size="17"
@ -390,6 +381,7 @@ const SectionHeaderContent = (props) => {
className="arrow-button" className="arrow-button"
/> />
)} )}
<Headline type="content" truncate={true}>
<div className="settings-section_header"> <div className="settings-section_header">
<div className="header">{translatedHeader}</div> <div className="header">{translatedHeader}</div>
{isNeedPaidIcon ? ( {isNeedPaidIcon ? (

View File

@ -62,6 +62,7 @@ const UsersRow = (props: AddEmailUsersRowProps) => {
onRowClick={handleAccountToggle} onRowClick={handleAccountToggle}
onSelect={toggleAccount} onSelect={toggleAccount}
isDisabled={!isPrevEmailValid} isDisabled={!isPrevEmailValid}
contextButtonSpacerWidth="0"
> >
<UsersRowContent <UsersRowContent
id={data.key} id={data.key}

View File

@ -60,6 +60,9 @@ const DecisionButton = styled(Button)`
width: 32px; width: 32px;
height: 32px; height: 32px;
padding: 0; padding: 0;
path {
fill: ${(props) => props.theme.client.settings.migration.tableHeaderText};
}
`; `;
DecisionButton.defaultProps = { theme: Base }; DecisionButton.defaultProps = { theme: Base };
@ -82,13 +85,19 @@ const StyledRowContent = styled(RowContent)`
.user-email { .user-email {
margin-inline-end: 5px; margin-inline-end: 5px;
font-size: 12px;
font-weight: 600;
color: ${(props) =>
props.theme.client.settings.migration.tableRowTextColor};
path { path {
fill: #a3a9ae; fill: ${(props) => props.theme.client.settings.migration.tableHeaderText};
} }
} }
.row-main-container-wrapper { .row-main-container-wrapper {
margin: 0; margin: 0;
width: 100%;
} }
.mainIcons { .mainIcons {
@ -179,7 +188,7 @@ const UsersRowContent = (props: AddEmailRowContentProps) => {
<Text fontWeight={600} fontSize="14px"> <Text fontWeight={600} fontSize="14px">
{displayName} {displayName}
</Text> </Text>
<Text fontWeight={600} fontSize="12px" color="#A3A9AE"> <Text className="user-email">
{prevEmail === "" ? t("Settings:NoEmail") : prevEmail} {prevEmail === "" ? t("Settings:NoEmail") : prevEmail}
</Text> </Text>
</div> </div>

View File

@ -37,13 +37,7 @@ import UsersRow from "./UsersRow";
import { AddEmailRowProps, RowViewProps } from "../../../../types"; import { AddEmailRowProps, RowViewProps } from "../../../../types";
const StyledRowContainer = styled(RowContainer)` const StyledRowContainer = styled(RowContainer)`
margin-bottom: 20px; margin: 0 0 20px;
.row-main-container-wrapper {
@media ${tablet} {
margin: 0;
}
}
`; `;
const StyledRow = styled(Row)` const StyledRow = styled(Row)`
@ -51,6 +45,12 @@ const StyledRow = styled(Row)`
height: 40px; height: 40px;
min-height: 40px; min-height: 40px;
.row-header-title {
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
font-weight: 600;
font-size: 12px;
}
@media ${tablet} { @media ${tablet} {
.row_content { .row_content {
height: auto; height: auto;
@ -101,9 +101,7 @@ const RowView = (props: RowViewProps) => {
indeterminate={isIndeterminate} indeterminate={isIndeterminate}
isDisabled={usersWithFilledEmails.length === 0} isDisabled={usersWithFilledEmails.length === 0}
> >
<Text color="#a3a9ae" fontWeight={600} fontSize="12px"> <Text className="row-header-title">{t("Common:Name")}</Text>
{t("Common:Name")}
</Text>
</StyledRow> </StyledRow>
{accountsData.map((data) => ( {accountsData.map((data) => (
<UsersRow <UsersRow

View File

@ -64,25 +64,27 @@ const StyledTableRow = styled(TableRow)`
display: flex; display: flex;
gap: 8px; gap: 8px;
overflow: hidden; overflow: hidden;
font-size: 12px;
font-weight: 600;
color: ${(props) =>
props.theme.client.settings.migration.tableRowTextColor};
path { path {
fill: #a3a9ae; fill: ${(props) => props.theme.client.settings.migration.tableHeaderText};
} }
} }
.import-email-input { .import-email-input {
width: 357.67px; width: 357.67px;
} }
.textOverflow {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`; `;
const DecisionButton = styled(Button)` const DecisionButton = styled(Button)`
width: 32px; width: 32px;
height: 32px; height: 32px;
path {
fill: ${(props) => props.theme.client.settings.migration.tableHeaderText};
}
`; `;
DecisionButton.defaultProps = { theme: Base }; DecisionButton.defaultProps = { theme: Base };
@ -199,7 +201,7 @@ const UsersTableRow = (props: AddEmailTableRowProps) => {
isChecked={isChecked} isChecked={isChecked}
isDisabled={!isPrevEmailValid} isDisabled={!isPrevEmailValid}
/> />
<Text fontWeight={600} className="textOverflow"> <Text fontWeight={600} truncate>
{displayName} {displayName}
</Text> </Text>
</TableCell> </TableCell>
@ -230,7 +232,7 @@ const UsersTableRow = (props: AddEmailTableRowProps) => {
) : ( ) : (
<span onClick={openEmail} className="user-email" ref={emailTextRef}> <span onClick={openEmail} className="user-email" ref={emailTextRef}>
<EditSvg /> <EditSvg />
<Text fontWeight={600} color="#A3A9AE" className="textOverflow"> <Text className="user-email" truncate>
{prevEmail !== "" ? prevEmail : t("Settings:NoEmail")} {prevEmail !== "" ? prevEmail : t("Settings:NoEmail")}
</Text> </Text>
</span> </span>

View File

@ -75,10 +75,7 @@ const LDAP = ({
if (!isLoaded && isLdapAvailable) return <LdapLoader />; if (!isLoaded && isLdapAvailable) return <LdapLoader />;
return ( return (
<StyledLdapPage <StyledLdapPage isSmallWindow={isSmallWindow}>
isSmallWindow={isSmallWindow}
isSettingPaid={isLdapAvailable}
>
<Text className="intro-text settings_unavailable">{t("LdapIntro")}</Text> <Text className="intro-text settings_unavailable">{t("LdapIntro")}</Text>
<Box marginProp="8px 0 24px 0"> <Box marginProp="8px 0 24px 0">
<Link <Link

View File

@ -27,7 +27,6 @@
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import { Box } from "@docspace/shared/components/box"; import { Box } from "@docspace/shared/components/box";
import { mobile } from "@docspace/shared/utils"; import { mobile } from "@docspace/shared/utils";
import { UnavailableStyles } from "../../../../utils/commonSettingsStyles";
const StyledLdapPage = styled(Box)` const StyledLdapPage = styled(Box)`
max-width: ${(props) => (props.isSmallWindow ? "100%" : "700px")}; max-width: ${(props) => (props.isSmallWindow ? "100%" : "700px")};
@ -222,8 +221,6 @@ const StyledLdapPage = styled(Box)`
margin-top: 24px; margin-top: 24px;
} }
} }
${(props) => !props.isSettingPaid && UnavailableStyles}
`; `;
export default StyledLdapPage; export default StyledLdapPage;

View File

@ -33,22 +33,14 @@ import { Text } from "@docspace/shared/components/text";
const HideButton = (props) => { const HideButton = (props) => {
const { t } = useTranslation("SingleSignOn"); const { t } = useTranslation("SingleSignOn");
const { const { text, label, isAdditionalParameters, value, setIsSettingsShown } =
text, props;
label,
isAdditionalParameters,
value,
setIsSettingsShown,
isDisabled,
} = props;
const marginProp = isAdditionalParameters ? null : "24px 0"; const marginProp = isAdditionalParameters ? null : "24px 0";
const onClick = () => { const onClick = () => {
setIsSettingsShown(!value); setIsSettingsShown(!value);
}; };
const onClickProp = isDisabled ? {} : { onClick: onClick };
return ( return (
<Box <Box
alignItems="center" alignItems="center"
@ -71,7 +63,7 @@ const HideButton = (props) => {
<Link <Link
className="hide-button settings_unavailable" className="hide-button settings_unavailable"
isHovered isHovered
{...onClickProp} onClick={onClick}
type="action" type="action"
> >
{value {value

View File

@ -83,11 +83,7 @@ const SettingsContainer = ({
const renderBody = () => ( const renderBody = () => (
<> <>
{!isMobileView && ( {!isMobileView && (
<HideButton <HideButton text={t("Settings:LDAP")} value={isSettingsShown} />
text={t("Settings:LDAP")}
value={isSettingsShown}
isDisabled={!isLdapAvailable}
/>
)} )}
{isMobileView && <ToggleLDAP />} {isMobileView && <ToggleLDAP />}

View File

@ -45,6 +45,7 @@ const SubmitResetButtons = (props) => {
hasChanges, hasChanges,
isLoadingXml, isLoadingXml,
enableSso, enableSso,
isSSOAvailable,
} = props; } = props;
return ( return (
@ -62,7 +63,9 @@ const SubmitResetButtons = (props) => {
saveButtonDisabled={ saveButtonDisabled={
!enableSso || hasErrors || !hasChanges || isLoadingXml !enableSso || hasErrors || !hasChanges || isLoadingXml
} }
cancelEnable={!(isSubmitLoading || isLoadingXml)} disableRestoreToDefault={
isSubmitLoading || isLoadingXml || !isSSOAvailable
}
additionalClassSaveButton="save-button" additionalClassSaveButton="save-button"
additionalClassCancelButton="restore-button" additionalClassCancelButton="restore-button"
/> />
@ -71,7 +74,7 @@ const SubmitResetButtons = (props) => {
); );
}; };
export default inject(({ ssoStore }) => { export default inject(({ ssoStore, currentQuotaStore }) => {
const { const {
saveSsoSettings, saveSsoSettings,
isSsoEnabled, isSsoEnabled,
@ -84,6 +87,7 @@ export default inject(({ ssoStore }) => {
isLoadingXml, isLoadingXml,
enableSso, enableSso,
} = ssoStore; } = ssoStore;
const { isSSOAvailable } = currentQuotaStore;
return { return {
saveSsoSettings, saveSsoSettings,
@ -96,5 +100,6 @@ export default inject(({ ssoStore }) => {
hasChanges, hasChanges,
isLoadingXml, isLoadingXml,
enableSso, enableSso,
isSSOAvailable,
}; };
})(observer(SubmitResetButtons)); })(observer(SubmitResetButtons));

View File

@ -69,7 +69,6 @@ const SingleSignOn = (props) => {
<StyledSsoPage <StyledSsoPage
hideSettings={serviceProviderSettings} hideSettings={serviceProviderSettings}
hideMetadata={spMetadata} hideMetadata={spMetadata}
isSettingPaid={isSSOAvailable}
> >
<Text className="intro-text settings_unavailable" noSelect> <Text className="intro-text settings_unavailable" noSelect>
{t("SsoIntro")} {t("SsoIntro")}
@ -88,7 +87,7 @@ const SingleSignOn = (props) => {
})} })}
label={SERVICE_PROVIDER_SETTINGS} label={SERVICE_PROVIDER_SETTINGS}
value={serviceProviderSettings} value={serviceProviderSettings}
isDisabled={!isSSOAvailable} //isDisabled={!isSSOAvailable}
/> />
<SPSettings /> <SPSettings />
@ -101,7 +100,7 @@ const SingleSignOn = (props) => {
})} })}
label={SP_METADATA} label={SP_METADATA}
value={spMetadata} value={spMetadata}
isDisabled={!isSSOAvailable} //isDisabled={!isSSOAvailable}
/> />
<Box className="sp-metadata"> <Box className="sp-metadata">

View File

@ -25,7 +25,6 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import { UnavailableStyles } from "../../../../utils/commonSettingsStyles";
const StyledSsoPage = styled.div` const StyledSsoPage = styled.div`
max-width: 100%; max-width: 100%;
@ -147,8 +146,6 @@ const StyledSsoPage = styled.div`
`} `}
} }
} }
${(props) => !props.isSettingPaid && UnavailableStyles}
`; `;
export default StyledSsoPage; export default StyledSsoPage;

View File

@ -52,22 +52,13 @@ const StyledWrapper = styled.div`
const HideButton = (props) => { const HideButton = (props) => {
const { t } = useTranslation("SingleSignOn"); const { t } = useTranslation("SingleSignOn");
const { const { text, label, isAdditionalParameters, value, setHideLabel, id } =
text, props;
label,
isAdditionalParameters,
value,
setHideLabel,
isDisabled,
id,
} = props;
const onClick = () => { const onClick = () => {
setHideLabel(label); setHideLabel(label);
}; };
const onClickProp = isDisabled ? {} : { onClick: onClick };
return ( return (
<StyledWrapper isAdditionalParameters={isAdditionalParameters}> <StyledWrapper isAdditionalParameters={isAdditionalParameters}>
{!isAdditionalParameters && ( {!isAdditionalParameters && (
@ -86,7 +77,7 @@ const HideButton = (props) => {
id={id} id={id}
className="hide-button settings_unavailable" className="hide-button settings_unavailable"
isHovered isHovered
{...onClickProp} onClick={onClick}
type="action" type="action"
> >
{value {value

View File

@ -32,6 +32,7 @@ import { Text } from "@docspace/shared/components/text";
import { ToggleButton } from "@docspace/shared/components/toggle-button"; import { ToggleButton } from "@docspace/shared/components/toggle-button";
import { Badge } from "@docspace/shared/components/badge"; import { Badge } from "@docspace/shared/components/badge";
import { mobile } from "@docspace/shared/utils"; import { mobile } from "@docspace/shared/utils";
import { UnavailableStyles } from "../../../../utils/commonSettingsStyles";
const StyledWrapper = styled.div` const StyledWrapper = styled.div`
display: flex; display: flex;
@ -69,6 +70,8 @@ const StyledWrapper = styled.div`
} }
} }
} }
${(props) => !props.isSSOAvailable && UnavailableStyles}
`; `;
const ToggleSSO = ({ enableSso, ssoToggle, isSSOAvailable }) => { const ToggleSSO = ({ enableSso, ssoToggle, isSSOAvailable }) => {
@ -76,7 +79,7 @@ const ToggleSSO = ({ enableSso, ssoToggle, isSSOAvailable }) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
<StyledWrapper> <StyledWrapper isSSOAvailable={isSSOAvailable}>
<ToggleButton <ToggleButton
className="enable-sso toggle" className="enable-sso toggle"
isChecked={enableSso} isChecked={enableSso}

View File

@ -279,7 +279,7 @@ class ConsumerModalDialog extends React.Component {
> >
<ModalDialog.Header>{selectedConsumer.title}</ModalDialog.Header> <ModalDialog.Header>{selectedConsumer.title}</ModalDialog.Header>
<ModalDialog.Body> <ModalDialog.Body>
<Box paddingProp="0 0 16px">{consumerInstruction}</Box> <Box paddingProp="16px 0 16px">{consumerInstruction}</Box>
<React.Fragment> <React.Fragment>
{selectedConsumer.props.map((prop, i) => {selectedConsumer.props.map((prop, i) =>
this.inputsRender(prop, i), this.inputsRender(prop, i),

View File

@ -70,7 +70,7 @@ export const StyledAvatarWrapper = styled.div`
@media ${mobile} { @media ${mobile} {
display: flex; display: flex;
position: fixed; position: absolute;
right: 16px; right: 16px;
${(props) => ${(props) =>
props.theme.interfaceDirection === "rtl" props.theme.interfaceDirection === "rtl"

View File

@ -70,6 +70,7 @@ export const StyledPage = styled.div`
.public-room-text { .public-room-text {
margin: 8px 0; margin: 8px 0;
white-space: wrap;
} }
.public-room-name { .public-room-name {
@ -83,12 +84,6 @@ export const StyledPage = styled.div`
min-width: 32px; min-width: 32px;
min-height: 32px; min-height: 32px;
} }
.public-room-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
} }
`; `;

View File

@ -294,6 +294,7 @@ const Sdk = ({
acceptButtonLabel={frameConfig?.acceptButtonLabel} acceptButtonLabel={frameConfig?.acceptButtonLabel}
cancelButtonLabel={frameConfig?.cancelButtonLabel} cancelButtonLabel={frameConfig?.cancelButtonLabel}
currentFolderId={frameConfig?.id} currentFolderId={frameConfig?.id}
openRoot={!frameConfig?.id}
descriptionText={formatsDescription[frameConfig?.filterParam] || ""} descriptionText={formatsDescription[frameConfig?.filterParam] || ""}
/> />
); );

View File

@ -28,7 +28,7 @@ import { makeAutoObservable } from "mobx";
import { isMobile } from "@docspace/shared/utils"; import { isMobile } from "@docspace/shared/utils";
import { checkDialogsOpen } from "@docspace/shared/utils/checkDialogsOpen"; import { checkDialogsOpen } from "@docspace/shared/utils/checkDialogsOpen";
import { TUser, TUserGroup } from "@docspace/shared/api/people/types"; import { TUser, TUserGroup } from "@docspace/shared/api/people/types";
import { TABLE_HEADER_HEIGHT } from "@docspace/shared/components/table/Table.constants"; import { TABLE_HEADER_HEIGHT } from "@docspace/shared/utils/device";
type AccountsType = TUser | TUserGroup; type AccountsType = TUser | TUserGroup;

View File

@ -116,6 +116,13 @@ const LOADER_TIMER = 500;
let loadingTime; let loadingTime;
let timer; let timer;
const systemFolders = [
FolderType.InProgress,
FolderType.Done,
FolderType.SubFolderDone,
FolderType.SubFolderInProgress,
];
class ContextOptionsStore { class ContextOptionsStore {
settingsStore; settingsStore;
dialogsStore; dialogsStore;
@ -409,7 +416,9 @@ class ContextOptionsStore {
const isShared = shared || sharedItem; const isShared = shared || sharedItem;
if (isShared && !isArchive) { const isSystemFolder = systemFolders.includes(item.type);
if (isShared && !isArchive && !isSystemFolder) {
try { try {
const itemLink = item.isFolder const itemLink = item.isFolder
? await getFolderLink(item.id) ? await getFolderLink(item.id)
@ -965,7 +974,7 @@ class ContextOptionsStore {
const filterUrlParams = filesFilter.toUrlParams(); const filterUrlParams = filesFilter.toUrlParams();
const url = getCategoryUrl( const url = getCategoryUrl(
this.filesStore.categoryType, this.filesStore.categoryType,
filterUrlParams.folder, filesFilter.folder,
); );
navigate( navigate(

View File

@ -1723,6 +1723,8 @@ class FilesStore {
].includes(err?.response?.status); ].includes(err?.response?.status);
if (isUserError && !isThirdPartyError) { if (isUserError && !isThirdPartyError) {
if (isPublicRoom()) return Promise.reject(err);
this.setIsErrorRoomNotAvailable(true); this.setIsErrorRoomNotAvailable(true);
} else { } else {
if (axios.isCancel(err)) { if (axios.isCancel(err)) {
@ -3961,19 +3963,6 @@ class FilesStore {
openDocEditor = (id, preview = false, shareKey = null, editForm = false) => { openDocEditor = (id, preview = false, shareKey = null, editForm = false) => {
const { openOnNewPage } = this.filesSettingsStore; const { openOnNewPage } = this.filesSettingsStore;
const foundIndex = this.files.findIndex((x) => x.id === id);
const file = foundIndex !== -1 ? this.files[foundIndex] : undefined;
if (
file &&
!preview &&
file.rootFolderType !== FolderType.Archive &&
file.fileExst !== ".oform"
) {
const newStatus = file.fileStatus | FileStatus.IsEditing;
this.updateSelectionStatus(id, newStatus, true);
this.updateFileStatus(foundIndex, newStatus);
}
const share = shareKey ? shareKey : this.publicRoomStore.publicRoomKey; const share = shareKey ? shareKey : this.publicRoomStore.publicRoomKey;

View File

@ -37,7 +37,7 @@ import getFilesFromEvent from "@docspace/shared/components/drag-and-drop/get-fil
import config from "PACKAGE_FILE"; import config from "PACKAGE_FILE";
import { getCategoryUrl } from "SRC_DIR/helpers/utils"; import { getCategoryUrl } from "SRC_DIR/helpers/utils";
import { encryptionUploadDialog } from "../helpers/encryptionUploadDialog"; import { encryptionUploadDialog } from "../helpers/encryptionUploadDialog";
import { TABLE_HEADER_HEIGHT } from "@docspace/shared/components/table/Table.constants"; import { TABLE_HEADER_HEIGHT } from "@docspace/shared/utils/device";
class HotkeyStore { class HotkeyStore {
filesStore; filesStore;

View File

@ -29,6 +29,7 @@ import { makeAutoObservable } from "mobx";
import api from "@docspace/shared/api"; import api from "@docspace/shared/api";
import FilesFilter from "@docspace/shared/api/files/filter"; import FilesFilter from "@docspace/shared/api/files/filter";
import { combineUrl } from "@docspace/shared/utils/combineUrl";
import { import {
frameCallCommand, frameCallCommand,
isPublicRoom as isPublicRoomUtil, isPublicRoom as isPublicRoomUtil,
@ -124,7 +125,13 @@ class PublicRoomStore {
if (filter) { if (filter) {
const folderId = filter.folder; const folderId = filter.folder;
return fetchFiles(folderId, filter); return fetchFiles(folderId, filter).catch((error) => {
if (error?.response?.status === 403) {
window.location.replace(
combineUrl(window.ClientConfig?.proxy?.url, "/login"),
);
}
});
} }
return Promise.resolve(); return Promise.resolve();

View File

@ -1,12 +1,12 @@
{ {
"BackToRoom": "Back to room", "BackToRoom": "Back to room",
"CheckReadyForm": "Check ready form", "CheckReadyForms": "Check ready forms",
"Description": "Your form completed and saved", "Description": "Your form completed and saved",
"DescriptionForAnonymous": "You submitted the filled PDF form which was assigned a unique number. To check the status, contact the form owner providing the assigned number.", "DescriptionForAnonymous": "You submitted the filled PDF form which was assigned a unique number. To check the status, contact the form owner providing the assigned number.",
"DescriptionForRegisteredUser": "The filled PDF form is saved and available to you in the Complete folder. To check the status, contact the form owner providing the assigned number.", "DescriptionForRegisteredUser": "The filled PDF form is saved and available to you in the Complete folder. To check the status, contact the form owner providing the assigned number.",
"FillItOutAgain": "Fill it out again", "FillItOutAgain": "Fill it out again",
"FormCompletedSuccessfully": "Form completed successfully", "FormCompletedSuccessfully": "Form completed successfully",
"FormNumber": "Form number:", "FormNumber": "Form number:",
"Manager": "Manager:", "FormOwner": "Form owner",
"Title": "The form is completed" "Title": "The form is completed"
} }

View File

@ -24,15 +24,18 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { headers } from "next/headers"; import { headers, cookies } from "next/headers";
import { ThemeKeys } from "@docspace/shared/enums";
import { getBaseUrl } from "@docspace/shared/utils/next-ssr-helper"; import { getBaseUrl } from "@docspace/shared/utils/next-ssr-helper";
import { SYSTEM_THEME_KEY } from "@docspace/shared/constants";
import Providers from "@/providers";
import Scripts from "@/components/Scripts"; import Scripts from "@/components/Scripts";
import StyledComponentsRegistry from "@/utils/registry"; import StyledComponentsRegistry from "@/utils/registry";
import { getColorTheme, getSettings, getUser } from "@/utils/actions";
import "@/styles/globals.scss"; import "@/styles/globals.scss";
import Providers from "@/providers";
import { getSettings, getUser } from "@/utils/actions";
export default async function RootLayout({ export default async function RootLayout({
children, children,
@ -41,13 +44,23 @@ export default async function RootLayout({
}) { }) {
const hdrs = headers(); const hdrs = headers();
const cookieStore = cookies();
const systemTheme = cookieStore.get(SYSTEM_THEME_KEY)?.value as
| ThemeKeys
| undefined;
if (hdrs.get("x-health-check") || hdrs.get("referer")?.includes("/health")) { if (hdrs.get("x-health-check") || hdrs.get("referer")?.includes("/health")) {
console.log("is health check"); console.log("is health check");
return <></>; return <></>;
} }
const startDate = new Date(); const startDate = new Date();
const [user, settings] = await Promise.all([getUser(), getSettings()]); const [user, settings, colorTheme] = await Promise.all([
getUser(),
getSettings(),
getColorTheme(),
]);
const timer = new Date().getTime() - startDate.getTime(); const timer = new Date().getTime() - startDate.getTime();
if (settings === "access-restricted") redirect(`${getBaseUrl()}/${settings}`); if (settings === "access-restricted") redirect(`${getBaseUrl()}/${settings}`);
@ -70,7 +83,7 @@ export default async function RootLayout({
<body> <body>
<StyledComponentsRegistry> <StyledComponentsRegistry>
<Providers <Providers
contextData={{ user, settings }} contextData={{ user, settings, systemTheme, colorTheme }}
api_host={api_host} api_host={api_host}
timer={timer} timer={timer}
> >

View File

@ -169,6 +169,7 @@ const Editor = ({
goBack = { goBack = {
requestClose: true, requestClose: true,
text: openFileLocationText, text: openFileLocationText,
blank: openOnNewPage,
}; };
} else { } else {
goBack = { goBack = {

View File

@ -43,11 +43,9 @@ export const CompletedFormLayout = styled.section<CompletedFormLayoutProps>`
} }
width: 100%; width: 100%;
height: 100dvh; min-height: 100dvh;
padding: 100px 16px 16px; padding: 100px 16px 16px;
overflow-y: auto;
background-image: ${(props) => props.bgPattern}; background-image: ${(props) => props.bgPattern};
background-repeat: no-repeat; background-repeat: no-repeat;
background-attachment: fixed; background-attachment: fixed;
@ -85,7 +83,7 @@ export const CompletedFormLayout = styled.section<CompletedFormLayoutProps>`
align-self: center; align-self: center;
justify-content: center; justify-content: center;
height: 48px; height: 48px;
width: 100vw; width: calc(100% + 32px);
margin: 0 -16px; margin: 0 -16px;
margin-bottom: 32px; margin-bottom: 32px;
@ -163,7 +161,7 @@ export const MainContent = styled.main`
width: 100%; width: 100%;
display: grid; display: grid;
grid-template-columns: 1fr 2fr; grid-template-columns: 45fr 101fr;
grid-template-rows: 1fr auto; grid-template-rows: 1fr auto;
grid-template-areas: grid-template-areas:
"form-file form-file form-file" "form-file form-file form-file"
@ -175,6 +173,10 @@ export const MainContent = styled.main`
.completed-form__file { .completed-form__file {
grid-area: form-file; grid-area: form-file;
svg {
flex-shrink: 0;
}
} }
.completed-form__filename { .completed-form__filename {
@ -182,11 +184,16 @@ export const MainContent = styled.main`
line-height: 16px; line-height: 16px;
margin: 0px; margin: 0px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
} }
.completed-form__download { .completed-form__download {
cursor: pointer; cursor: pointer;
margin-inline-start: auto; margin-inline-start: auto;
flex-shrink: 0;
} }
.label { .label {
@ -204,6 +211,15 @@ export const MainContent = styled.main`
font-weight: 600; font-weight: 600;
color: ${(props) => props.theme.completedForm.descriptionColor}; color: ${(props) => props.theme.completedForm.descriptionColor};
} }
@media ${mobile} {
grid-template-columns: 100%;
grid-template-areas:
"form-file"
"form-number"
"manager";
}
`; `;
export const FormNumberWrapper = styled.div` export const FormNumberWrapper = styled.div`
@ -249,14 +265,49 @@ export const ManagerWrapper = styled.div`
font-size: 16px; font-size: 16px;
line-height: 22px; line-height: 22px;
font-weight: 700; font-weight: 700;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* max-width: 300px; */
} }
.manager__mail { .manager__mail {
grid-area: mail; grid-area: mail;
display: flex; display: flex;
gap: 8px; gap: 8px;
align-items: center; align-items: center;
span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
svg {
flex: 0 0 auto;
}
}
@media ${mobile} {
grid-template-columns: 100%;
grid-template-areas:
"avatar"
"user-name"
"mail";
.manager__avatar {
justify-self: center;
}
.manager__user-name {
text-align: center;
}
.manager__mail {
justify-content: center;
}
} }
} }
`; `;

View File

@ -114,10 +114,17 @@ export const CompletedForm = ({
); );
const { const {
response: { completedForm, formNumber, manager, originalForm, roomId }, response: {
completedForm,
formNumber,
manager,
originalForm,
roomId,
isRoomMember,
},
} = session; } = session;
const isAnonim = Boolean(share); const isAnonim = Boolean(share) && !isRoomMember;
const getFolderUrl = (folderId: number, isAnonim: boolean): string => { const getFolderUrl = (folderId: number, isAnonim: boolean): string => {
if (isNullOrUndefined(folderId)) return ""; if (isNullOrUndefined(folderId)) return "";
@ -135,10 +142,6 @@ export const CompletedForm = ({
return `${origin}${path}${filter.toUrlParams()}`; return `${origin}${path}${filter.toUrlParams()}`;
}; };
const setHistory = (url: string) => {
history.pushState({}, "", url);
};
const copyLinkFile = async () => { const copyLinkFile = async () => {
const origin = window.location.origin; const origin = window.location.origin;
@ -154,14 +157,12 @@ export const CompletedForm = ({
const gotoCompleteFolder = () => { const gotoCompleteFolder = () => {
const url = getFolderUrl(completedForm.folderId, false); const url = getFolderUrl(completedForm.folderId, false);
setHistory(url); window.location.assign(url);
window.location.replace(url);
}; };
const handleBackToRoom = () => { const handleBackToRoom = () => {
const url = getFolderUrl(roomId, isAnonim); const url = getFolderUrl(roomId, isAnonim);
setHistory(url); window.location.assign(url);
window.location.replace(url);
}; };
const fillAgainSearchParams = new URLSearchParams({ const fillAgainSearchParams = new URLSearchParams({
@ -207,7 +208,7 @@ export const CompletedForm = ({
</Box> </Box>
</FormNumberWrapper> </FormNumberWrapper>
<ManagerWrapper> <ManagerWrapper>
<span className="label">{t("CompletedForm:Manager")}</span> <span className="label">{t("CompletedForm:FormOwner")}</span>
<Box> <Box>
<Avatar <Avatar
className="manager__avatar" className="manager__avatar"
@ -223,22 +224,22 @@ export const CompletedForm = ({
href={`mailto:${manager.email}`} href={`mailto:${manager.email}`}
> >
<MailIcon /> <MailIcon />
{manager.email} <span>{manager.email}</span>
</Link> </Link>
</Box> </Box>
</ManagerWrapper> </ManagerWrapper>
</MainContent> </MainContent>
<ButtonWrapper isShreFile={isShreFile}> <ButtonWrapper isShreFile={isShreFile && !isRoomMember}>
<Button <Button
scale scale
primary primary
size={ButtonSize.medium} size={ButtonSize.medium}
label={ label={
isAnonim ? t("Common:Download") : t("CompletedForm:CheckReadyForm") isAnonim ? t("Common:Download") : t("CompletedForm:CheckReadyForms")
} }
onClick={isAnonim ? handleDownload : gotoCompleteFolder} onClick={isAnonim ? handleDownload : gotoCompleteFolder}
/> />
{!isShreFile && ( {(!isShreFile || isRoomMember) && (
<Button <Button
scale scale
size={ButtonSize.medium} size={ButtonSize.medium}

View File

@ -39,6 +39,7 @@ export type CompletedFormProps = {
originalForm: TFile; originalForm: TFile;
formNumber: number; formNumber: number;
roomId: number; roomId: number;
isRoomMember: boolean;
}; };
}; };
isShreFile: boolean; isShreFile: boolean;

View File

@ -38,8 +38,11 @@ interface UseI18NProps {
const useI18N = ({ settings, user }: UseI18NProps) => { const useI18N = ({ settings, user }: UseI18NProps) => {
const [i18n, setI18N] = React.useState<i18n>( const [i18n, setI18N] = React.useState<i18n>(
getI18NInstance(user?.cultureName ?? "en", settings?.culture ?? "en") ?? () =>
({} as i18n), getI18NInstance(
user?.cultureName ?? settings?.culture ?? "en",
settings?.culture ?? "en",
) ?? ({} as i18n),
); );
const isInit = React.useRef(false); const isInit = React.useRef(false);
@ -53,7 +56,7 @@ const useI18N = ({ settings, user }: UseI18NProps) => {
isInit.current = true; isInit.current = true;
const instance = getI18NInstance( const instance = getI18NInstance(
user?.cultureName ?? "en", user?.cultureName ?? settings?.culture ?? "en",
settings?.culture ?? "en", settings?.culture ?? "en",
); );

View File

@ -34,7 +34,7 @@ import {
} from "@docspace/shared/api/files"; } from "@docspace/shared/api/files";
// import { getOperationProgress } from "@docspace/shared/utils/getOperationProgress"; // import { getOperationProgress } from "@docspace/shared/utils/getOperationProgress";
import { toastr } from "@docspace/shared/components/toast"; import { toastr } from "@docspace/shared/components/toast";
import { EDITOR_ID } from "@docspace/shared/constants"; import { CREATED_FORM_KEY, EDITOR_ID } from "@docspace/shared/constants";
import type { import type {
TFile, TFile,
@ -52,6 +52,13 @@ import type { TData } from "@docspace/shared/components/toast/Toast.type";
import { saveAs } from "@/utils"; import { saveAs } from "@/utils";
import type { ConflictStateType } from "@/types"; import type { ConflictStateType } from "@/types";
type SuccessResponseType = {
form: TFile;
message: string;
};
type FaildResponseType = string;
type ResponseType = SuccessResponseType | FaildResponseType;
const DefaultConflictDataDialogState: ConflictStateType = { const DefaultConflictDataDialogState: ConflictStateType = {
visible: false, visible: false,
resolve: () => {}, resolve: () => {},
@ -70,6 +77,12 @@ const hasFileUrl = (arg: object): arg is { data: { url: string } } => {
); );
}; };
const isSuccessResponse = (
res: ResponseType | undefined,
): res is SuccessResponseType => {
return Boolean(res) && typeof res === "object" && "form" in res;
};
const useStartFillingSelectDialog = (fileInfo: TFile | undefined) => { const useStartFillingSelectDialog = (fileInfo: TFile | undefined) => {
// const { t } = useTranslation(["Common"]); // const { t } = useTranslation(["Common"]);
const resolveRef = useRef<(value: string | PromiseLike<string>) => void>(); const resolveRef = useRef<(value: string | PromiseLike<string>) => void>();
@ -179,14 +192,22 @@ const useStartFillingSelectDialog = (fileInfo: TFile | undefined) => {
const fileUrl = await getFileUrl(); const fileUrl = await getFileUrl();
const response = await saveAs( const response = await saveAs<ResponseType>(
fileInfo.title, fileInfo.title,
fileUrl, fileUrl,
selectedItemId, selectedItemId,
false, false,
"createForm",
); );
const [key, value] = response?.split(":") ?? []; if (isSuccessResponse(response)) {
const { form } = response;
sessionStorage.setItem(CREATED_FORM_KEY, JSON.stringify(form));
}
const [key, value] =
typeof response === "string" ? response.split(":") : [];
// await copyToFolder( // await copyToFolder(
// Number(selectedItemId), // Number(selectedItemId),

View File

@ -26,34 +26,64 @@
import React from "react"; import React from "react";
import { i18n } from "i18next"; import { i18n } from "i18next";
import { match, P } from "ts-pattern";
import { Base, Dark, TColorScheme, TTheme } from "@docspace/shared/themes"; import { Base, Dark, TColorScheme, TTheme } from "@docspace/shared/themes";
import { getSystemTheme } from "@docspace/shared/utils"; import { getSystemTheme } from "@docspace/shared/utils";
import { setCookie } from "@docspace/shared/utils/cookie";
import { ThemeKeys } from "@docspace/shared/enums"; import { ThemeKeys } from "@docspace/shared/enums";
import { getAppearanceTheme } from "@docspace/shared/api/settings"; import { getAppearanceTheme } from "@docspace/shared/api/settings";
import { TUser } from "@docspace/shared/api/people/types";
import { getFontFamilyDependingOnLanguage } from "@docspace/shared/utils/rtlUtils"; import { getFontFamilyDependingOnLanguage } from "@docspace/shared/utils/rtlUtils";
import { SYSTEM_THEME_KEY } from "@docspace/shared/constants";
const SYSTEM_THEME = getSystemTheme(); import type { TUser } from "@docspace/shared/api/people/types";
import type { TGetColorTheme } from "@docspace/shared/api/settings/types";
type MatchType = [ThemeKeys | undefined, ThemeKeys | undefined];
export interface UseThemeProps { export interface UseThemeProps {
user?: TUser; user?: TUser;
i18n?: i18n; i18n?: i18n;
systemTheme?: ThemeKeys;
colorTheme?: TGetColorTheme;
lang?: string;
} }
const useTheme = ({ user, i18n }: UseThemeProps) => { const useTheme = ({
user,
i18n,
systemTheme,
colorTheme,
lang,
}: UseThemeProps) => {
const [currentColorTheme, setCurrentColorTheme] = const [currentColorTheme, setCurrentColorTheme] =
React.useState<TColorScheme>({} as TColorScheme); React.useState<TColorScheme>(() => {
if (!colorTheme) return {} as TColorScheme;
return (
colorTheme.themes.find((theme) => theme.id === colorTheme.selected) ??
({} as TColorScheme)
);
});
const [theme, setTheme] = React.useState<TTheme>(() => { const [theme, setTheme] = React.useState<TTheme>(() => {
if (user?.theme === ThemeKeys.DarkStr) const interfaceDirection = i18n?.dir ? i18n.dir(lang) : "ltr";
return {
...Dark, const newTheme = match<MatchType>([user?.theme, systemTheme])
currentColorScheme: currentColorTheme, .returnType<TTheme>()
}; .with([ThemeKeys.DarkStr, P._], () => Dark)
.with([ThemeKeys.BaseStr, P._], () => Base)
.with([ThemeKeys.SystemStr, ThemeKeys.BaseStr], () => Base)
.with([ThemeKeys.SystemStr, ThemeKeys.DarkStr], () => Dark)
.with([undefined, ThemeKeys.DarkStr], () => Dark)
.with([undefined, ThemeKeys.BaseStr], () => Base)
.otherwise(() => Base);
return { return {
...Base, ...newTheme,
currentColorScheme: currentColorTheme, currentColorScheme: currentColorTheme,
interfaceDirection,
fontFamily: getFontFamilyDependingOnLanguage(i18n?.language ?? "en"),
}; };
}); });
@ -73,29 +103,27 @@ const useTheme = ({ user, i18n }: UseThemeProps) => {
}, []); }, []);
const getUserTheme = React.useCallback(() => { const getUserTheme = React.useCallback(() => {
if (!user?.theme) return; const SYSTEM_THEME = getSystemTheme();
let theme = user.theme;
const interfaceDirection = i18n?.dir ? i18n.dir() : "ltr";
if (user.theme === ThemeKeys.SystemStr) theme = SYSTEM_THEME; let theme = user?.theme ?? SYSTEM_THEME;
const interfaceDirection = i18n?.dir ? i18n.dir(lang) : "ltr";
if (theme === ThemeKeys.BaseStr) { if (user?.theme === ThemeKeys.SystemStr) theme = SYSTEM_THEME;
setTheme({
...Base,
currentColorScheme: currentColorTheme,
interfaceDirection,
fontFamily: getFontFamilyDependingOnLanguage(i18n?.language),
});
return; const fontFamily = getFontFamilyDependingOnLanguage(i18n?.language ?? "en");
}
const isBaseTheme = theme === ThemeKeys.BaseStr;
const themeCookie = isBaseTheme ? ThemeKeys.BaseStr : ThemeKeys.DarkStr;
setTheme({ setTheme({
...Dark, ...(isBaseTheme ? Base : Dark),
currentColorScheme: currentColorTheme, currentColorScheme: currentColorTheme,
interfaceDirection, interfaceDirection,
fontFamily,
}); });
}, [user?.theme, i18n, currentColorTheme]);
setCookie(SYSTEM_THEME_KEY, themeCookie);
}, [user?.theme, i18n, currentColorTheme, lang]);
React.useEffect(() => { React.useEffect(() => {
getCurrentColorTheme(); getCurrentColorTheme();
@ -105,6 +133,16 @@ const useTheme = ({ user, i18n }: UseThemeProps) => {
getUserTheme(); getUserTheme();
}, [currentColorTheme, getUserTheme]); }, [currentColorTheme, getUserTheme]);
React.useEffect(() => {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
mediaQuery.addEventListener("change", getUserTheme);
return () => {
mediaQuery.removeEventListener("change", getUserTheme);
};
}, [getUserTheme]);
return { theme, currentColorTheme }; return { theme, currentColorTheme };
}; };

View File

@ -30,7 +30,11 @@ import React from "react";
import { ThemeProvider as ComponentThemeProvider } from "@docspace/shared/components/theme-provider"; import { ThemeProvider as ComponentThemeProvider } from "@docspace/shared/components/theme-provider";
import { TUser } from "@docspace/shared/api/people/types"; import { TUser } from "@docspace/shared/api/people/types";
import { TSettings } from "@docspace/shared/api/settings/types"; import type {
TGetColorTheme,
TSettings,
} from "@docspace/shared/api/settings/types";
import type { ThemeKeys } from "@docspace/shared/enums";
import useTheme from "@/hooks/useTheme"; import useTheme from "@/hooks/useTheme";
import useI18N from "@/hooks/useI18N"; import useI18N from "@/hooks/useI18N";
@ -39,12 +43,28 @@ type TThemeProvider = {
children: React.ReactNode; children: React.ReactNode;
settings: TSettings | undefined; settings: TSettings | undefined;
user: TUser | undefined; user: TUser | undefined;
systemTheme: ThemeKeys | undefined;
colorTheme: TGetColorTheme | undefined;
}; };
const ThemeProvider = ({ children, user, settings }: TThemeProvider) => { const ThemeProvider = ({
children,
user,
settings,
systemTheme,
colorTheme,
}: TThemeProvider) => {
const { i18n } = useI18N({ settings, user }); const { i18n } = useI18N({ settings, user });
const { theme, currentColorTheme } = useTheme({ user, i18n }); const lang = user?.cultureName ?? settings?.culture;
const { theme, currentColorTheme } = useTheme({
user,
i18n,
systemTheme,
colorTheme,
lang,
});
return ( return (
<ComponentThemeProvider <ComponentThemeProvider

View File

@ -24,9 +24,13 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { TUser } from "@docspace/shared/api/people/types";
import { TSettings } from "@docspace/shared/api/settings/types";
import { Toast } from "@docspace/shared/components/toast/Toast"; import { Toast } from "@docspace/shared/components/toast/Toast";
import type { TUser } from "@docspace/shared/api/people/types";
import type {
TGetColorTheme,
TSettings,
} from "@docspace/shared/api/settings/types";
import type { ThemeKeys } from "@docspace/shared/enums";
import ThemeProvider from "./ThemeProvider"; import ThemeProvider from "./ThemeProvider";
import TranslationProvider from "./TranslationProvider"; import TranslationProvider from "./TranslationProvider";
@ -35,6 +39,8 @@ import ErrorProvider from "./ErrorProvider";
export type TContextData = { export type TContextData = {
user: TUser | undefined; user: TUser | undefined;
settings: TSettings | undefined; settings: TSettings | undefined;
systemTheme: ThemeKeys | undefined;
colorTheme: TGetColorTheme | undefined;
}; };
export type TProviders = { export type TProviders = {

View File

@ -36,7 +36,10 @@ import type {
TFile, TFile,
} from "@docspace/shared/api/files/types"; } from "@docspace/shared/api/files/types";
import { TUser } from "@docspace/shared/api/people/types"; import { TUser } from "@docspace/shared/api/people/types";
import { TSettings } from "@docspace/shared/api/settings/types"; import type {
TGetColorTheme,
TSettings,
} from "@docspace/shared/api/settings/types";
import type { import type {
ActionType, ActionType,
@ -500,3 +503,19 @@ export async function getEditorUrl(
return editorUrl.response as TDocServiceLocation; return editorUrl.response as TDocServiceLocation;
} }
export async function getColorTheme() {
const [getSettings] = createRequest(
[`/settings/colortheme`],
[["", ""]],
"GET",
);
const res = await fetch(getSettings);
if (!res.ok) return;
const colorTheme = await res.json();
return colorTheme.response as TGetColorTheme;
}

View File

@ -81,30 +81,31 @@ export const onSDKRequestEditRights = async (
const url = window.location.href; const url = window.location.href;
const isPDF = documentType === "pdf"; const isPDF = documentType === "pdf";
if (isPDF) { let newURL = new URL(url);
const newURL = new URL(url);
if (newURL.searchParams.has("action")) { if (
newURL.searchParams.delete("action"); !isPDF &&
fileInfo?.viewAccessibility?.MustConvert &&
fileInfo?.security?.Convert
) {
try {
const response = await convertDocumentUrl(fileInfo.id);
if (response && response.webUrl) {
newURL = new URL(response.webUrl);
} else {
throw new Error("Invalid response data");
}
} catch (error) {
console.error("Error converting document", { error });
return;
} }
} else {
if (newURL.searchParams.has("action")) newURL.searchParams.delete("action");
newURL.searchParams.append("action", "edit"); newURL.searchParams.append("action", "edit");
history.pushState({}, "", newURL.toString());
document.location.reload();
return;
} }
let convertUrl = url; history.pushState({}, "", newURL.toString());
if (fileInfo?.viewAccessibility?.MustConvert && fileInfo?.security?.Convert) {
const newUrl = await convertDocumentUrl(fileInfo.id);
if (newUrl) {
convertUrl = newUrl.webUrl;
}
}
history.pushState({}, "", convertUrl);
document.location.reload(); document.location.reload();
}; };

View File

@ -48,6 +48,8 @@ export const getBackUrl = (
} else { } else {
backUrl = `/rooms/shared/${folderId}/filter?folder=${folderId}`; backUrl = `/rooms/shared/${folderId}/filter?folder=${folderId}`;
} }
} else if (rootFolderType === FolderType.Archive) {
backUrl = `/rooms/archived/${folderId}/filter?folder=${folderId}`;
} else { } else {
if ( if (
rootFolderType === FolderType.SHARE || rootFolderType === FolderType.SHARE ||
@ -99,14 +101,15 @@ export const getDataSaveAs = async (params: string) => {
} }
}; };
export const saveAs = ( export const saveAs = <T = string>(
title: string, title: string,
url: string, url: string,
folderId: string | number, folderId: string | number,
openNewTab: boolean, openNewTab: boolean,
action = "create",
) => { ) => {
const options = { const options = {
action: "create", action,
fileuri: url, fileuri: url,
title: title, title: title,
folderid: folderId, folderid: folderId,
@ -115,7 +118,7 @@ export const saveAs = (
const params = toUrlParams(options, true); const params = toUrlParams(options, true);
if (!openNewTab) { if (!openNewTab) {
return getDataSaveAs(params); return getDataSaveAs(params) as Promise<T>;
} else { } else {
const handlerUrl = combineUrl( const handlerUrl = combineUrl(
window.ClientConfig?.proxy?.url, window.ClientConfig?.proxy?.url,

View File

@ -42,7 +42,10 @@ import { useSearchParams, useRouter } from "next/navigation";
import { Text } from "@docspace/shared/components/text"; import { Text } from "@docspace/shared/components/text";
import { Button, ButtonSize } from "@docspace/shared/components/button"; import { Button, ButtonSize } from "@docspace/shared/components/button";
import { createPasswordHash } from "@docspace/shared/utils/common"; import {
createPasswordHash,
frameCallCommand,
} from "@docspace/shared/utils/common";
import { checkPwd } from "@docspace/shared/utils/desktop"; import { checkPwd } from "@docspace/shared/utils/desktop";
import { login } from "@docspace/shared/utils/loginUtils"; import { login } from "@docspace/shared/utils/loginUtils";
import { toastr } from "@docspace/shared/components/toast"; import { toastr } from "@docspace/shared/components/toast";
@ -129,6 +132,7 @@ const LoginForm = ({
setIdentifier(email); setIdentifier(email);
setEmailFromInvitation(email); setEmailFromInvitation(email);
frameCallCommand("setIsLoaded");
}, [loginData]); }, [loginData]);
const authCallback = useCallback( const authCallback = useCallback(

View File

@ -27,8 +27,12 @@
import Filter from "./filter"; import Filter from "./filter";
import { request } from "../client"; import { request } from "../client";
import { checkFilterInstance } from "../../utils/common"; import { checkFilterInstance, toUrlParams } from "../../utils/common";
import { TGroup } from "./types"; import {
TGetGroupMembersInRoom,
TGetGroupMembersInRoomFilter,
TGroup,
} from "./types";
// * Create // * Create
@ -104,11 +108,14 @@ export const getGroupsByUserId = (userId: string) => {
export const getGroupMembersInRoom = ( export const getGroupMembersInRoom = (
folderId: string | number, folderId: string | number,
groupId: string, groupId: string,
filter: TGetGroupMembersInRoomFilter,
) => { ) => {
const url = `/files/folder/${folderId}/group/${groupId}/share?${toUrlParams(filter, false)}`;
return request({ return request({
method: "get", method: "get",
url: `/files/folder/${folderId}/group/${groupId}/share`, url,
}); }) as Promise<TGetGroupMembersInRoom>;
}; };
// * Update // * Update

View File

@ -25,6 +25,7 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { TUser } from "../people/types"; import { TUser } from "../people/types";
import { ShareAccessRights } from "../../enums";
export type TGroup = { export type TGroup = {
category: string; category: string;
@ -37,3 +38,23 @@ export type TGroup = {
shared?: boolean; shared?: boolean;
isLDAP: boolean; isLDAP: boolean;
}; };
export type TGroupMemberInvitedInRoom = {
user: TUser;
canEditAccess: boolean;
overridden: boolean;
owner: boolean;
groupAccess: number;
userAccess: ShareAccessRights;
};
export type TGetGroupMembersInRoom = {
items: TGroupMemberInvitedInRoom[];
total: number;
};
export type TGetGroupMembersInRoomFilter = {
startIndex?: number;
count?: number;
filterValue?: string;
};

View File

@ -112,11 +112,11 @@ const StyledBody = styled.div<{
height: ${(props) => height: ${(props) =>
props.footerVisible props.footerVisible
? props.withHeader ? props.withHeader
? `calc(100% - 16px - ${props.footerHeight}px - ${props.headerHeight}px)` ? `calc(100% - ${props.footerHeight}px - ${props.headerHeight}px)`
: `calc(100% - 16px - ${props.footerHeight}px)` : `calc(100% - ${props.footerHeight}px)`
: props.withHeader : props.withHeader
? `calc(100% - 16px - ${props.headerHeight}px)` ? `calc(100% - ${props.headerHeight}px)`
: `calc(100% - 16px)`}; : "100%"};
padding: ${({ withTabs }) => (withTabs ? "0" : "16px 0 0 0")}; padding: ${({ withTabs }) => (withTabs ? "0" : "16px 0 0 0")};

View File

@ -189,7 +189,7 @@ const Body = ({
if (withInfoBar) { if (withInfoBar) {
const infoEl = document.querySelector(".selector_info-bar"); const infoEl = document.querySelector(".selector_info-bar");
if (infoEl) { if (infoEl) {
const height = infoEl.getClientRects()[0].height; const height = infoEl.getClientRects()[0].height + CONTAINER_PADDING;
listHeight -= height; listHeight -= height;
} }
} }

View File

@ -24,7 +24,10 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { SETTINGS_SIZE } from "./Table.constants"; export const MIN_SIZE_FIRST_COLUMN = 210;
export const DEFAULT_MIN_COLUMN_SIZE = 110;
export const SETTINGS_SIZE = 24;
export const HANDLE_OFFSET = 8;
export const getSubstring = (str: string) => +str.substring(0, str.length - 2); export const getSubstring = (str: string) => +str.substring(0, str.length - 2);

View File

@ -38,16 +38,17 @@ import {
import { TTableColumn, TableHeaderProps } from "./Table.types"; import { TTableColumn, TableHeaderProps } from "./Table.types";
import { TableSettings } from "./sub-components/TableSettings"; import { TableSettings } from "./sub-components/TableSettings";
import { TableHeaderCell } from "./sub-components/TableHeaderCell"; import { TableHeaderCell } from "./sub-components/TableHeaderCell";
import { checkingForUnfixedSize, getSubstring } from "./Table.utils"; import {
DEFAULT_MIN_COLUMN_SIZE,
MIN_SIZE_FIRST_COLUMN,
SETTINGS_SIZE,
HANDLE_OFFSET,
checkingForUnfixedSize,
getSubstring,
} from "./Table.utils";
import { isDesktop } from "../../utils"; import { isDesktop } from "../../utils";
const defaultMinColumnSize = 110; class TableHeaderComponent extends React.Component<
const settingsSize = 24;
const minSizeFirstColumn = 210;
const handleOffset = 8;
class TableHeader extends React.Component<
TableHeaderProps, TableHeaderProps,
{ hideColumns: boolean; columnIndex: null | number } { hideColumns: boolean; columnIndex: null | number }
> { > {
@ -137,7 +138,7 @@ class TableHeader extends React.Component<
if (leftColumn) { if (leftColumn) {
const minSize = leftColumn.dataset.minWidth const minSize = leftColumn.dataset.minWidth
? leftColumn.dataset.minWidth ? leftColumn.dataset.minWidth
: defaultMinColumnSize; : DEFAULT_MIN_COLUMN_SIZE;
if (leftColumn.getBoundingClientRect().width <= +minSize) { if (leftColumn.getBoundingClientRect().width <= +minSize) {
if (colIndex < 0) return false; if (colIndex < 0) return false;
@ -185,17 +186,17 @@ class TableHeader extends React.Component<
const defaultColumn = document.getElementById(`column_${colIndex}`); const defaultColumn = document.getElementById(`column_${colIndex}`);
if (!defaultColumn || defaultColumn.dataset.defaultSize) return; if (!defaultColumn || defaultColumn.dataset.defaultSize) return;
if (column2Width + offset - handleOffset >= defaultMinColumnSize) { if (column2Width + offset - HANDLE_OFFSET >= DEFAULT_MIN_COLUMN_SIZE) {
widths[+columnIndex] = `${newWidth + handleOffset}px`; widths[+columnIndex] = `${newWidth + HANDLE_OFFSET}px`;
widths[colIndex] = `${column2Width + offset - handleOffset}px`; widths[colIndex] = `${column2Width + offset - HANDLE_OFFSET}px`;
} else if (column2Width !== defaultMinColumnSize) { } else if (column2Width !== DEFAULT_MIN_COLUMN_SIZE) {
const width = const width =
getSubstring(widths[+columnIndex]) + getSubstring(widths[+columnIndex]) +
getSubstring(widths[+colIndex]) - getSubstring(widths[+colIndex]) -
defaultMinColumnSize; DEFAULT_MIN_COLUMN_SIZE;
widths[+columnIndex] = `${width}px`; widths[+columnIndex] = `${width}px`;
widths[colIndex] = `${defaultMinColumnSize}px`; widths[colIndex] = `${DEFAULT_MIN_COLUMN_SIZE}px`;
} else { } else {
if (colIndex === columns.length) return false; if (colIndex === columns.length) return false;
this.moveToRight(widths, newWidth, colIndex + 1); this.moveToRight(widths, newWidth, colIndex + 1);
@ -229,7 +230,7 @@ class TableHeader extends React.Component<
)?.defaultSize; )?.defaultSize;
const widthColumns = const widthColumns =
containerWidth - settingsSize - (defaultSizeColumn || 0); containerWidth - SETTINGS_SIZE - (defaultSizeColumn || 0);
const newColumnSize = defaultSize || widthColumns / allColumnsLength; const newColumnSize = defaultSize || widthColumns / allColumnsLength;
@ -247,9 +248,9 @@ class TableHeader extends React.Component<
}; };
if ( if (
(indexOfMaxSize === 0 && newSizeMaxColumn < minSizeFirstColumn) || (indexOfMaxSize === 0 && newSizeMaxColumn < MIN_SIZE_FIRST_COLUMN) ||
(indexOfMaxSize !== 0 && newSizeMaxColumn < defaultMinColumnSize) || (indexOfMaxSize !== 0 && newSizeMaxColumn < DEFAULT_MIN_COLUMN_SIZE) ||
newColumnSize < defaultMinColumnSize || newColumnSize < DEFAULT_MIN_COLUMN_SIZE ||
enableColumnsLength === 1 enableColumnsLength === 1
) )
return ResetColumnsSize(); return ResetColumnsSize();
@ -277,14 +278,14 @@ class TableHeader extends React.Component<
const minSize = column.dataset.minWidth const minSize = column.dataset.minWidth
? column.dataset.minWidth ? column.dataset.minWidth
: defaultMinColumnSize; : DEFAULT_MIN_COLUMN_SIZE;
if (newWidth <= +minSize - handleOffset) { if (newWidth <= +minSize - HANDLE_OFFSET) {
const currentWidth = getSubstring(widths[+columnIndex]); const currentWidth = getSubstring(widths[+columnIndex]);
// Move left // Move left
if (currentWidth !== +minSize) { if (currentWidth !== +minSize) {
newWidth = +minSize - handleOffset; newWidth = +minSize - HANDLE_OFFSET;
this.moveToRight(widths, newWidth); this.moveToRight(widths, newWidth);
} else this.moveToLeft(widths, newWidth); } else this.moveToLeft(widths, newWidth);
} else { } else {
@ -369,7 +370,7 @@ class TableHeader extends React.Component<
if (storageSize) { if (storageSize) {
const splitStorage = storageSize.split(" "); const splitStorage = storageSize.split(" ");
if (getSubstring(splitStorage[0]) <= defaultMinColumnSize) { if (getSubstring(splitStorage[0]) <= DEFAULT_MIN_COLUMN_SIZE) {
localStorage.removeItem(columnStorageName); localStorage.removeItem(columnStorageName);
this.onResize(); this.onResize();
return; return;
@ -407,7 +408,7 @@ class TableHeader extends React.Component<
.map((column) => getSubstring(column)) .map((column) => getSubstring(column))
.reduce((x, y) => x + y); .reduce((x, y) => x + y);
const oldWidth = defaultWidth - defaultSize - settingsSize; const oldWidth = defaultWidth - defaultSize - SETTINGS_SIZE;
let str = ""; let str = "";
let gridTemplateColumnsWithoutOverfilling: string[] = []; let gridTemplateColumnsWithoutOverfilling: string[] = [];
@ -424,7 +425,7 @@ class TableHeader extends React.Component<
? storageInfoPanelSize.split(" ") ? storageInfoPanelSize.split(" ")
: tableContainer; : tableContainer;
let containerMinWidth = containerWidth - defaultSize - settingsSize; let containerMinWidth = containerWidth - defaultSize - SETTINGS_SIZE;
tableInfoPanelContainer.forEach((item, index) => { tableInfoPanelContainer.forEach((item, index) => {
const column = document.getElementById(`column_${index}`); const column = document.getElementById(`column_${index}`);
@ -436,12 +437,12 @@ class TableHeader extends React.Component<
if ( if (
enable && enable &&
(item !== `${defaultSize}px` || `${defaultSize}px` === `0px`) && (item !== `${defaultSize}px` || `${defaultSize}px` === `0px`) &&
item !== `${settingsSize}px` item !== `${SETTINGS_SIZE}px`
) { ) {
if (column?.dataset?.minWidth) { if (column?.dataset?.minWidth) {
containerMinWidth -= +column.dataset.minWidth; containerMinWidth -= +column.dataset.minWidth;
} else { } else {
containerMinWidth -= defaultMinColumnSize; containerMinWidth -= DEFAULT_MIN_COLUMN_SIZE;
} }
} }
}); });
@ -461,11 +462,11 @@ class TableHeader extends React.Component<
if (column?.dataset?.minWidth && column?.dataset?.default) { if (column?.dataset?.minWidth && column?.dataset?.default) {
gridTemplateColumns.push( gridTemplateColumns.push(
`${containerWidth - defaultSize - settingsSize}px`, `${containerWidth - defaultSize - SETTINGS_SIZE}px`,
); );
} else if ( } else if (
item === `${defaultSize}px` || item === `${defaultSize}px` ||
item === `${settingsSize}px` item === `${SETTINGS_SIZE}px`
) { ) {
gridTemplateColumns.push(item); gridTemplateColumns.push(item);
} else { } else {
@ -477,7 +478,7 @@ class TableHeader extends React.Component<
let hasGridTemplateColumnsWithoutOverfilling = false; let hasGridTemplateColumnsWithoutOverfilling = false;
if (infoPanelVisible) { if (infoPanelVisible) {
if (!hideColumns) { if (!hideColumns) {
const contentWidth = containerWidth - defaultSize - settingsSize; const contentWidth = containerWidth - defaultSize - SETTINGS_SIZE;
let enabledColumnsCount = 0; let enabledColumnsCount = 0;
@ -486,7 +487,7 @@ class TableHeader extends React.Component<
index !== 0 && index !== 0 &&
item !== "0px" && item !== "0px" &&
item !== `${defaultSize}px` && item !== `${defaultSize}px` &&
item !== `${settingsSize}px` item !== `${SETTINGS_SIZE}px`
) { ) {
enabledColumnsCount += 1; enabledColumnsCount += 1;
} }
@ -497,10 +498,10 @@ class TableHeader extends React.Component<
.map((column) => getSubstring(column)) .map((column) => getSubstring(column))
.reduce((x, y) => x + y) - .reduce((x, y) => x + y) -
defaultSize - defaultSize -
settingsSize; SETTINGS_SIZE;
if ( if (
contentWidth - enabledColumnsCount * defaultMinColumnSize > contentWidth - enabledColumnsCount * DEFAULT_MIN_COLUMN_SIZE >
getSubstring(tableInfoPanelContainer[0]) getSubstring(tableInfoPanelContainer[0])
) { ) {
const currentContentWidth = const currentContentWidth =
@ -524,7 +525,7 @@ class TableHeader extends React.Component<
if (!enable) { if (!enable) {
gridTemplateColumns.push("0px"); gridTemplateColumns.push("0px");
} else if (item !== `${settingsSize}px`) { } else if (item !== `${SETTINGS_SIZE}px`) {
const percent = const percent =
enabledColumnsCount === 0 enabledColumnsCount === 0
? 100 ? 100
@ -536,17 +537,17 @@ class TableHeader extends React.Component<
const newItemWidth = defaultColumnSize const newItemWidth = defaultColumnSize
? `${defaultColumnSize}px` ? `${defaultColumnSize}px`
: (currentContentWidth * percent) / 100 > : (currentContentWidth * percent) / 100 >
defaultMinColumnSize DEFAULT_MIN_COLUMN_SIZE
? `${(currentContentWidth * percent) / 100}px` ? `${(currentContentWidth * percent) / 100}px`
: `${defaultMinColumnSize}px`; : `${DEFAULT_MIN_COLUMN_SIZE}px`;
if ( if (
(currentContentWidth * percent) / 100 < (currentContentWidth * percent) / 100 <
defaultMinColumnSize && DEFAULT_MIN_COLUMN_SIZE &&
!defaultColumnSize !defaultColumnSize
) { ) {
overWidth += overWidth +=
defaultMinColumnSize - DEFAULT_MIN_COLUMN_SIZE -
(currentContentWidth * percent) / 100; (currentContentWidth * percent) / 100;
} }
@ -565,10 +566,10 @@ class TableHeader extends React.Component<
index !== 0 && index !== 0 &&
column !== "0px" && column !== "0px" &&
column !== `${defaultSize}px` && column !== `${defaultSize}px` &&
column !== `${settingsSize}px` && column !== `${SETTINGS_SIZE}px` &&
columnWidth > defaultMinColumnSize columnWidth > DEFAULT_MIN_COLUMN_SIZE
) { ) {
const availableWidth = columnWidth - defaultMinColumnSize; const availableWidth = columnWidth - DEFAULT_MIN_COLUMN_SIZE;
if (availableWidth < Math.abs(overWidth)) { if (availableWidth < Math.abs(overWidth)) {
overWidth = Math.abs(overWidth) - availableWidth; overWidth = Math.abs(overWidth) - availableWidth;
@ -598,15 +599,15 @@ class TableHeader extends React.Component<
if (!enable) { if (!enable) {
gridTemplateColumns.push("0px"); gridTemplateColumns.push("0px");
} else if (item !== `${settingsSize}px`) { } else if (item !== `${SETTINGS_SIZE}px`) {
const newItemWidth = defaultColumnSize const newItemWidth = defaultColumnSize
? `${defaultColumnSize}px` ? `${defaultColumnSize}px`
: index === 0 : index === 0
? `${ ? `${
contentWidth - contentWidth -
enabledColumnsCount * defaultMinColumnSize enabledColumnsCount * DEFAULT_MIN_COLUMN_SIZE
}px` }px`
: `${defaultMinColumnSize}px`; : `${DEFAULT_MIN_COLUMN_SIZE}px`;
gridTemplateColumns.push(newItemWidth); gridTemplateColumns.push(newItemWidth);
} else { } else {
@ -617,7 +618,7 @@ class TableHeader extends React.Component<
} }
} else { } else {
let overWidth = 0; let overWidth = 0;
if (!hideColumns) { if (!hideColumns && !hideColumnsConst) {
// eslint-disable-next-line guard-for-in, no-restricted-syntax // eslint-disable-next-line guard-for-in, no-restricted-syntax
for (const index in tableContainer) { for (const index in tableContainer) {
const item = tableContainer[index]; const item = tableContainer[index];
@ -646,7 +647,7 @@ class TableHeader extends React.Component<
getSubstring(gridTemplateColumns[+index - colIndex]) + getSubstring(gridTemplateColumns[+index - colIndex]) +
getSubstring(item) getSubstring(item)
}px`; }px`;
} else if (item !== `${settingsSize}px`) { } else if (item !== `${SETTINGS_SIZE}px`) {
const percent = (getSubstring(item) / oldWidth) * 100; const percent = (getSubstring(item) / oldWidth) * 100;
if (percent === 100) { if (percent === 100) {
@ -663,30 +664,31 @@ class TableHeader extends React.Component<
let newItemWidth = defaultColumnSize let newItemWidth = defaultColumnSize
? `${defaultColumnSize}px` ? `${defaultColumnSize}px`
: percent === 0 : percent === 0
? `${defaultMinColumnSize}px` ? `${DEFAULT_MIN_COLUMN_SIZE}px`
: `${ : `${
((containerWidth - defaultSize - settingsSize) * ((containerWidth - defaultSize - SETTINGS_SIZE) *
percent) / percent) /
100 100
}px`; }px`;
const minWidth = column?.dataset?.minWidth; const minWidth = column?.dataset?.minWidth;
const minSize = minWidth ? +minWidth : minSizeFirstColumn; const minSize = minWidth ? +minWidth : MIN_SIZE_FIRST_COLUMN;
// Checking whether the first column is less than the minimum width // Checking whether the first column is less than the minimum width
if (+index === 0 && getSubstring(newItemWidth) < minSize) { if (+index === 0 && getSubstring(newItemWidth) < minSize) {
overWidth += minSizeFirstColumn - getSubstring(newItemWidth); overWidth += MIN_SIZE_FIRST_COLUMN - getSubstring(newItemWidth);
newItemWidth = `${minSizeFirstColumn}px`; newItemWidth = `${MIN_SIZE_FIRST_COLUMN}px`;
} }
// Checking whether columns are smaller than the minimum width // Checking whether columns are smaller than the minimum width
if ( if (
+index !== 0 && +index !== 0 &&
!defaultColumnSize && !defaultColumnSize &&
getSubstring(newItemWidth) < defaultMinColumnSize getSubstring(newItemWidth) < DEFAULT_MIN_COLUMN_SIZE
) { ) {
overWidth += defaultMinColumnSize - getSubstring(newItemWidth); overWidth +=
newItemWidth = `${defaultMinColumnSize}px`; DEFAULT_MIN_COLUMN_SIZE - getSubstring(newItemWidth);
newItemWidth = `${DEFAULT_MIN_COLUMN_SIZE}px`;
} }
gridTemplateColumns.push(newItemWidth); gridTemplateColumns.push(newItemWidth);
@ -783,9 +785,11 @@ class TableHeader extends React.Component<
const column = document.getElementById(`column_${index}`); const column = document.getElementById(`column_${index}`);
const minWidth = column?.dataset?.minWidth; const minWidth = column?.dataset?.minWidth;
const minSize = minWidth ? +minWidth : minSizeFirstColumn; const minSize = minWidth ? +minWidth : MIN_SIZE_FIRST_COLUMN;
if ((index === 0 ? minSize : defaultMinColumnSize) !== getSubstring(item)) if (
(index === 0 ? minSize : DEFAULT_MIN_COLUMN_SIZE) !== getSubstring(item)
)
countColumns += 1; countColumns += 1;
}); });
@ -797,21 +801,21 @@ class TableHeader extends React.Component<
const column = document.getElementById(`column_${index}`); const column = document.getElementById(`column_${index}`);
const minWidth = column?.dataset?.minWidth; const minWidth = column?.dataset?.minWidth;
const minSize = minWidth ? +minWidth : minSizeFirstColumn; const minSize = minWidth ? +minWidth : MIN_SIZE_FIRST_COLUMN;
const itemSubstring = getSubstring(item); const itemSubstring = getSubstring(item);
if ((index === 0 ? minSize : defaultMinColumnSize) === itemSubstring) if ((index === 0 ? minSize : DEFAULT_MIN_COLUMN_SIZE) === itemSubstring)
return; return;
const differenceWithMinimum = const differenceWithMinimum =
itemSubstring - (index === 0 ? minSize : defaultMinColumnSize); itemSubstring - (index === 0 ? minSize : DEFAULT_MIN_COLUMN_SIZE);
if (differenceWithMinimum >= addWidth) { if (differenceWithMinimum >= addWidth) {
newGridTemplateColumns[index] = `${itemSubstring - addWidth}px`; newGridTemplateColumns[index] = `${itemSubstring - addWidth}px`;
} else { } else {
newGridTemplateColumns[index] = `${ newGridTemplateColumns[index] = `${
index === 0 ? minSize : defaultMinColumnSize index === 0 ? minSize : DEFAULT_MIN_COLUMN_SIZE
}px`; }px`;
} }
}); });
@ -862,7 +866,7 @@ class TableHeader extends React.Component<
columns.find((col) => col.defaultSize && col.enable)?.defaultSize || 0; columns.find((col) => col.defaultSize && col.enable)?.defaultSize || 0;
const containerWidth = const containerWidth =
container.clientWidth - defaultColumnSize - settingsSize; container.clientWidth - defaultColumnSize - SETTINGS_SIZE;
const firstColumnPercent = enableColumns.length > 0 ? 40 : 100; const firstColumnPercent = enableColumns.length > 0 ? 40 : 100;
const percent = enableColumns.length > 0 ? 60 / enableColumns.length : 0; const percent = enableColumns.length > 0 ? 60 / enableColumns.length : 0;
@ -882,7 +886,7 @@ class TableHeader extends React.Component<
} }
} }
str += `${settingsSize}px`; str += `${SETTINGS_SIZE}px`;
if (container) container.style.gridTemplateColumns = str; if (container) container.style.gridTemplateColumns = str;
if (this.headerRef && this.headerRef.current) { if (this.headerRef && this.headerRef.current) {
@ -970,4 +974,6 @@ class TableHeader extends React.Component<
} }
} }
export default withTheme(TableHeader); const TableHeader = withTheme(TableHeaderComponent);
export { TableHeader };

View File

@ -24,13 +24,10 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import TableHeader from "./TableHeader";
export { TableHeader };
export { TableContainer } from "./TableContainer"; export { TableContainer } from "./TableContainer";
export { TableBody } from "./TableBody"; export { TableBody } from "./TableBody";
export { TableRow } from "./TableRow"; export { TableRow } from "./TableRow";
// export { TableHeader } from "./TableHeader"; export { TableHeader } from "./TableHeader";
export { TableGroupMenu } from "./TableGroupMenu"; export { TableGroupMenu } from "./TableGroupMenu";
export { TableCell } from "./sub-components/TableCell"; export { TableCell } from "./sub-components/TableCell";

View File

@ -53,6 +53,7 @@ export const ROOM = "room";
export const USERS = "users"; export const USERS = "users";
export const USERS_IN_ROOM = "usersInRoom"; export const USERS_IN_ROOM = "usersInRoom";
export const PDF_FORM_DIALOG_KEY = "pdf_form_dialog"; export const PDF_FORM_DIALOG_KEY = "pdf_form_dialog";
export const CREATED_FORM_KEY = "created_form_key";
export const COUNT_FOR_SHOWING_BAR = 2; export const COUNT_FOR_SHOWING_BAR = 2;
export const PERCENTAGE_FOR_SHOWING_BAR = 90; export const PERCENTAGE_FOR_SHOWING_BAR = 90;

View File

@ -33,6 +33,30 @@ import {
StyledMembersLoader, StyledMembersLoader,
} from "../body.styled"; } from "../body.styled";
export const MemberLoader = ({ count = 1 }: { count?: number }) => {
return (
<>
{[...Array(count).keys()].map((i) => (
<StyledMemberLoader key={i}>
<RectangleSkeleton
className="avatar"
width="32px"
height="32px"
borderRadius="50%"
/>
<RectangleSkeleton width="212px" height="16px" borderRadius="3px" />
<RectangleSkeleton
className="role-selector"
width="64px"
height="20px"
borderRadius="3px"
/>
</StyledMemberLoader>
))}
</>
);
};
const MembersLoader = () => { const MembersLoader = () => {
return ( return (
<StyledMembersLoader> <StyledMembersLoader>
@ -41,46 +65,14 @@ const MembersLoader = () => {
<RectangleSkeleton width="16px" height="16px" borderRadius="3px" /> <RectangleSkeleton width="16px" height="16px" borderRadius="3px" />
</StyledMemberSubtitleLoader> </StyledMemberSubtitleLoader>
{[...Array(4).keys()].map((i) => ( <MemberLoader count={4} />
<StyledMemberLoader key={i}>
<RectangleSkeleton
className="avatar"
width="32px"
height="32px"
borderRadius="50%"
/>
<RectangleSkeleton width="212px" height="16px" borderRadius="3px" />
<RectangleSkeleton
className="role-selector"
width="64px"
height="20px"
borderRadius="3px"
/>
</StyledMemberLoader>
))}
<StyledMemberSubtitleLoader className="pending_users"> <StyledMemberSubtitleLoader className="pending_users">
<RectangleSkeleton width="111px" height="16px" borderRadius="3px" /> <RectangleSkeleton width="111px" height="16px" borderRadius="3px" />
<RectangleSkeleton width="16px" height="16px" borderRadius="3px" /> <RectangleSkeleton width="16px" height="16px" borderRadius="3px" />
</StyledMemberSubtitleLoader> </StyledMemberSubtitleLoader>
{[...Array(4).keys()].map((i) => ( <MemberLoader count={4} />
<StyledMemberLoader key={i}>
<RectangleSkeleton
className="avatar"
width="32px"
height="32px"
borderRadius="50%"
/>
<RectangleSkeleton width="212px" height="16px" borderRadius="3px" />
<RectangleSkeleton
className="role-selector"
width="64px"
height="20px"
borderRadius="3px"
/>
</StyledMemberLoader>
))}
</StyledMembersLoader> </StyledMembersLoader>
); );
}; };

View File

@ -369,7 +369,7 @@ class AuthStore {
login = async (user: TUser, hash: string, session = true) => { login = async (user: TUser, hash: string, session = true) => {
try { try {
const response = (await api.user.login(user, hash, session)) as { const response = (await api.user.login(user, hash, "", session)) as {
token: string; token: string;
tfa: string; tfa: string;
error: { message: unknown }; error: { message: unknown };

View File

@ -2114,6 +2114,7 @@ export const getBaseTheme = () => {
folderLabelColor: "#A3A9AE", folderLabelColor: "#A3A9AE",
renamedItemColor: "#A3A9AE", renamedItemColor: "#A3A9AE",
oldRoleColor: "#657077", oldRoleColor: "#657077",
messageColor: "#333333",
}, },
details: { details: {
@ -2720,6 +2721,7 @@ export const getBaseTheme = () => {
upload: { upload: {
color: gray, color: gray,
tooltipColor: lightCumulus, tooltipColor: lightCumulus,
iconColor: lightErrorStatus,
shareButton: { shareButton: {
color: gray, color: gray,

View File

@ -2086,6 +2086,7 @@ const Dark: TTheme = {
folderLabelColor: "#A3A9AE", folderLabelColor: "#A3A9AE",
renamedItemColor: "#A3A9AE", renamedItemColor: "#A3A9AE",
oldRoleColor: "#A3A9AE", oldRoleColor: "#A3A9AE",
messageColor: "#FFFFFF",
}, },
details: { details: {
@ -2698,6 +2699,7 @@ const Dark: TTheme = {
upload: { upload: {
color: black, color: black,
tooltipColor: "#F5E9BA", tooltipColor: "#F5E9BA",
iconColor: darkErrorStatus,
shareButton: { shareButton: {
color: gray, color: gray,

View File

@ -25,6 +25,7 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
export const INFO_PANEL_WIDTH = 400; export const INFO_PANEL_WIDTH = 400;
export const TABLE_HEADER_HEIGHT = 40;
export function checkIsSSR() { export function checkIsSSR() {
return typeof window === "undefined"; return typeof window === "undefined";