plane/apps/app/components/issues/description-form.tsx
pablohashescobar e9a0eb87cc
feat: inbox (#1023)
* dev: initialize inbox

* dev: inbox and inbox issues models, views and serializers

* dev: issue object filter for inbox

* dev: filter for search issues

* dev: inbox snooze and duplicates

* dev: set duplicate to null by default

* feat: inbox ui and services

* feat: project detail in inbox

* style: layout, popover, icons, sidebar

* dev: default inbox for project and pending issues count

* dev: fix exception when creating default inbox

* fix: empty state for inbox

* dev: auto issue state updation when rejected or marked duplicate

* fix: inbox update status

* fix: hydrating chose with old values

filters workflow

* feat: inbox issue filtering

* fix: issue inbox filtering

* feat: filter inbox issues

* refactor: analytics, border colors

* dev: filters and views for inbox

* dev: source for inboxissue and update list inbox issue

* dev: update list endpoint to house filters and additional data

* dev: bridge id for list

* dev: remove print logs

* dev: update inbox issue workflow

* dev: add description_html in issue details

* fix: inbox track event auth, chore: inbox issue action authorization

* fix: removed unnecessary api calls

* style: viewed issues

* fix: priority validation

* dev: remove print logs

* dev: update issue inbox update workflow

* chore: added inbox view context

* fix: type errors

* fix: build errors and warnings

* dev: update issue inbox workflow and log all the changes

* fix: filters logic, sidebar fields to show

* dev: update issue filtering status

* chore: update create inbox issue modal, fix: mutation issues

* dev: update issue accept workflow

* chore: add comment to inbox issues

* chore: remove inboxIssueId from url after deleting

* dev: update the issue triage workflow

* fix: mutation after issue status change

* chore: issue details sidebar divider

* fix: issue activity for inbox issues

* dev: update inbox perrmissions

* dev: create new permission layer

* chore: auth layer for inbox

* chore: show accepting status

* chore: show issue status at the top of issue details

---------

Co-authored-by: Dakshesh Jain <dakshesh.jain14@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2023-06-16 18:57:17 +05:30

178 lines
4.7 KiB
TypeScript

import { FC, useCallback, useEffect, useState } from "react";
import dynamic from "next/dynamic";
// react-hook-form
import { Controller, useForm } from "react-hook-form";
// hooks
import useReloadConfirmations from "hooks/use-reload-confirmation";
// components
import { Loader, TextArea } from "components/ui";
const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), {
ssr: false,
loading: () => (
<Loader>
<Loader.Item height="12rem" width="100%" />
</Loader>
),
});
// types
import { IIssue } from "types";
export interface IssueDescriptionFormValues {
name: string;
description: any;
description_html: string;
}
export interface IssueDetailsProps {
issue: {
name: string;
description: string;
description_html: string;
};
handleFormSubmit: (value: IssueDescriptionFormValues) => Promise<void>;
isAllowed: boolean;
}
export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
issue,
handleFormSubmit,
isAllowed,
}) => {
const [isSubmitting, setIsSubmitting] = useState(false);
const [characterLimit, setCharacterLimit] = useState(false);
const { setShowAlert } = useReloadConfirmations();
const {
handleSubmit,
watch,
setValue,
reset,
register,
control,
formState: { errors },
} = useForm<IIssue>({
defaultValues: {
name: "",
description: "",
description_html: "",
},
});
const handleDescriptionFormSubmit = useCallback(
async (formData: Partial<IIssue>) => {
if (!formData.name || formData.name.length === 0 || formData.name.length > 255) return;
await handleFormSubmit({
name: formData.name ?? "",
description: formData.description ?? "",
description_html: formData.description_html ?? "<p></p>",
});
},
[handleFormSubmit]
);
// reset form values
useEffect(() => {
if (!issue) return;
reset({
...issue,
});
}, [issue, reset]);
return (
<div className="relative">
<div className="relative">
<TextArea
id="name"
name="name"
placeholder="Enter issue name"
register={register}
onFocus={() => setCharacterLimit(true)}
onBlur={() => {
setCharacterLimit(false);
setIsSubmitting(true);
handleSubmit(handleDescriptionFormSubmit)()
.then(() => {
setIsSubmitting(false);
})
.catch(() => {
setIsSubmitting(false);
});
}}
required={true}
className="min-h-10 block w-full resize-none
overflow-hidden rounded border-none bg-transparent
px-3 py-2 text-xl outline-none ring-0 focus:ring-1 focus:ring-theme"
role="textbox"
disabled={!isAllowed}
/>
{characterLimit && (
<div className="pointer-events-none absolute bottom-0 right-0 z-[2] rounded bg-brand-surface-2 p-1 text-xs">
<span
className={`${
watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
}`}
>
{watch("name").length}
</span>
/255
</div>
)}
</div>
<span>{errors.name ? errors.name.message : null}</span>
<Controller
name="description"
control={control}
render={({ field: { value } }) => {
if (!value && !watch("description_html")) return <></>;
return (
<RemirrorRichTextEditor
value={
!value ||
value === "" ||
(typeof value === "object" && Object.keys(value).length === 0)
? watch("description_html")
: value
}
onJSONChange={(jsonValue) => {
setShowAlert(true);
setValue("description", jsonValue);
}}
onHTMLChange={(htmlValue) => {
setShowAlert(true);
setValue("description_html", htmlValue);
}}
onBlur={() => {
setIsSubmitting(true);
handleSubmit(handleDescriptionFormSubmit)()
.then(() => {
setIsSubmitting(false);
setShowAlert(false);
})
.catch(() => {
setIsSubmitting(false);
});
}}
placeholder="Description"
editable={isAllowed}
/>
);
}}
/>
<div
className={`absolute -bottom-8 right-0 text-sm text-brand-secondary ${
isSubmitting ? "block" : "hidden"
}`}
>
Saving...
</div>
</div>
);
};