Skip to content

Commit

Permalink
Merge branch 'preview' of github.com:makeplane/plane into chore-priva…
Browse files Browse the repository at this point in the history
…te-bucket
  • Loading branch information
pablohashescobar committed Oct 11, 2024
2 parents 4db53aa + 3011ef9 commit 2dda046
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 61 deletions.
2 changes: 2 additions & 0 deletions apiserver/plane/app/serializers/draft.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ class Meta:
"updated_at",
"created_by",
"updated_by",
"type_id",
"description_html",
]
read_only_fields = fields

Expand Down
21 changes: 12 additions & 9 deletions web/app/[workspaceSlug]/(projects)/drafts/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { PenSquare } from "lucide-react";
// ui
import { Breadcrumbs, Button, Header } from "@plane/ui";
// components
import { BreadcrumbLink } from "@/components/common";
import { BreadcrumbLink, CountChip } from "@/components/common";
import { CreateUpdateIssueModal } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
// hooks
import { useUserPermissions } from "@/hooks/store";
import { useUserPermissions, useWorkspaceDraftIssues } from "@/hooks/store";
// plane-web
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";

Expand All @@ -20,7 +20,7 @@ export const WorkspaceDraftHeader: FC = observer(() => {
const [isDraftIssueModalOpen, setIsDraftIssueModalOpen] = useState(false);
// store hooks
const { allowPermissions } = useUserPermissions();

const { paginationInfo } = useWorkspaceDraftIssues();
// check if user is authorized to create draft issue
const isAuthorizedUser = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
Expand All @@ -37,12 +37,15 @@ export const WorkspaceDraftHeader: FC = observer(() => {
/>
<Header>
<Header.LeftItem>
<Breadcrumbs>
<Breadcrumbs.BreadcrumbItem
type="text"
link={<BreadcrumbLink label={`Draft`} icon={<PenSquare className="h-4 w-4 text-custom-text-300" />} />}
/>
</Breadcrumbs>
<div className="flex items-center gap-2.5">
<Breadcrumbs>
<Breadcrumbs.BreadcrumbItem
type="text"
link={<BreadcrumbLink label={`Draft`} icon={<PenSquare className="h-4 w-4 text-custom-text-300" />} />}
/>
</Breadcrumbs>
{paginationInfo?.count && paginationInfo?.count > 0 ? <CountChip count={paginationInfo?.count} /> : <></>}
</div>
</Header.LeftItem>

<Header.RightItem>
Expand Down
17 changes: 4 additions & 13 deletions web/core/components/issues/issue-modal/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
const { handleCreateUpdatePropertyValues } = useIssueModal();
// pathname
const pathname = usePathname();
// local storage
const { storedValue: localStorageDraftIssues, setValue: setLocalStorageDraftIssue } = useLocalStorage<
Record<string, Partial<TIssue>>
>("draftedIssue", {});
// current store details
const { createIssue, updateIssue } = useIssuesActions(storeType);
// derived values
Expand Down Expand Up @@ -132,14 +128,9 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
setCreateMore(value);
};

const handleClose = (saveDraftIssueInLocalStorage?: boolean) => {
if (changesMade && saveDraftIssueInLocalStorage) {
// updating the current edited issue data in the local storage
let draftIssues = localStorageDraftIssues ? localStorageDraftIssues : {};
if (workspaceSlug) {
draftIssues = { ...draftIssues, [workspaceSlug.toString()]: changesMade };
setLocalStorageDraftIssue(draftIssues);
}
const handleClose = (saveAsDraft?: boolean) => {
if (changesMade && saveAsDraft && !data) {
handleCreateIssue(changesMade, true);
}

setActiveProjectId(null);
Expand Down Expand Up @@ -349,7 +340,7 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
module_ids: data?.module_ids ? data?.module_ids : moduleId ? [moduleId.toString()] : null,
}}
onAssetUpload={handleUpdateUploadedAssetIds}
onClose={() => handleClose(false)}
onClose={handleClose}
isCreateMoreToggleEnabled={createMore}
onCreateMoreToggleChange={handleCreateMoreToggleChange}
onSubmit={(payload) => handleFormSubmit(payload, isDraft)}
Expand Down
8 changes: 3 additions & 5 deletions web/core/components/issues/issue-modal/draft-issue-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ import { ConfirmIssueDiscard } from "@/components/issues";
import { isEmptyHtmlString } from "@/helpers/string.helper";
// hooks
import { useIssueModal } from "@/hooks/context/use-issue-modal";
import { useEventTracker } from "@/hooks/store";
// services
import workspaceDraftService from "@/services/issue/workspace_draft.service";
import { useEventTracker, useWorkspaceDraftIssues } from "@/hooks/store";
// local components
import { IssueFormRoot } from "./form";

Expand Down Expand Up @@ -57,6 +55,7 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
// store hooks
const { captureIssueEvent } = useEventTracker();
const { handleCreateUpdatePropertyValues } = useIssueModal();
const { createIssue } = useWorkspaceDraftIssues();

const handleClose = () => {
if (data?.id) {
Expand Down Expand Up @@ -98,8 +97,7 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
project_id: projectId,
};

const response = await workspaceDraftService
.createIssue(workspaceSlug.toString(), payload)
const response = await createIssue(workspaceSlug.toString(), payload)
.then((res) => {
setToast({
type: TOAST_TYPE.SUCCESS,
Expand Down
56 changes: 38 additions & 18 deletions web/core/components/issues/workspace-draft/root.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
"use client";

import { FC } from "react";
import { FC, Fragment } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
// components
import { EmptyState } from "@/components/empty-state";
// constants
import { EmptyStateType } from "@/constants/empty-state";
import { EDraftIssuePaginationType } from "@/constants/workspace-drafts";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useWorkspaceDraftIssues } from "@/hooks/store";
import { useCommandPalette, useProject, useWorkspaceDraftIssues } from "@/hooks/store";
// components
import { DraftIssueBlock } from "./draft-issue-block";
import { WorkspaceDraftEmptyState } from "./empty-state";
Expand All @@ -21,7 +24,9 @@ type TWorkspaceDraftIssuesRoot = {
export const WorkspaceDraftIssuesRoot: FC<TWorkspaceDraftIssuesRoot> = observer((props) => {
const { workspaceSlug } = props;
// hooks
const { loader, paginationInfo, fetchIssues, issuesMap, issueIds } = useWorkspaceDraftIssues();
const { loader, paginationInfo, fetchIssues, issueIds } = useWorkspaceDraftIssues();
const { workspaceProjectIds } = useProject();
const { toggleCreateProjectModal } = useCommandPalette();

// fetching issues
useSWR(
Expand All @@ -39,6 +44,17 @@ export const WorkspaceDraftIssuesRoot: FC<TWorkspaceDraftIssuesRoot> = observer(
return <WorkspaceDraftIssuesLoader items={14} />;
}

if (workspaceProjectIds?.length === 0)
return (
<EmptyState
type={EmptyStateType.WORKSPACE_NO_PROJECTS}
size="sm"
primaryButtonOnClick={() => {
toggleCreateProjectModal(true);
}}
/>
);

if (loader === "empty-state" && issueIds.length <= 0) return <WorkspaceDraftEmptyState />;

return (
Expand All @@ -48,22 +64,26 @@ export const WorkspaceDraftIssuesRoot: FC<TWorkspaceDraftIssuesRoot> = observer(
<DraftIssueBlock key={issueId} workspaceSlug={workspaceSlug} issueId={issueId} />
))}
</div>
{loader === "pagination" && issueIds.length >= 0 ? (
<WorkspaceDraftIssuesLoader items={1} />
) : (
<div
className={cn(
"h-11 pl-6 p-3 text-sm font-medium bg-custom-background-100 border-b border-custom-border-200 transition-all",
{
"text-custom-primary-100 hover:text-custom-primary-200 cursor-pointer underline-offset-2 hover:underline":
paginationInfo?.next_page_results,
"text-custom-text-300 cursor-not-allowed": !paginationInfo?.next_page_results,
}

{paginationInfo?.next_page_results && (
<Fragment>
{loader === "pagination" && issueIds.length >= 0 ? (
<WorkspaceDraftIssuesLoader items={1} />
) : (
<div
className={cn(
"h-11 pl-6 p-3 text-sm font-medium bg-custom-background-100 border-b border-custom-border-200 transition-all",
{
"text-custom-primary-100 hover:text-custom-primary-200 cursor-pointer underline-offset-2 hover:underline":
paginationInfo?.next_page_results,
}
)}
onClick={handleNextIssues}
>
Load More &darr;
</div>
)}
onClick={handleNextIssues}
>
Load More &darr;
</div>
</Fragment>
)}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion web/core/store/issue/root.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export class IssueRootStore implements IIssueRootStore {
this.profileIssues = new ProfileIssues(this, this.profileIssuesFilter);

this.workspaceDraftIssuesFilter = new WorkspaceDraftIssuesFilter(this);
this.workspaceDraftIssues = new WorkspaceDraftIssues();
this.workspaceDraftIssues = new WorkspaceDraftIssues(this);

this.projectIssuesFilter = new ProjectIssuesFilter(this);
this.projectIssues = new ProjectIssues(this, this.projectIssuesFilter);
Expand Down
51 changes: 36 additions & 15 deletions web/core/store/issue/workspace-draft/issue.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ import { EDraftIssuePaginationType } from "@/constants/workspace-drafts";
import { getCurrentDateTimeInISO, convertToISODateString } from "@/helpers/date-time.helper";
// services
import workspaceDraftService from "@/services/issue/workspace_draft.service";
// types
import { IIssueRootStore } from "../root.store";

export type TDraftIssuePaginationType = EDraftIssuePaginationType;

export interface IWorkspaceDraftIssues {
// observables
issuesMap: Record<string, TWorkspaceDraftIssue>;
paginationInfo: Omit<TWorkspaceDraftPaginationInfo<TWorkspaceDraftIssue>, "results"> | undefined;
loader: TWorkspaceDraftIssueLoader;
paginationInfo: Omit<TWorkspaceDraftPaginationInfo<TWorkspaceDraftIssue>, "results"> | undefined;
issuesMap: Record<string, TWorkspaceDraftIssue>; // issue_id -> issue;
issueMapIds: Record<string, string[]>; // workspace_id -> issue_ids;
// computed
issueIds: string[];
// computed functions
Expand Down Expand Up @@ -112,15 +115,17 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
// local constants
paginatedCount = 50;
// observables
paginationInfo: Omit<TWorkspaceDraftPaginationInfo<TWorkspaceDraftIssue>, "results"> | undefined = undefined;
loader: TWorkspaceDraftIssueLoader = undefined;
paginationInfo: Omit<TWorkspaceDraftPaginationInfo<TWorkspaceDraftIssue>, "results"> | undefined = undefined;
issuesMap: Record<string, TWorkspaceDraftIssue> = {};
issueMapIds: Record<string, string[]> = {};

constructor() {
constructor(public issueStore: IIssueRootStore) {
makeObservable(this, {
paginationInfo: observable,
loader: observable.ref,
paginationInfo: observable,
issuesMap: observable,
issueMapIds: observable,
// computed
issueIds: computed,
// action
Expand All @@ -136,10 +141,11 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {

// computed
get issueIds() {
if (Object.keys(this.issuesMap).length <= 0) return [];
return orderBy(Object.values(this.issuesMap), (issue) => convertToISODateString(issue["created_at"]), ["asc"]).map(
(issue) => issue?.id
);
const workspaceSlug = this.issueStore.workspaceSlug;
if (!workspaceSlug) return [];
if (!this.issueMapIds[workspaceSlug]) return [];
const issueIds = this.issueMapIds[workspaceSlug];
return orderBy(issueIds, (issueId) => convertToISODateString(this.issuesMap[issueId]?.created_at), ["desc"]);
}

// computed functions
Expand Down Expand Up @@ -216,7 +222,10 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
const { results, ...paginationInfo } = draftIssuesResponse;
runInAction(() => {
if (results && results.length > 0) {
this.addIssue(results as TWorkspaceDraftIssue[]);
// adding issueIds
const issueIds = results.map((issue) => issue.id);
this.addIssue(results);
update(this.issueMapIds, [workspaceSlug], (existingIssueIds = []) => [...issueIds, ...existingIssueIds]);
this.loader = undefined;
} else {
this.loader = "empty-state";
Expand All @@ -240,7 +249,10 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {

const response = await workspaceDraftService.createIssue(workspaceSlug, payload);
if (response) {
runInAction(() => set(this.issuesMap, response.id, response));
runInAction(() => {
this.addIssue([response]);
update(this.issueMapIds, [workspaceSlug], (existingIssueIds = []) => [response.id, ...existingIssueIds]);
});
}

this.loader = undefined;
Expand All @@ -256,8 +268,11 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
try {
this.loader = "update";
runInAction(() => {
set(this.issuesMap, [issueId, "updated_at"], getCurrentDateTimeInISO());
set(this.issuesMap, [issueId], { ...issueBeforeUpdate, ...payload });
set(this.issuesMap, [issueId], {
...issueBeforeUpdate,
...payload,
...{ updated_at: getCurrentDateTimeInISO() },
});
});
const response = await workspaceDraftService.updateIssue(workspaceSlug, issueId, payload);
this.loader = undefined;
Expand All @@ -276,7 +291,10 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
this.loader = "delete";

const response = await workspaceDraftService.deleteIssue(workspaceSlug, issueId);
runInAction(() => unset(this.issuesMap, issueId));
runInAction(() => {
unset(this.issueMapIds[workspaceSlug], issueId);
unset(this.issuesMap, issueId);
});

this.loader = undefined;
return response;
Expand All @@ -291,7 +309,10 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
this.loader = "move";

const response = await workspaceDraftService.moveIssue(workspaceSlug, issueId, payload);
runInAction(() => unset(this.issuesMap, issueId));
runInAction(() => {
unset(this.issueMapIds[workspaceSlug], issueId);
unset(this.issuesMap, issueId);
});

this.loader = undefined;
return response;
Expand Down
Binary file modified web/public/empty-state/workspace-draft/issue-dark.webp
Binary file not shown.
Binary file modified web/public/empty-state/workspace-draft/issue-light.webp
Binary file not shown.

0 comments on commit 2dda046

Please sign in to comment.