Client:OAuth2: enable save button and throw error after click

This commit is contained in:
Timofey Boyko 2023-12-15 10:29:40 +03:00
parent 06da361ba3
commit fbd5335167
10 changed files with 133 additions and 92 deletions

View File

@ -63,6 +63,7 @@
"TermsOfService": "Terms of Service",
"TermsOfServiceURL": "Terms of Service URL",
"TermsOfServiceURLHelpButton": "Terms and conditions that users must comply with when using this application.",
"ThisRequiredField": "This is a required field",
"WebsiteUrl": "Website URL",
"Write": "Write"
}

View File

@ -34,6 +34,10 @@ const StyledBlock = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
.icon-field {
margin: 0;
}
`;
const StyledHeaderRow = styled.div`

View File

@ -1,8 +1,10 @@
import React from "react";
import { Trans } from "react-i18next";
//@ts-ignore
// @ts-ignore
import HelpButton from "@docspace/components/help-button";
// @ts-ignore
import FieldContainer from "@docspace/components/field-container";
import { StyledBlock, StyledInputBlock } from "../ClientForm.styled";
@ -26,6 +28,7 @@ interface BasicBlockProps {
isEdit: boolean;
errorFields: string[];
requiredErrorFields: string[];
onBlur: (name: string) => void;
}
@ -75,6 +78,7 @@ const BasicBlock = ({
isEdit,
errorFields,
requiredErrorFields,
onBlur,
}: BasicBlockProps) => {
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -133,6 +137,12 @@ const BasicBlock = ({
<Trans t={t} i18nKey="AllowPKCEHelpButton" ns="OAuth" />
);
const isNameRequiredError = requiredErrorFields.includes("name");
const isWebsiteRequiredError = requiredErrorFields.includes("website_url");
const isNameError = errorFields.includes("name");
const isWebsiteError = errorFields.includes("website_url");
const isLogoRequiredError = requiredErrorFields.includes("logo");
return (
<StyledBlock>
<BlockHeader header={"Basic info"} />
@ -142,10 +152,10 @@ const BasicBlock = ({
name={"name"}
placeholder={t("Common:EnterName")}
value={nameValue}
error={`${t("ErrorName")} 3`}
error={isNameError ? `${t("ErrorName")} 3` : t("ThisRequiredField")}
onChange={onChange}
isRequired
isError={errorFields.includes("name")}
isError={isNameRequiredError || isNameError}
onBlur={onBlur}
/>
<InputGroup
@ -153,26 +163,40 @@ const BasicBlock = ({
name={"website_url"}
placeholder={t("EnterURL")}
value={websiteUrlValue}
error={`${t("ErrorWrongURL")}: ${window.location.origin}`}
error={
isWebsiteError
? `${t("ErrorWrongURL")}: ${window.location.origin}`
: t("ThisRequiredField")
}
onChange={onChange}
disabled={isEdit}
isRequired
isError={errorFields.includes("website_url")}
isError={isWebsiteRequiredError || isWebsiteError}
onBlur={onBlur}
/>
<SelectGroup
label={t("AppIcon")}
value={logoValue}
selectLabel={t("SelectNewImage")}
description={t("IconDescription")}
onSelect={onSelect}
/>
<FieldContainer
isVertical
labelVisible={false}
errorMessage={t("ThisRequiredField")}
hasError={isLogoRequiredError}
className="icon-field"
>
<SelectGroup
label={t("AppIcon")}
value={logoValue}
selectLabel={t("SelectNewImage")}
description={t("IconDescription")}
onSelect={onSelect}
/>
</FieldContainer>
<TextAreaGroup
label={t("Common:Description")}
name={"description"}
placeholder={t("EnterDescription")}
value={descriptionValue}
onChange={onChange}
increaseHeight={isLogoRequiredError}
/>
<InputGroup
label={t("AuthenticationMethod")}

View File

@ -114,6 +114,7 @@ const InputGroup = ({
onIconClick={withCopy && onCopyClick}
type={isPassword ? "password" : "text"}
onBlur={() => onBlur?.(name)}
hasError={isError}
/>
)}
{buttonLabel && (

View File

@ -24,7 +24,7 @@ interface MultiInputGroupProps {
name: string;
placeholder: string;
currentValue: string[];
hasError?: boolean;
onAdd: (name: string, value: string, remove?: boolean) => void;
helpButtonText?: string;
@ -39,7 +39,7 @@ const MultiInputGroup = ({
placeholder,
currentValue,
onAdd,
hasError,
helpButtonText,
isDisabled,
}: MultiInputGroupProps) => {
@ -74,9 +74,13 @@ const MultiInputGroup = ({
value={value}
placeholder={placeholder}
onChange={onChange}
error={`${t("ErrorWrongURL")}: ${window.location.origin}`}
error={
isError
? `${t("ErrorWrongURL")}: ${window.location.origin}`
: t("ThisRequiredField")
}
isRequired
isError={isError}
isError={isError || hasError}
>
<StyledInputRow>
<InputBlock
@ -89,6 +93,7 @@ const MultiInputGroup = ({
maxLength={255}
isDisabled={isDisabled}
onBlur={onBlur}
hasError={isError || hasError}
/>
<SelectorAddButton
onClick={() => {

View File

@ -11,6 +11,7 @@ interface OAuthBlockProps {
allowedOriginsValue: string[];
changeValue: (name: string, value: string) => void;
requiredErrorFields: string[];
isEdit: boolean;
}
@ -21,7 +22,7 @@ const OAuthBlock = ({
allowedOriginsValue,
changeValue,
requiredErrorFields,
isEdit,
}: OAuthBlockProps) => {
return (
@ -37,6 +38,7 @@ const OAuthBlock = ({
currentValue={redirectUrisValue}
helpButtonText={t("RedirectsURLSHelpButton")}
isDisabled={isEdit}
hasError={requiredErrorFields.includes("redirect_uris")}
/>
<MultiInputGroup
t={t}
@ -46,6 +48,7 @@ const OAuthBlock = ({
onAdd={changeValue}
currentValue={allowedOriginsValue}
helpButtonText={t("AllowedOriginsHelpButton")}
hasError={requiredErrorFields.includes("allowed_origins")}
/>
</StyledInputBlock>
</StyledBlock>

View File

@ -15,6 +15,7 @@ interface SupportBlockProps {
isEdit: boolean;
errorFields: string[];
onBlur?: (name: string) => void;
requiredErrorFields: string[];
}
const SupportBlock = ({
@ -27,6 +28,7 @@ const SupportBlock = ({
isEdit,
errorFields,
onBlur,
requiredErrorFields,
}: SupportBlockProps) => {
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const target = e.target;
@ -34,6 +36,11 @@ const SupportBlock = ({
changeValue(target.name, target.value);
};
const policyRequiredError = requiredErrorFields.includes("policy_url");
const termsRequiredError = requiredErrorFields.includes("terms_url");
const policyError = errorFields.includes("policy_url");
const termsError = errorFields.includes("terms_url");
return (
<StyledBlock>
<BlockHeader header={t("SupportAndLegalInfo")} />
@ -43,12 +50,16 @@ const SupportBlock = ({
name={"policy_url"}
placeholder={t("EnterURL")}
value={policyUrlValue}
error={`${t("ErrorWrongURL")}: ${window.location.origin}`}
error={
policyError
? `${t("ErrorWrongURL")}: ${window.location.origin}`
: t("ThisRequiredField")
}
onChange={onChange}
helpButtonText={t("PrivacyPolicyURLHelpButton")}
disabled={isEdit}
isRequired
isError={errorFields.includes("policy_url")}
isError={policyError || policyRequiredError}
onBlur={onBlur}
/>
<InputGroup
@ -56,12 +67,16 @@ const SupportBlock = ({
name={"terms_url"}
placeholder={t("EnterURL")}
value={termsUrlValue}
error={`${t("ErrorWrongURL")}: ${window.location.origin}`}
error={
termsError
? `${t("ErrorWrongURL")}: ${window.location.origin}`
: t("ThisRequiredField")
}
onChange={onChange}
helpButtonText={t("TermsOfServiceURLHelpButton")}
helpButtonText={t("TermsOfServiceURLHelpButton")}
disabled={isEdit}
isRequired
isError={errorFields.includes("terms_url")}
isError={termsError || termsRequiredError}
onBlur={onBlur}
/>
</StyledInputBlock>

View File

@ -12,7 +12,7 @@ interface TextAreaProps {
name: string;
value: string;
placeholder: string;
increaseHeight: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
@ -22,6 +22,7 @@ const TextAreaGroup = ({
name,
value,
placeholder,
increaseHeight,
onChange,
}: TextAreaProps) => {
@ -48,7 +49,7 @@ const TextAreaGroup = ({
onChange={onChange}
scale
tabIndex={0}
heightTextArea={60}
heightTextArea={increaseHeight ? 81 : 60}
maxLength={255}
/>
</StyledInputGroup>

View File

@ -81,6 +81,9 @@ const ClientForm = ({
});
const [errorFields, setErrorFields] = React.useState<string[]>([]);
const [requiredErrorFields, setRequiredErrorFields] = React.useState<
string[]
>([]);
const { t } = useTranslation(["OAuth", "Common"]);
@ -99,6 +102,41 @@ const ClientForm = ({
const onSaveClick = async () => {
try {
if (!id) {
let isValid = true;
for (let key in form) {
switch (key) {
case "name":
case "logo":
case "website_url":
case "terms_url":
case "policy_url":
if (form[key] === "") {
if (!requiredErrorFields.includes(key))
setRequiredErrorFields((s) => [...s, key]);
isValid = false;
}
isValid = isValid && !errorFields.includes(key);
break;
case "redirect_uris":
case "allowed_origins":
case "scopes":
if (form[key].length === 0) {
if (!requiredErrorFields.includes(key))
setRequiredErrorFields((s) => [...s, key]);
isValid = false;
}
isValid = isValid && !errorFields.includes(key);
break;
}
}
if (!isValid) return;
setIsRequestRunning(true);
await saveClient?.(form);
@ -299,8 +337,10 @@ const ClientForm = ({
for (let key in form) {
switch (key) {
case "name":
isValid = isValid && !!form[key];
case "logo":
case "website_url":
case "terms_url":
case "policy_url":
if (
errorFields.includes(key) &&
(!form[key] || (form[key].length > 2 && form[key].length < 256))
@ -310,73 +350,17 @@ const ClientForm = ({
});
}
isValid = isValid && !errorFields.includes(key);
if (requiredErrorFields.includes(key) && form[key] !== "")
setRequiredErrorFields((value) => value.filter((v) => v !== key));
break;
case "logo":
isValid = isValid && !!form[key];
break;
case "description":
// isValid = isValid && !!form[key];
break;
case "website_url":
isValid = isValid && !!form[key];
if (
errorFields.includes(key) &&
(!form[key] || isValidUrl(form[key]))
) {
setErrorFields((value) => {
return value.filter((n) => n !== key);
});
}
break;
case "redirect_uris":
isValid = isValid && form[key].length > 0;
break;
case "allowed_origins":
isValid = isValid && form[key].length > 0;
break;
case "logout_redirect_uris":
isValid = isValid;
break;
case "terms_url":
isValid = isValid && !!form[key];
if (
errorFields.includes(key) &&
(!form[key] || isValidUrl(form[key]))
) {
setErrorFields((value) => {
return value.filter((n) => n !== key);
});
}
break;
case "policy_url":
isValid = isValid && !!form[key];
if (
errorFields.includes(key) &&
(!form[key] || isValidUrl(form[key]))
) {
setErrorFields((value) => {
return value.filter((n) => n !== key);
});
}
break;
case "authentication_method":
isValid = isValid;
break;
case "scopes":
isValid = isValid && form[key].length > 0;
if (requiredErrorFields.includes(key) && form[key].length > 0)
setRequiredErrorFields((value) => value.filter((v) => v !== key));
break;
}
}
@ -406,6 +390,7 @@ const ClientForm = ({
changeValue={onChangeForm}
isEdit={isEdit}
errorFields={errorFields}
requiredErrorFields={requiredErrorFields}
onBlur={onBlur}
/>
{isEdit && (
@ -422,6 +407,7 @@ const ClientForm = ({
allowedOriginsValue={form.allowed_origins}
changeValue={onChangeForm}
isEdit={isEdit}
requiredErrorFields={requiredErrorFields}
/>
<ScopesBlock
t={t}
@ -437,6 +423,7 @@ const ClientForm = ({
changeValue={onChangeForm}
isEdit={isEdit}
errorFields={errorFields}
requiredErrorFields={requiredErrorFields}
onBlur={onBlur}
/>
<ButtonsBlock
@ -445,7 +432,7 @@ const ClientForm = ({
onSaveClick={onSaveClick}
onCancelClick={onCancelClick}
isRequestRunning={isRequestRunning}
saveButtonDisabled={!isValid}
saveButtonDisabled={isEdit ? !isValid : false}
cancelButtonDisabled={isRequestRunning}
currentDeviceType={currentDeviceType || ""}
/>

View File

@ -1,10 +1,10 @@
{
"date": "20231115_15376",
"date": "20231215_102541",
"checksums": {
"api.js": "47b1c3ac53feb3667b3f0b42cacf8038",
"api.js": "47725bc876523672712707c384e3cab2",
"api.poly.js": "586ce6831fa68f6bc33486e2cebc856e",
"browserDetector.js": "965560573181ee0d643d9a91d3014f39",
"config.json": "1a5613e4029a95b738179f30501700b6",
"browserDetector.js": "9e853f32d806dd83652ccc2d67510a84",
"config.json": "3f54595a6ea8207c37f1197069a2db86",
"tiff.min.js": "90496a3b99551fee464b40e26523e84d"
}
}