forked from github/plane
fix: form validations (#213)
This commit is contained in:
parent
27e3364a1f
commit
60a35e6af1
@ -18,6 +18,7 @@ const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor
|
||||
});
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import useToast from "hooks/use-toast";
|
||||
|
||||
export interface IssueDescriptionFormValues {
|
||||
name: string;
|
||||
@ -31,7 +32,16 @@ export interface IssueDetailsProps {
|
||||
}
|
||||
|
||||
export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormSubmit }) => {
|
||||
const { handleSubmit, watch, setValue, reset } = useForm<IIssue>({
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
reset,
|
||||
formState: { errors },
|
||||
setError,
|
||||
} = useForm<IIssue>({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
@ -41,13 +51,31 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormS
|
||||
|
||||
const handleDescriptionFormSubmit = useCallback(
|
||||
(formData: Partial<IIssue>) => {
|
||||
if (!formData.name || formData.name === "") {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error in saving!",
|
||||
message: "Title is required.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (formData.name.length > 255) {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error in saving!",
|
||||
message: "Title cannot have more than 255 characters.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
handleFormSubmit({
|
||||
name: formData.name ?? "",
|
||||
description: formData.description,
|
||||
description_html: formData.description_html,
|
||||
});
|
||||
},
|
||||
[handleFormSubmit]
|
||||
[handleFormSubmit, setToastAlert]
|
||||
);
|
||||
|
||||
const debounceHandler = useMemo(
|
||||
@ -83,9 +111,8 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormS
|
||||
}}
|
||||
mode="transparent"
|
||||
className="text-xl font-medium"
|
||||
required={true}
|
||||
/>
|
||||
|
||||
<span>{errors.name ? errors.name.message : null}</span>
|
||||
<RemirrorRichTextEditor
|
||||
value={watch("description")}
|
||||
placeholder="Describe the issue..."
|
||||
|
@ -197,7 +197,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
||||
required: "Title is required",
|
||||
maxLength: {
|
||||
value: 255,
|
||||
message: "Name should be less than 255 characters",
|
||||
message: "Title should be less than 255 characters",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
@ -93,9 +93,8 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
|
||||
const projectIdentifier = watch("identifier") ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
if (projectName && isChangeIdentifierRequired) {
|
||||
if (projectName && isChangeIdentifierRequired)
|
||||
setValue("identifier", projectName.replace(/ /g, "").toUpperCase().substring(0, 3));
|
||||
}
|
||||
}, [projectName, projectIdentifier, setValue, isChangeIdentifierRequired]);
|
||||
|
||||
useEffect(() => () => setIsChangeIdentifierRequired(true), [isOpen]);
|
||||
@ -215,6 +214,10 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
|
||||
register={register}
|
||||
validations={{
|
||||
required: "Name is required",
|
||||
maxLength: {
|
||||
value: 255,
|
||||
message: "Name should be less than 255 characters",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -162,6 +162,10 @@ const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, proj
|
||||
register={register}
|
||||
validations={{
|
||||
required: "Name is required",
|
||||
maxLength: {
|
||||
value: 255,
|
||||
message: "Name should be less than 255 characters",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -13,7 +13,7 @@ import stateService from "services/state.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
import { Button, Input, Select } from "components/ui";
|
||||
import { Button, CustomSelect, Input, Select } from "components/ui";
|
||||
// types
|
||||
import type { IState } from "types";
|
||||
// fetch-keys
|
||||
@ -183,18 +183,27 @@ export const CreateUpdateStateInline: React.FC<Props> = ({
|
||||
autoComplete="off"
|
||||
/>
|
||||
{data && (
|
||||
<Select
|
||||
id="group"
|
||||
<Controller
|
||||
name="group"
|
||||
error={errors.group}
|
||||
register={register}
|
||||
validations={{
|
||||
required: true,
|
||||
}}
|
||||
options={Object.keys(GROUP_CHOICES).map((key) => ({
|
||||
value: key,
|
||||
label: GROUP_CHOICES[key as keyof typeof GROUP_CHOICES],
|
||||
}))}
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<CustomSelect
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
label={
|
||||
Object.keys(GROUP_CHOICES).find((k) => k === value.toString())
|
||||
? GROUP_CHOICES[value.toString() as keyof typeof GROUP_CHOICES]
|
||||
: "Select group"
|
||||
}
|
||||
input
|
||||
>
|
||||
{Object.keys(GROUP_CHOICES).map((key) => (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
{GROUP_CHOICES[key as keyof typeof GROUP_CHOICES]}
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
@ -209,7 +218,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({
|
||||
Cancel
|
||||
</Button>
|
||||
<Button theme="primary" disabled={isSubmitting} type="submit">
|
||||
{isSubmitting ? "Loading..." : data ? "Update" : "Create"}
|
||||
{isSubmitting ? (data ? "Updating..." : "Creating...") : data ? "Update" : "Create"}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
|
@ -178,6 +178,10 @@ const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, pro
|
||||
register={register}
|
||||
validations={{
|
||||
required: "Name is required",
|
||||
maxLength: {
|
||||
value: 255,
|
||||
message: "Name should be less than 255 characters",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -32,7 +32,7 @@ export const CustomDatePicker: React.FC<Props> = ({
|
||||
renderAs === "input"
|
||||
? "block bg-transparent text-sm focus:outline-none rounded-md border border-gray-300 px-3 py-2 w-full cursor-pointer"
|
||||
: renderAs === "button"
|
||||
? "w-full cursor-pointer rounded-md border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
||||
? "w-full rounded-md border px-2 py-1 text-xs shadow-sm hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 duration-300 cursor-pointer"
|
||||
: ""
|
||||
} ${error ? "border-red-500 bg-red-200" : ""} bg-transparent caret-transparent`}
|
||||
isClearable={isClearable}
|
||||
|
Loading…
Reference in New Issue
Block a user