Client:OAuth2: fix logic for new client form

This commit is contained in:
Timofey Boyko 2023-10-31 18:34:07 +03:00
parent 86f17497a8
commit 8318dbb8f5
9 changed files with 207 additions and 205 deletions

View File

@ -111,64 +111,6 @@ const StyledChipsContainer = styled.div`
gap: 4px;
`;
const StyledChips = styled.div`
background: #eceef1;
padding: 6px 8px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
`;
// const CheckboxGroup = styled.div`
// width: 100%;
// height: auto;
// display: flex;
// flex-direction: column;
// gap: 8px;
// `;
// const CheckboxRaw = styled.div`
// width: 100%;
// height: auto;
// display: flex;
// flex-direction: raw;
// align-items: center;
// gap: 8px;
// .checkbox {
// margin-right: 0px;
// }
// `;
// const CategorySubHeader = styled.div`
// margin-top: 8px;
// margin-bottom: 8px;
// font-size: 15px;
// font-style: normal;
// font-weight: 600;
// line-height: 16px;
// @media ${tablet} {
// &:not(&.copy-window-code) {
// margin-bottom: 0;
// }
// }
// @media ${hugeMobile} {
// &:first-of-type {
// margin-top: 0;
// }
// }
// `;
const StyledScopesContainer = styled.div`
width: 100%;
@ -225,6 +167,20 @@ const StyledScopesCheckbox = styled.div`
}
`;
const StyledButtonContainer = styled.div`
width: fit-content;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 8px;
@media ${mobile} {
width: 100%;
}
`;
export {
StyledContainer,
StyledBlock,
@ -233,8 +189,8 @@ export {
StyledInputGroup,
StyledInputRow,
StyledChipsContainer,
StyledChips,
StyledScopesContainer,
StyledScopesName,
StyledScopesCheckbox,
StyledButtonContainer,
};

View File

@ -1,3 +1,5 @@
//@ts-ignore
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
import { IClientProps, IScope } from "@docspace/common/utils/oauth/interfaces";
export interface InputProps {
@ -45,10 +47,11 @@ export interface ClientFormProps {
) => Promise<IClientProps>;
regenerateSecret?: (clientId: string) => Promise<string>;
currentDeviceType?: string;
}
export interface PreviewProps {
clientId: string;
redirectURI: string;
scopes: string[];
export interface ClientStore {
auth: { settingsStore: { currentDeviceType: string } };
oauthStore: OAuthStoreProps;
}

View File

@ -25,13 +25,6 @@ const BasicBlock = ({
descriptionValue,
changeValue,
}: BasicBlockProps) => {
const [value, setValue] = React.useState<{ [key: string]: string }>({
name: nameValue,
websiteUrl: websiteUrlValue,
logo: logoValue,
description: descriptionValue,
});
const [error, setError] = React.useState({
name: "",
websiteUrl: "",
@ -42,12 +35,6 @@ const BasicBlock = ({
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const target = e.target;
setValue((value) => {
value[target.name] = target.value;
return { ...value };
});
changeValue(target.name, target.value);
};
@ -62,7 +49,6 @@ const BasicBlock = ({
reader.onload = () => {
const result = reader.result as string;
setValue((v) => ({ ...v, logo: result }));
changeValue("logo", result);
};
}
@ -76,21 +62,21 @@ const BasicBlock = ({
label={"App name"}
name={"name"}
placeholder={"Enter name"}
value={value.name}
value={nameValue}
error={error.name}
onChange={onChange}
/>
<InputGroup
label={"Website URL"}
name={"websiteUrl"}
name={"website_url"}
placeholder={"Enter URL"}
value={value.websiteUrl}
value={websiteUrlValue}
error={error.websiteUrl}
onChange={onChange}
/>
<SelectGroup
label={"App icon"}
value={value.logo}
value={logoValue}
selectLabel={"Select new image"}
description={"JPG, PNG or SVG, 32x32"}
onSelect={onSelect}
@ -99,7 +85,7 @@ const BasicBlock = ({
label={"Description"}
name={"description"}
placeholder={"Enter description"}
value={value.description}
value={descriptionValue}
error={error.description}
onChange={onChange}
/>

View File

@ -0,0 +1,60 @@
import React from "react";
import Button from "@docspace/components/button";
import { StyledButtonContainer } from "../ClientForm.styled";
import { DeviceType } from "@docspace/common/constants";
interface ButtonsBlockProps {
saveLabel: string;
cancelLabel: string;
isRequestRunning: boolean;
saveButtonDisabled: boolean;
cancelButtonDisabled: boolean;
onSaveClick: () => void;
onCancelClick: () => void;
currentDeviceType: string;
}
const ButtonsBlock = ({
saveLabel,
cancelLabel,
isRequestRunning,
saveButtonDisabled,
cancelButtonDisabled,
onSaveClick,
onCancelClick,
currentDeviceType,
}: ButtonsBlockProps) => {
const isDesktop = currentDeviceType === DeviceType.desktop;
const buttonSize = isDesktop ? "small" : "normal";
return (
<StyledButtonContainer>
<Button
// @ts-ignore
label={saveLabel}
isLoading={isRequestRunning}
isDisabled={saveButtonDisabled}
primary
size={buttonSize}
scale={!isDesktop}
onClick={onSaveClick}
/>
<Button
// @ts-ignore
label={cancelLabel}
isDisabled={cancelButtonDisabled}
size={buttonSize}
scale={!isDesktop}
onClick={onCancelClick}
/>
</StyledButtonContainer>
);
};
export default ButtonsBlock;

View File

@ -6,6 +6,8 @@ import InputBlock from "@docspace/components/input-block";
import HelpButton from "@docspace/components/help-button";
//@ts-ignore
import SelectorAddButton from "@docspace/components/selector-add-button";
//@ts-ignore
import SelectedItem from "@docspace/components/selected-item";
import {
StyledChipsContainer,
@ -21,8 +23,7 @@ interface MultiInputGroupProps {
placeholder: string;
currentValue: string[];
onAdd: (name: string, value: string) => void;
onRemove: (name: string, value: string) => void;
onAdd: (name: string, value: string, remove?: boolean) => void;
helpButtonText?: string;
}
@ -33,7 +34,7 @@ const MultiInputGroup = ({
placeholder,
currentValue,
onAdd,
onRemove,
helpButtonText,
}: MultiInputGroupProps) => {
const [value, setValue] = React.useState("");
@ -71,9 +72,23 @@ const MultiInputGroup = ({
tabIndex={0}
maxLength={255}
/>
<SelectorAddButton />
<SelectorAddButton
onClick={() => {
onAdd(name, value);
setValue("");
}}
/>
</StyledInputRow>
<StyledChipsContainer></StyledChipsContainer>
<StyledChipsContainer>
{currentValue.map((v, index) => (
<SelectedItem
key={`${v}-${index}`}
isInline
label={v}
onClose={() => onAdd(name, v)}
/>
))}
</StyledChipsContainer>
</StyledInputGroup>
);
};

View File

@ -2,9 +2,6 @@ import React from "react";
import { StyledBlock, StyledInputBlock } from "../ClientForm.styled";
import BlockHeader from "./BlockHeader";
import InputGroup from "./InputGroup";
import TextAreaGroup from "./TextAreaGroup";
import SelectGroup from "./SelectGroup";
import MultiInputGroup from "./MultiInputGroup";
interface OAuthBlockProps {
@ -23,19 +20,6 @@ const OAuthBlock = ({
changeValue,
}: OAuthBlockProps) => {
const [value, setValue] = React.useState<{ [key: string]: string[] }>({
redirectUris: redirectUrisValue,
allowedOrigins: allowedOriginsValue,
});
const onAdd = (name: string, value: string) => {
setValue((v) => {
v[name] = [...v[name], value];
return { ...v };
});
};
return (
<StyledBlock>
<BlockHeader header={"OAuth URLs"} />
@ -43,19 +27,17 @@ const OAuthBlock = ({
<MultiInputGroup
label={"Redirect URLs"}
placeholder={"Enter URL"}
name={"redirectUris"}
onAdd={onAdd}
onRemove={onAdd}
currentValue={value.redirect_uris}
name={"redirect_uris"}
onAdd={changeValue}
currentValue={redirectUrisValue}
helpButtonText={"Redirect uris"}
/>
<MultiInputGroup
label={"Allowed origins"}
placeholder={"Enter URL"}
name={"allowedOrigins"}
onAdd={onAdd}
onRemove={onAdd}
currentValue={value.allowed_origins}
name={"allowed_origins"}
onAdd={changeValue}
currentValue={allowedOriginsValue}
helpButtonText={"Allowed origins"}
/>
</StyledInputBlock>

View File

@ -24,7 +24,7 @@ import {
interface IScopesBlockProps {
scopes: IScope[];
selectedScopes: string[];
onAddScope: (scope: string[]) => void;
onAddScope: (name: string, scope: string) => void;
t: any;
}
@ -82,6 +82,8 @@ const ScopesBlock = ({
setCheckedScopes((val) => val.filter((v) => v !== name));
}
onAddScope("scopes", name);
};
const getRenderedScopeList = () => {

View File

@ -3,8 +3,6 @@ import { StyledBlock, StyledInputBlock } from "../ClientForm.styled";
import BlockHeader from "./BlockHeader";
import InputGroup from "./InputGroup";
import TextAreaGroup from "./TextAreaGroup";
import SelectGroup from "./SelectGroup";
interface SupportBlockProps {
t: any;
@ -22,11 +20,6 @@ const SupportBlock = ({
changeValue,
}: SupportBlockProps) => {
const [value, setValue] = React.useState<{ [key: string]: string }>({
policyUrl: policyUrlValue,
termsUrl: termsUrlValue,
});
const [error, setError] = React.useState({
policyUrl: "",
termsUrl: "",
@ -35,22 +28,18 @@ const SupportBlock = ({
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const target = e.target;
setValue((value) => {
value[target.name] = target.value;
return { ...value };
});
changeValue(target.name, target.value);
};
return (
<StyledBlock>
<BlockHeader header={"Basic info"} />
<BlockHeader header={"Support & Legal info"} />
<StyledInputBlock>
<InputGroup
label={"Privacy policy URL"}
name={"policyUrl"}
name={"policy_url"}
placeholder={"Enter URL"}
value={value.policyUrl}
value={policyUrlValue}
error={error.policyUrl}
onChange={onChange}
helpButtonText={
@ -59,9 +48,9 @@ const SupportBlock = ({
/>
<InputGroup
label={"Terms of Service URL"}
name={"termsUrl"}
name={"terms_url"}
placeholder={"Enter URL"}
value={value.termsUrl}
value={termsUrlValue}
error={error.termsUrl}
onChange={onChange}
helpButtonText={"Terms of service help"}

View File

@ -9,18 +9,16 @@ import {
IScope,
} from "@docspace/common/utils/oauth/interfaces";
// @ts-ignore
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
import BasicBlock from "./components/BasicBlock";
import ClientBlock from "./components/ClientBlock";
import SupportBlock from "./components/SupportBlock";
import OAuthBlock from "./components/OAuthBlock";
import ScopesBlock from "./components/ScopesBlock";
import ButtonsBlock from "./components/ButtonsBlock";
import { StyledContainer } from "./ClientForm.styled";
import { ClientFormProps } from "./ClientForm.types";
import OAuthBlock from "./components/OAuthBlock";
import ScopesBlock from "./components/ScopesBlock";
import { ClientFormProps, ClientStore } from "./ClientForm.types";
const ClientForm = ({
id,
@ -36,11 +34,15 @@ const ClientForm = ({
updateClient,
regenerateSecret,
currentDeviceType,
}: ClientFormProps) => {
const { t } = useTranslation(["Common"]);
const navigate = useNavigate();
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const [isRequestRunning, setIsRequestRunning] =
React.useState<boolean>(false);
const [initClient, setInitClient] = React.useState<IClientProps | null>(null);
@ -50,16 +52,16 @@ const ClientForm = ({
website_url: "",
description: "",
redirect_uris: [""],
allowed_origins: [""],
logout_redirect_uris: [""],
redirect_uris: [],
allowed_origins: [],
logout_redirect_uris: [],
terms_url: "",
policy_url: "",
authentication_method: "",
scopes: [""],
scopes: [],
});
const [clientId, setClientId] = React.useState<string>("");
@ -174,8 +176,19 @@ const ClientForm = ({
setForm((val) => {
const newVal = { ...val };
if (typeof newVal[name as keyof IClientReqDTO]) {
typeof newVal[name as keyof IClientReqDTO] === value;
if (newVal[name as keyof IClientReqDTO] instanceof Array) {
if (newVal[name as keyof IClientReqDTO].includes(value)) {
newVal[name as keyof IClientReqDTO] = newVal[
name as keyof IClientReqDTO
//@ts-ignore
].filter((v: string) => v !== value);
} else {
//@ts-ignore
newVal[name as keyof IClientReqDTO].push(value);
}
} else {
//@ts-ignore
newVal[name as keyof IClientReqDTO] = value;
}
return { ...newVal };
@ -197,6 +210,7 @@ const ClientForm = ({
}, [id, scopeList, getScopeList, fetchScopes]);
// React.useEffect(() => {
// if (id) {
// setClientId(id);
// if (!client) {
@ -207,69 +221,58 @@ const ClientForm = ({
// }
// }, [id, client, fetchClient, getClient, setClient]);
// const compareAndValidate = () => {
// let isValid = false;
const compareAndValidate = () => {
let isValid = true;
// for (let key in form) {
// if (!!form[key] || key === "appIcon" || key === "authenticationMethod") {
// if (initClient) {
// switch (key) {
// case "appName":
// isValid = isValid || initClient.name !== form[key];
for (let key in form) {
switch (key) {
case "name":
isValid = isValid && !!form[key];
// break;
// case "appIcon":
// isValid = isValid || initClient.name !== form[key];
break;
case "logo":
isValid = isValid && !!form[key];
// break;
// case "description":
// isValid = isValid || initClient.description !== form[key];
break;
case "description":
isValid = isValid && !!form[key];
// break;
// case "redirectUrl":
// isValid = isValid || initClient.redirectUri !== form[key];
break;
case "website_url":
isValid = isValid && !!form[key];
// break;
// case "logoutRedirectUrl":
// isValid = isValid || initClient.logoutRedirectUri !== form[key];
break;
case "redirect_uris":
isValid = isValid && form[key].length > 0;
// break;
// case "privacyUrl":
// isValid = isValid || initClient.policyUrl !== form[key];
break;
case "allowed_origins":
isValid = isValid && form[key].length > 0;
// break;
break;
case "logout_redirect_uris":
isValid = isValid;
// case "termsUrl":
// isValid = isValid || initClient.termsUrl !== form[key];
break;
case "terms_url":
isValid = isValid && !!form[key];
break;
case "policy_url":
isValid = isValid && !!form[key];
break;
case "authentication_method":
isValid = isValid;
break;
case "scopes":
isValid = isValid && form[key].length > 0;
break;
}
}
// break;
// }
// }
// isValid = true;
// } else {
// isValid = false;
// }
// }
return isValid;
};
// if (checkedScopes.length > 0) {
// if (initClient) {
// let isSame = checkedScopes.length === initClient?.scopes.length;
// if (isSame) {
// checkedScopes.forEach((scope) => {
// if (!initClient?.scopes.includes(scope)) isSame = false;
// });
// }
// isValid = isValid || !isSame;
// }
// } else {
// isValid = false;
// }
// return isValid;
// };
// const isValid = compareAndValidate();
const isValid = compareAndValidate();
return (
<StyledContainer>
@ -292,23 +295,29 @@ const ClientForm = ({
t={t}
redirectUrisValue={form.redirect_uris}
allowedOriginsValue={form.allowed_origins}
changeValue={(name: string, value: string) => {
console.log(name, value);
}}
changeValue={onChangeForm}
/>
<ScopesBlock
t={t}
scopes={scopeList || []}
selectedScopes={[]}
onAddScope={() => {}}
onAddScope={onChangeForm}
/>
<SupportBlock
t={t}
policyUrlValue={form.policy_url}
termsUrlValue={form.terms_url}
changeValue={(name: string, value: string) => {
console.log(name, value);
}}
changeValue={onChangeForm}
/>
<ButtonsBlock
saveLabel={"Save"}
cancelLabel={"Cancel"}
onSaveClick={() => {}}
onCancelClick={() => {}}
isRequestRunning={isRequestRunning}
saveButtonDisabled={!isValid}
cancelButtonDisabled={isRequestRunning}
currentDeviceType={currentDeviceType || ""}
/>
</>
)}
@ -317,10 +326,7 @@ const ClientForm = ({
};
export default inject(
(
{ oauthStore }: { oauthStore: OAuthStoreProps },
{ id }: ClientFormProps
) => {
({ oauthStore, auth }: ClientStore, { id }: ClientFormProps) => {
const {
clientList,
scopeList,
@ -334,6 +340,8 @@ export default inject(
regenerateSecret,
} = oauthStore;
const { currentDeviceType } = auth.settingsStore;
const props: ClientFormProps = {
scopeList,
@ -344,6 +352,7 @@ export default inject(
updateClient,
regenerateSecret,
currentDeviceType,
};
if (id) {