diff --git a/web/components/core/filters/filters-list.tsx b/web/components/core/filters/filters-list.tsx index acb307d2a..0480413c2 100644 --- a/web/components/core/filters/filters-list.tsx +++ b/web/components/core/filters/filters-list.tsx @@ -168,7 +168,7 @@ export const FiltersList: React.FC = ({ filters, setFilters, clearAllFilt key={memberId} className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1" > - + {member?.display_name} = (props) => { key={`mentions-${member.id}`} isChecked={appliedFilters?.includes(member.id) ? true : false} onClick={() => handleUpdate(member.id)} - icon={} + icon={} title={member.display_name} /> ))} @@ -65,4 +64,4 @@ export const FilterMentions: React.FC = (props) => { )} ); -}; \ No newline at end of file +}; diff --git a/web/components/views/select-filters.tsx b/web/components/views/select-filters.tsx deleted file mode 100644 index 8a5f9dd12..000000000 --- a/web/components/views/select-filters.tsx +++ /dev/null @@ -1,278 +0,0 @@ -import { useState } from "react"; -import { useRouter } from "next/router"; -import useSWR from "swr"; -// services -import { ProjectStateService, ProjectService } from "services/project"; -import { IssueLabelService } from "services/issue"; -// ui -import { Avatar, MultiLevelDropdown } from "components/ui"; -// icons -import { PriorityIcon, StateGroupIcon } from "@plane/ui"; -// helpers -import { getStatesList } from "helpers/state.helper"; -import { checkIfArraysHaveSameElements } from "helpers/array.helper"; -// types -import { IIssueFilterOptions } from "types"; -// fetch-keys -import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATES_LIST } from "constants/fetch-keys"; -// constants -import { PRIORITIES } from "constants/project"; -import { DATE_FILTER_OPTIONS } from "constants/filters"; - -type Props = { - filters: Partial; - onSelect: (option: any) => void; - direction?: "left" | "right"; - height?: "sm" | "md" | "rg" | "lg"; -}; - -const projectService = new ProjectService(); -const projectStateService = new ProjectStateService(); -const issueLabelService = new IssueLabelService(); - -export const SelectFilters: React.FC = ({ filters, onSelect, direction = "right", height = "md" }) => { - const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); - const [dateFilterType, setDateFilterType] = useState<{ - title: string; - type: "start_date" | "target_date"; - }>({ - title: "", - type: "start_date", - }); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { data: states } = useSWR( - workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => projectStateService.getStates(workspaceSlug as string, projectId as string) - : null - ); - const statesList = getStatesList(states); - - const { data: members } = useSWR( - projectId ? PROJECT_MEMBERS(projectId as string) : null, - workspaceSlug && projectId - ? () => projectService.fetchProjectMembers(workspaceSlug as string, projectId as string) - : null - ); - - const { data: issueLabels } = useSWR( - projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, - workspaceSlug && projectId - ? () => issueLabelService.getProjectIssueLabels(workspaceSlug as string, projectId.toString()) - : null - ); - - const projectFilterOption = [ - { - id: "priority", - label: "Priority", - value: PRIORITIES, - hasChildren: true, - children: PRIORITIES.map((priority) => ({ - id: priority === null ? "null" : priority, - label: ( -
- - {priority ?? "None"} -
- ), - value: { - key: "priority", - value: priority === null ? "null" : priority, - }, - selected: filters?.priority?.includes(priority === null ? "null" : priority), - })), - }, - { - id: "state", - label: "State", - value: statesList, - hasChildren: true, - children: statesList?.map((state) => ({ - id: state.id, - label: ( -
- - {state.name} -
- ), - value: { - key: "state", - value: state.id, - }, - selected: filters?.state?.includes(state.id), - })), - }, - { - id: "assignees", - label: "Assignees", - value: members, - hasChildren: true, - children: members?.map((member) => ({ - id: member.member.id, - label: ( -
- - {member.member.display_name} -
- ), - value: { - key: "assignees", - value: member.member.id, - }, - selected: filters?.assignees?.includes(member.member.id), - })), - }, - { - id: "mentions", - label: "Mentions", - value: members, - hasChildren: true, - children: members?.map((member) => ({ - id: member.member.id, - label: ( -
- - {member.member.display_name} -
- ), - value: { - key: "mentions", - value: member.member.id, - }, - selected: filters?.mentions?.includes(member.member.id), - })), - }, - { - id: "created_by", - label: "Created by", - value: members, - hasChildren: true, - children: members?.map((member) => ({ - id: member.member.id, - label: ( -
- - {member.member.display_name} -
- ), - value: { - key: "created_by", - value: member.member.id, - }, - selected: filters?.created_by?.includes(member.member.id), - })), - }, - { - id: "labels", - label: "Labels", - value: issueLabels, - hasChildren: true, - children: issueLabels?.map((label) => ({ - id: label.id, - label: ( -
-
- {label.name} -
- ), - value: { - key: "labels", - value: label.id, - }, - selected: filters?.labels?.includes(label.id), - })), - }, - { - id: "start_date", - label: "Start date", - value: DATE_FILTER_OPTIONS, - hasChildren: true, - children: [ - ...DATE_FILTER_OPTIONS.map((option) => ({ - id: option.name, - label: option.name, - value: { - key: "start_date", - value: option.value, - }, - selected: checkIfArraysHaveSameElements(filters?.start_date ?? [], [option.value]), - })), - { - id: "custom", - label: "Custom", - value: "custom", - element: ( - - ), - }, - ], - }, - { - id: "target_date", - label: "Due date", - value: DATE_FILTER_OPTIONS, - hasChildren: true, - children: [ - ...DATE_FILTER_OPTIONS.map((option) => ({ - id: option.name, - label: option.name, - value: { - key: "target_date", - value: option.value, - }, - selected: checkIfArraysHaveSameElements(filters?.target_date ?? [], [option.value]), - })), - { - id: "custom", - label: "Custom", - value: "custom", - element: ( - - ), - }, - ], - }, - ]; - return ( - <> - - - ); -}; diff --git a/web/components/web-view/add-comment.tsx b/web/components/web-view/add-comment.tsx deleted file mode 100644 index ee1aee5dd..000000000 --- a/web/components/web-view/add-comment.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React from "react"; -import { useRouter } from "next/router"; -import { useForm, Controller } from "react-hook-form"; -// hooks -import useProjectDetails from "hooks/use-project-details"; -// components -import { LiteTextEditorWithRef } from "@plane/lite-text-editor"; -import { Button } from "@plane/ui"; -// icons -import { Send } from "lucide-react"; -// types -import type { IIssueComment } from "types"; -// services -import { FileService } from "services/file.service"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; - -const defaultValues: Partial = { - access: "INTERNAL", - comment_html: "", -}; - -type Props = { - disabled?: boolean; - onSubmit: (data: IIssueComment) => Promise; -}; - -type commentAccessType = { - icon: string; - key: string; - label: "Private" | "Public"; -}; - -const commentAccess: commentAccessType[] = [ - { - icon: "lock", - key: "INTERNAL", - label: "Private", - }, - { - icon: "public", - key: "EXTERNAL", - label: "Public", - }, -]; - -const fileService = new FileService(); - -export const AddComment: React.FC = ({ disabled = false, onSubmit }) => { - const editorRef = React.useRef(null); - - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { projectDetails } = useProjectDetails(); - - const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectDetails?.id) - - const showAccessSpecifier = projectDetails?.is_deployed || false; - - const { - control, - formState: { isSubmitting }, - handleSubmit, - reset, - } = useForm({ defaultValues }); - - const handleAddComment = async (formData: IIssueComment) => { - if (!formData.comment_html || isSubmitting) return; - - await onSubmit(formData).then(() => { - reset(defaultValues); - editorRef.current?.clearEditor(); - }); - }; - - return ( -
-
- ( - ( -

" : commentValue} - customClassName="p-3 min-h-[100px] shadow-sm" - debouncedUpdatesEnabled={false} - mentionSuggestions={editorSuggestions.mentionSuggestions} - mentionHighlights={editorSuggestions.mentionHighlights} - onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)} - commentAccessSpecifier={{ accessValue, onAccessChange, showAccessSpecifier, commentAccess }} - /> - )} - /> - )} - /> -
- -
- -
-
- ); -}; diff --git a/web/components/web-view/comment-card.tsx b/web/components/web-view/comment-card.tsx deleted file mode 100644 index 545cc63dc..000000000 --- a/web/components/web-view/comment-card.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import React, { useEffect, useState } from "react"; - -// react-hook-form -import { useForm } from "react-hook-form"; -// icons -import { Check, Globe2, Lock, MessageSquare, Pencil, Trash2, X } from "lucide-react"; -// service -import { FileService } from "services/file.service"; -// hooks -import useUser from "hooks/use-user"; -// ui -import { CustomMenu } from "@plane/ui"; -import { CommentReaction } from "components/issues"; -import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-text-editor"; - -// helpers -import { timeAgo } from "helpers/date-time.helper"; -// types -import type { IIssueComment } from "types"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; - -type Props = { - comment: IIssueComment; - handleCommentDeletion: (comment: string) => void; - onSubmit: (commentId: string, data: Partial) => void; - showAccessSpecifier?: boolean; - workspaceSlug: string; - disabled?: boolean; -}; - -// services -const fileService = new FileService(); - -export const CommentCard: React.FC = (props) => { - const { comment, handleCommentDeletion, onSubmit, showAccessSpecifier = false, workspaceSlug, disabled } = props; - - const { user } = useUser(); - - const editorSuggestions = useEditorSuggestions(workspaceSlug, comment.project_detail.id) - - const editorRef = React.useRef(null); - const showEditorRef = React.useRef(null); - - const [isEditing, setIsEditing] = useState(false); - - const { - formState: { isSubmitting }, - handleSubmit, - setFocus, - watch, - setValue, - } = useForm({ - defaultValues: comment, - }); - - const onEnter = (formData: Partial) => { - if (isSubmitting) return; - setIsEditing(false); - - onSubmit(comment.id, formData); - - editorRef.current?.setEditorValue(formData.comment_html); - showEditorRef.current?.setEditorValue(formData.comment_html); - }; - - useEffect(() => { - isEditing && setFocus("comment"); - }, [isEditing, setFocus]); - - return ( -
-
- {comment.actor_detail.avatar && comment.actor_detail.avatar !== "" ? ( - { - ) : ( -
- {comment.actor_detail.is_bot - ? comment.actor_detail.first_name.charAt(0) - : comment.actor_detail.display_name.charAt(0)} -
- )} - - - - -
-
-
-
- {comment.actor_detail.is_bot ? comment.actor_detail.first_name + " Bot" : comment.actor_detail.display_name} -
-

commented {timeAgo(comment.created_at)}

-
-
-
-
- { - setValue("comment_json", comment_json); - setValue("comment_html", comment_html); - }} - mentionSuggestions={editorSuggestions.mentionSuggestions} - mentionHighlights={editorSuggestions.mentionHighlights} - /> -
-
- - -
-
-
- {showAccessSpecifier && ( -
- {comment.access === "INTERNAL" ? : } -
- )} - - -
-
-
- {user?.id === comment.actor && !disabled && ( - - setIsEditing(true)} className="flex items-center gap-1"> - - Edit comment - - {showAccessSpecifier && ( - <> - {comment.access === "INTERNAL" ? ( - onSubmit(comment.id, { access: "EXTERNAL" })} - className="flex items-center gap-1" - > - - Switch to public comment - - ) : ( - onSubmit(comment.id, { access: "INTERNAL" })} - className="flex items-center gap-1" - > - - Switch to private comment - - )} - - )} - { - handleCommentDeletion(comment.id); - }} - className="flex items-center gap-1" - > - - Delete comment - - - )} -
- ); -}; diff --git a/web/components/web-view/issue-web-view-form.tsx b/web/components/web-view/issue-web-view-form.tsx deleted file mode 100644 index e805886b4..000000000 --- a/web/components/web-view/issue-web-view-form.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import { Controller } from "react-hook-form"; - -// services -import { FileService } from "services/file.service"; -// hooks -import { useDebouncedCallback } from "use-debounce"; -import useReloadConfirmations from "hooks/use-reload-confirmation"; -// ui -import { TextArea } from "@plane/ui"; -// components -import { RichTextEditor } from "@plane/rich-text-editor"; -import { Label } from "components/web-view"; -// types -import type { IIssue } from "types"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; - -type Props = { - isAllowed: boolean; - issueDetails: IIssue; - submitChanges: (data: Partial) => Promise; - register: any; - control: any; - watch: any; - handleSubmit: any; -}; - -// services -const fileService = new FileService(); - -export const IssueWebViewForm: React.FC = (props) => { - const { isAllowed, issueDetails, submitChanges, control, watch, handleSubmit } = props; - - const router = useRouter(); - const { workspaceSlug } = router.query; - - const [characterLimit, setCharacterLimit] = useState(false); - const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); - - const { setShowAlert } = useReloadConfirmations(); - - const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, issueDetails.project_detail.id) - - useEffect(() => { - if (isSubmitting === "submitted") { - setShowAlert(false); - setTimeout(async () => { - setIsSubmitting("saved"); - }, 2000); - } else if (isSubmitting === "submitting") { - setShowAlert(true); - } - }, [isSubmitting, setShowAlert]); - - const debouncedTitleSave = useDebouncedCallback(async () => { - setTimeout(async () => { - handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted")); - }, 500); - }, 1000); - - const handleDescriptionFormSubmit = useCallback( - async (formData: Partial) => { - if (!formData?.name || formData?.name.length === 0 || formData?.name.length > 255) return; - - await submitChanges({ - name: formData.name ?? "", - description_html: formData.description_html ?? "

", - }); - }, - [submitChanges] - ); - - return ( - <> -
- -
- {isAllowed ? ( - ( -