WEB-2381 Chore: intake refactor (#5752)
* chore: intake emails and forms * fix: moved files to ee * fix: intake form ui * fix: settings apis integrated * fix: removed publish api * fix: removed space app * fix: lint issue * fix: removed logs * fix: removed comment * fix: improved success image
This commit is contained in:
parent
fb49644185
commit
0f25f39404
13 changed files with 137 additions and 32 deletions
13
packages/types/src/inbox.d.ts
vendored
13
packages/types/src/inbox.d.ts
vendored
|
|
@ -96,3 +96,16 @@ export type TInboxIssuePaginationInfo = TPaginationInfo & {
|
||||||
export type TInboxIssueWithPagination = TInboxIssuePaginationInfo & {
|
export type TInboxIssueWithPagination = TInboxIssuePaginationInfo & {
|
||||||
results: TInboxIssue[];
|
results: TInboxIssue[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TInboxForm = {
|
||||||
|
anchor: string;
|
||||||
|
id: string;
|
||||||
|
is_disabled: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TInboxIssueForm = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export const Popover = (props: TPopover) => {
|
||||||
panelClassName = "",
|
panelClassName = "",
|
||||||
children,
|
children,
|
||||||
popoverButtonRef,
|
popoverButtonRef,
|
||||||
|
buttonRefClassName = "",
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null);
|
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
@ -38,7 +39,7 @@ export const Popover = (props: TPopover) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeadlessReactPopover className={cn("relative flex h-full w-full items-center justify-center", popoverClassName)}>
|
<HeadlessReactPopover className={cn("relative flex h-full w-full items-center justify-center", popoverClassName)}>
|
||||||
<div ref={setReferenceElement} className="w-full">
|
<div ref={setReferenceElement} className={cn("w-full", buttonRefClassName)}>
|
||||||
<HeadlessReactPopover.Button
|
<HeadlessReactPopover.Button
|
||||||
ref={popoverButtonRef as Ref<HTMLButtonElement>}
|
ref={popoverButtonRef as Ref<HTMLButtonElement>}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export type TPopoverButtonDefaultOptions = {
|
||||||
// button and button styling
|
// button and button styling
|
||||||
button?: ReactNode;
|
button?: ReactNode;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
|
buttonRefClassName?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
BIN
space/public/instance/intake-sent-dark.png
Normal file
BIN
space/public/instance/intake-sent-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 135 KiB |
BIN
space/public/instance/intake-sent-light.png
Normal file
BIN
space/public/instance/intake-sent-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 190 KiB |
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { AppHeader, ContentWrapper } from "@/components/core";
|
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 }) {
|
export default function ProjectInboxIssuesLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
1
web/ce/components/projects/settings/intake/index.ts
Normal file
1
web/ce/components/projects/settings/intake/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./header";
|
||||||
|
|
@ -1,16 +1,23 @@
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { FileText, Layers, Timer } from "lucide-react";
|
import { FileText, Layers, Timer } from "lucide-react";
|
||||||
|
import { IProject } from "@plane/types";
|
||||||
import { ContrastIcon, DiceIcon, Intake } from "@plane/ui";
|
import { ContrastIcon, DiceIcon, Intake } from "@plane/ui";
|
||||||
|
|
||||||
export type TFeatureList = {
|
export type TProperties = {
|
||||||
[key: string]: {
|
|
||||||
property: string;
|
property: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
isPro: boolean;
|
isPro: boolean;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
};
|
renderChildren?: (
|
||||||
|
currentProjectDetails: IProject,
|
||||||
|
isAdmin: boolean,
|
||||||
|
handleSubmit: (featureKey: string, featureProperty: string) => Promise<void>
|
||||||
|
) => ReactNode;
|
||||||
|
};
|
||||||
|
export type TFeatureList = {
|
||||||
|
[key: string]: TProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TProjectFeatures = {
|
export type TProjectFeatures = {
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,9 @@ export const ProjectFeaturesList: FC<Props> = observer((props) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={featureItemKey}
|
key={featureItemKey}
|
||||||
className="flex items-center justify-between gap-x-8 gap-y-2 border-b border-custom-border-100 bg-custom-background-100 pb-2 pt-4 last:border-b-0"
|
className="gap-x-8 gap-y-2 border-b border-custom-border-100 bg-custom-background-100 pb-2 pt-4"
|
||||||
>
|
>
|
||||||
|
<div key={featureItemKey} className="flex items-center justify-between">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="flex items-center justify-center rounded bg-custom-background-90 p-3">
|
<div className="flex items-center justify-center rounded bg-custom-background-90 p-3">
|
||||||
{featureItem.icon}
|
{featureItem.icon}
|
||||||
|
|
@ -85,7 +86,9 @@ export const ProjectFeaturesList: FC<Props> = observer((props) => {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm leading-5 tracking-tight text-custom-text-300">{featureItem.description}</p>
|
<p className="text-sm leading-5 tracking-tight text-custom-text-300">
|
||||||
|
{featureItem.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -96,6 +99,12 @@ export const ProjectFeaturesList: FC<Props> = observer((props) => {
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="pl-14">
|
||||||
|
{currentProjectDetails?.[featureItem.property as keyof IProject] &&
|
||||||
|
featureItem.renderChildren &&
|
||||||
|
featureItem.renderChildren(currentProjectDetails, isAdmin, handleSubmit)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// types
|
// 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 { API_BASE_URL } from "@/helpers/common.helper";
|
||||||
import { APIService } from "@/services/api.service";
|
import { APIService } from "@/services/api.service";
|
||||||
// helpers
|
// helpers
|
||||||
|
|
@ -75,4 +75,30 @@ export class InboxIssueService extends APIService {
|
||||||
throw error?.response?.data;
|
throw error?.response?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async retrievePublishForm(workspaceSlug: string, projectId: string): Promise<TInboxForm> {
|
||||||
|
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<TInboxIssue> {
|
||||||
|
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<TInboxIssue> {
|
||||||
|
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/publish-intake-regenerate/`)
|
||||||
|
.then((response) => response?.data)
|
||||||
|
.catch((error) => {
|
||||||
|
throw error?.response?.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
TInboxIssueSorting,
|
TInboxIssueSorting,
|
||||||
TInboxIssuePaginationInfo,
|
TInboxIssuePaginationInfo,
|
||||||
TInboxIssueSortingOrderByQueryParam,
|
TInboxIssueSortingOrderByQueryParam,
|
||||||
|
TInboxForm,
|
||||||
} from "@plane/types";
|
} from "@plane/types";
|
||||||
// helpers
|
// helpers
|
||||||
import { EInboxIssueCurrentTab, EInboxIssueStatus, EPastDurationFilters, getCustomDates } from "@/helpers/inbox.helper";
|
import { EInboxIssueCurrentTab, EInboxIssueStatus, EPastDurationFilters, getCustomDates } from "@/helpers/inbox.helper";
|
||||||
|
|
@ -39,6 +40,7 @@ export interface IProjectInboxStore {
|
||||||
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined;
|
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined;
|
||||||
inboxIssues: Record<string, IInboxIssueStore>; // issue_id -> IInboxIssueStore
|
inboxIssues: Record<string, IInboxIssueStore>; // issue_id -> IInboxIssueStore
|
||||||
inboxIssueIds: string[];
|
inboxIssueIds: string[];
|
||||||
|
intakeForms: Record<string, TInboxForm>;
|
||||||
// computed
|
// computed
|
||||||
inboxFilters: Partial<TInboxIssueFilter>; // computed project inbox filters
|
inboxFilters: Partial<TInboxIssueFilter>; // computed project inbox filters
|
||||||
inboxSorting: Partial<TInboxIssueSorting>; // computed project inbox sorting
|
inboxSorting: Partial<TInboxIssueSorting>; // computed project inbox sorting
|
||||||
|
|
@ -68,6 +70,9 @@ export interface IProjectInboxStore {
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
fetchInboxPaginationIssues: (workspaceSlug: string, projectId: string) => Promise<void>;
|
fetchInboxPaginationIssues: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||||
fetchInboxIssueById: (workspaceSlug: string, projectId: string, inboxIssueId: string) => Promise<TInboxIssue>;
|
fetchInboxIssueById: (workspaceSlug: string, projectId: string, inboxIssueId: string) => Promise<TInboxIssue>;
|
||||||
|
fetchIntakeForms: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||||
|
toggleIntakeForms: (workspaceSlug: string, projectId: string, isDisabled: boolean) => Promise<void>;
|
||||||
|
regenerateIntakeForms: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||||
createInboxIssue: (
|
createInboxIssue: (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
|
|
@ -89,6 +94,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
||||||
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined = undefined;
|
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined = undefined;
|
||||||
inboxIssues: Record<string, IInboxIssueStore> = {};
|
inboxIssues: Record<string, IInboxIssueStore> = {};
|
||||||
inboxIssueIds: string[] = [];
|
inboxIssueIds: string[] = [];
|
||||||
|
intakeForms: Record<string, TInboxForm> = {};
|
||||||
// services
|
// services
|
||||||
inboxIssueService;
|
inboxIssueService;
|
||||||
|
|
||||||
|
|
@ -103,6 +109,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
||||||
inboxIssuePaginationInfo: observable,
|
inboxIssuePaginationInfo: observable,
|
||||||
inboxIssues: observable,
|
inboxIssues: observable,
|
||||||
inboxIssueIds: observable,
|
inboxIssueIds: observable,
|
||||||
|
intakeForms: observable,
|
||||||
// computed
|
// computed
|
||||||
inboxFilters: computed,
|
inboxFilters: computed,
|
||||||
inboxSorting: 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
|
* @description fetch intake issues with paginated data
|
||||||
* @param workspaceSlug
|
* @param workspaceSlug
|
||||||
|
|
|
||||||
1
web/ee/components/projects/settings/intake/index.ts
Normal file
1
web/ee/components/projects/settings/intake/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "ce/components/projects/settings/intake";
|
||||||
Loading…
Add table
Add a link
Reference in a new issue