diff --git a/packages/types/src/inbox.d.ts b/packages/types/src/inbox.d.ts index afb744f6cd5..bb60b35a21e 100644 --- a/packages/types/src/inbox.d.ts +++ b/packages/types/src/inbox.d.ts @@ -96,3 +96,16 @@ export type TInboxIssuePaginationInfo = TPaginationInfo & { export type TInboxIssueWithPagination = TInboxIssuePaginationInfo & { results: TInboxIssue[]; }; + +export type TInboxForm = { + anchor: string; + id: string; + is_disabled: boolean; +}; + +export type TInboxIssueForm = { + name: string; + description: string; + username: string; + email: string; +}; diff --git a/packages/ui/src/popovers/popover.tsx b/packages/ui/src/popovers/popover.tsx index 30a168965fd..4860a25a89a 100644 --- a/packages/ui/src/popovers/popover.tsx +++ b/packages/ui/src/popovers/popover.tsx @@ -18,6 +18,7 @@ export const Popover = (props: TPopover) => { panelClassName = "", children, popoverButtonRef, + buttonRefClassName = "", } = props; // states const [referenceElement, setReferenceElement] = useState(null); @@ -38,7 +39,7 @@ export const Popover = (props: TPopover) => { return ( -
+
} className={cn( diff --git a/packages/ui/src/popovers/types.ts b/packages/ui/src/popovers/types.ts index 51b1e877a52..7801e2d8536 100644 --- a/packages/ui/src/popovers/types.ts +++ b/packages/ui/src/popovers/types.ts @@ -5,6 +5,7 @@ export type TPopoverButtonDefaultOptions = { // button and button styling button?: ReactNode; buttonClassName?: string; + buttonRefClassName?: string; disabled?: boolean; }; diff --git a/space/public/instance/intake-sent-dark.png b/space/public/instance/intake-sent-dark.png new file mode 100644 index 00000000000..70a62730e0f Binary files /dev/null and b/space/public/instance/intake-sent-dark.png differ diff --git a/space/public/instance/intake-sent-light.png b/space/public/instance/intake-sent-light.png new file mode 100644 index 00000000000..9425c072372 Binary files /dev/null and b/space/public/instance/intake-sent-light.png differ diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/layout.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/layout.tsx index 167823fc2b4..e4cde6cbb1e 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/layout.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/layout.tsx @@ -2,7 +2,7 @@ // components import { AppHeader, ContentWrapper } from "@/components/core"; -import { ProjectInboxHeader } from "./header"; +import { ProjectInboxHeader } from "@/plane-web/components/projects/settings/intake"; export default function ProjectInboxIssuesLayout({ children }: { children: React.ReactNode }) { return ( diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/header.tsx b/web/ce/components/projects/settings/intake/header.tsx similarity index 100% rename from web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/header.tsx rename to web/ce/components/projects/settings/intake/header.tsx diff --git a/web/ce/components/projects/settings/intake/index.ts b/web/ce/components/projects/settings/intake/index.ts new file mode 100644 index 00000000000..49ac70fe213 --- /dev/null +++ b/web/ce/components/projects/settings/intake/index.ts @@ -0,0 +1 @@ +export * from "./header"; diff --git a/web/ce/constants/project/settings/features.tsx b/web/ce/constants/project/settings/features.tsx index 3fdccc97940..b9b39700d68 100644 --- a/web/ce/constants/project/settings/features.tsx +++ b/web/ce/constants/project/settings/features.tsx @@ -1,16 +1,23 @@ import { ReactNode } from "react"; import { FileText, Layers, Timer } from "lucide-react"; +import { IProject } from "@plane/types"; import { ContrastIcon, DiceIcon, Intake } from "@plane/ui"; +export type TProperties = { + property: string; + title: string; + description: string; + icon: ReactNode; + isPro: boolean; + isEnabled: boolean; + renderChildren?: ( + currentProjectDetails: IProject, + isAdmin: boolean, + handleSubmit: (featureKey: string, featureProperty: string) => Promise + ) => ReactNode; +}; export type TFeatureList = { - [key: string]: { - property: string; - title: string; - description: string; - icon: ReactNode; - isPro: boolean; - isEnabled: boolean; - }; + [key: string]: TProperties; }; export type TProjectFeatures = { diff --git a/web/core/components/project/settings/features-list.tsx b/web/core/components/project/settings/features-list.tsx index 91eb188024d..f1f17f2d598 100644 --- a/web/core/components/project/settings/features-list.tsx +++ b/web/core/components/project/settings/features-list.tsx @@ -70,31 +70,40 @@ export const ProjectFeaturesList: FC = observer((props) => { return (
-
-
- {featureItem.icon} -
-
-
-

{featureItem.title}

- {featureItem.isPro && ( - - - - )} +
+
+
+ {featureItem.icon} +
+
+
+

{featureItem.title}

+ {featureItem.isPro && ( + + + + )} +
+

+ {featureItem.description} +

-

{featureItem.description}

-
- handleSubmit(featureItemKey, featureItem.property)} - disabled={!featureItem.isEnabled || !isAdmin} - size="sm" - /> + handleSubmit(featureItemKey, featureItem.property)} + disabled={!featureItem.isEnabled || !isAdmin} + size="sm" + /> +
+
+ {currentProjectDetails?.[featureItem.property as keyof IProject] && + featureItem.renderChildren && + featureItem.renderChildren(currentProjectDetails, isAdmin, handleSubmit)} +
); })} diff --git a/web/core/services/inbox/inbox-issue.service.ts b/web/core/services/inbox/inbox-issue.service.ts index f8fcf72c4e4..61aaeb84906 100644 --- a/web/core/services/inbox/inbox-issue.service.ts +++ b/web/core/services/inbox/inbox-issue.service.ts @@ -1,5 +1,5 @@ // types -import type { TInboxIssue, TIssue, TInboxIssueWithPagination } from "@plane/types"; +import type { TInboxIssue, TIssue, TInboxIssueWithPagination, TInboxForm } from "@plane/types"; import { API_BASE_URL } from "@/helpers/common.helper"; import { APIService } from "@/services/api.service"; // helpers @@ -75,4 +75,30 @@ export class InboxIssueService extends APIService { throw error?.response?.data; }); } + + async retrievePublishForm(workspaceSlug: string, projectId: string): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/publish-intake/`) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + + async updatePublishForm(workspaceSlug: string, projectId: string, is_disabled: boolean): Promise { + return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/publish-intake/`, { + is_disabled, + }) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + + async regeneratePublishForm(workspaceSlug: string, projectId: string): Promise { + return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/publish-intake-regenerate/`) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } } diff --git a/web/core/store/inbox/project-inbox.store.ts b/web/core/store/inbox/project-inbox.store.ts index 2897825397b..f6953debf82 100644 --- a/web/core/store/inbox/project-inbox.store.ts +++ b/web/core/store/inbox/project-inbox.store.ts @@ -12,6 +12,7 @@ import { TInboxIssueSorting, TInboxIssuePaginationInfo, TInboxIssueSortingOrderByQueryParam, + TInboxForm, } from "@plane/types"; // helpers import { EInboxIssueCurrentTab, EInboxIssueStatus, EPastDurationFilters, getCustomDates } from "@/helpers/inbox.helper"; @@ -39,6 +40,7 @@ export interface IProjectInboxStore { inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined; inboxIssues: Record; // issue_id -> IInboxIssueStore inboxIssueIds: string[]; + intakeForms: Record; // computed inboxFilters: Partial; // computed project inbox filters inboxSorting: Partial; // computed project inbox sorting @@ -68,6 +70,9 @@ export interface IProjectInboxStore { ) => Promise; fetchInboxPaginationIssues: (workspaceSlug: string, projectId: string) => Promise; fetchInboxIssueById: (workspaceSlug: string, projectId: string, inboxIssueId: string) => Promise; + fetchIntakeForms: (workspaceSlug: string, projectId: string) => Promise; + toggleIntakeForms: (workspaceSlug: string, projectId: string, isDisabled: boolean) => Promise; + regenerateIntakeForms: (workspaceSlug: string, projectId: string) => Promise; createInboxIssue: ( workspaceSlug: string, projectId: string, @@ -89,6 +94,7 @@ export class ProjectInboxStore implements IProjectInboxStore { inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined = undefined; inboxIssues: Record = {}; inboxIssueIds: string[] = []; + intakeForms: Record = {}; // services inboxIssueService; @@ -103,6 +109,7 @@ export class ProjectInboxStore implements IProjectInboxStore { inboxIssuePaginationInfo: observable, inboxIssues: observable, inboxIssueIds: observable, + intakeForms: observable, // computed inboxFilters: computed, inboxSorting: computed, @@ -310,6 +317,45 @@ export class ProjectInboxStore implements IProjectInboxStore { } }; + fetchIntakeForms = async (workspaceSlug: string, projectId: string) => { + try { + const intakeForms = await this.inboxIssueService.retrievePublishForm(workspaceSlug, projectId); + if (intakeForms) + runInAction(() => { + set(this.intakeForms, projectId, intakeForms); + }); + } catch { + console.error("Error fetching the publish forms"); + } + }; + + toggleIntakeForms = async (workspaceSlug: string, projectId: string, isDisabled: boolean) => { + try { + runInAction(() => { + set(this.intakeForms, projectId, { ...this.intakeForms[projectId], is_disabled: isDisabled }); + }); + await this.inboxIssueService.updatePublishForm(workspaceSlug, projectId, isDisabled); + } catch { + console.error("Error fetching the publish forms"); + runInAction(() => { + set(this.intakeForms, projectId, { ...this.intakeForms[projectId], is_disabled: !isDisabled }); + }); + } + }; + + regenerateIntakeForms = async (workspaceSlug: string, projectId: string) => { + try { + const form = await this.inboxIssueService.regeneratePublishForm(workspaceSlug, projectId); + if (form) { + runInAction(() => { + set(this.intakeForms, projectId, form); + }); + } + } catch { + console.error("Error fetching the publish forms"); + } + }; + /** * @description fetch intake issues with paginated data * @param workspaceSlug diff --git a/web/ee/components/projects/settings/intake/index.ts b/web/ee/components/projects/settings/intake/index.ts new file mode 100644 index 00000000000..4ffe8263896 --- /dev/null +++ b/web/ee/components/projects/settings/intake/index.ts @@ -0,0 +1 @@ +export * from "ce/components/projects/settings/intake";