Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/types/src/view-props.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,6 @@ export interface IssuePaginationOptions {
before?: string;
after?: string;
groupedBy?: TIssueGroupByOptions;
subGroupedBy?: TIssueGroupByOptions;
orderBy?: TIssueOrderByOptions;
}
12 changes: 12 additions & 0 deletions packages/types/src/views.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,21 @@ export interface IProjectView {
workspace: string;
logo_props: TLogoProps | undefined;
is_locked: boolean;
anchor?: string;
owned_by: string;
}

export type TPublishViewSettings = {
is_comments_enabled: boolean;
is_reactions_enabled: boolean;
is_votes_enabled: boolean;
};

export type TPublishViewDetails = TPublishViewSettings & {
id: string;
anchor: string;
};

export type TViewFiltersSortKey = "name" | "created_at" | "updated_at";

export type TViewFiltersSortBy = "asc" | "desc";
Expand Down
75 changes: 75 additions & 0 deletions space/app/views/[anchor]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use client";

import { observer } from "mobx-react";
import Image from "next/image";
import useSWR from "swr";
// components
import { LogoSpinner } from "@/components/common";
// hooks
import { usePublish, usePublishList } from "@/hooks/store";
// Plane web
import { ViewNavbarRoot } from "@/plane-web/components/navbar";
import { useView } from "@/plane-web/hooks/store";
// assets
import planeLogo from "@/public/plane-logo.svg";

type Props = {
children: React.ReactNode;
params: {
anchor: string;
};
};

const IssuesLayout = observer((props: Props) => {
const { children, params } = props;
// params
const { anchor } = params;
// store hooks
const { fetchPublishSettings } = usePublishList();
const { viewData, fetchViewDetails } = useView();
const publishSettings = usePublish(anchor);
// fetch publish settings
useSWR(
anchor ? `PUBLISH_SETTINGS_${anchor}` : null,
anchor
? async () => {
await fetchPublishSettings(anchor);
}
: null
);
// fetch view data
useSWR(
anchor ? `VIEW_DETAILS_${anchor}` : null,
anchor
? async () => {
await fetchViewDetails(anchor);
}
: null
);

if (!publishSettings || !viewData) return <LogoSpinner />;

return (
<div className="relative flex h-screen min-h-[500px] w-screen flex-col overflow-hidden">
<div className="relative flex h-[60px] flex-shrink-0 select-none items-center border-b border-custom-border-300 bg-custom-sidebar-background-100">
<ViewNavbarRoot publishSettings={publishSettings} />
</div>
<div className="relative h-full w-full overflow-hidden bg-custom-background-90">{children}</div>
<a
href="https://plane.so"
className="fixed bottom-2.5 right-5 !z-[999999] flex items-center gap-1 rounded border border-custom-border-200 bg-custom-background-100 px-2 py-1 shadow-custom-shadow-2xs"
target="_blank"
rel="noreferrer noopener"
>
<div className="relative grid h-6 w-6 place-items-center">
<Image src={planeLogo} alt="Plane logo" className="h-6 w-6" height="24" width="24" />
</div>
<div className="text-xs">
Powered by <span className="font-semibold">Plane Publish</span>
</div>
</a>
</div>
);
});

export default IssuesLayout;
30 changes: 30 additions & 0 deletions space/app/views/[anchor]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import { observer } from "mobx-react";
import { useSearchParams } from "next/navigation";
// hooks
import { usePublish } from "@/hooks/store";
// plane-web
import { ViewLayoutsRoot } from "@/plane-web/components/issue-layouts/root";

type Props = {
params: {
anchor: string;
};
};

const IssuesPage = observer((props: Props) => {
const { params } = props;
const { anchor } = params;
// params
const searchParams = useSearchParams();
const peekId = searchParams.get("peekId") || undefined;

const publishSettings = usePublish(anchor);

if (!publishSettings) return null;

return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
Comment on lines +16 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper handling of publish settings and improve readability.

The component fetches publish settings and renders the ViewLayoutsRoot component. Consider adding a loading state or error handling for better user experience. Also, destructure props and hooks for improved readability.

-const IssuesPage = observer((props: Props) => {
-  const { params } = props;
-  const { anchor } = params;
+const IssuesPage = observer(({ params: { anchor } }: Props) => {
+  const searchParams = useSearchParams();
+  const peekId = searchParams.get("peekId") || undefined;
+  const publishSettings = usePublish(anchor);
+
+  if (!publishSettings) return <div>Loading...</div>;
+
+  return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
});
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const IssuesPage = observer((props: Props) => {
const { params } = props;
const { anchor } = params;
// params
const searchParams = useSearchParams();
const peekId = searchParams.get("peekId") || undefined;
const publishSettings = usePublish(anchor);
if (!publishSettings) return null;
return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
const IssuesPage = observer(({ params: { anchor } }: Props) => {
const searchParams = useSearchParams();
const peekId = searchParams.get("peekId") || undefined;
const publishSettings = usePublish(anchor);
if (!publishSettings) return <div>Loading...</div>;
return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
});

});
Comment on lines +16 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve readability and error handling.

Consider destructuring props and handling the case where publishSettings is not available more gracefully.

const IssuesPage = observer(({ params: { anchor } }: Props) => {
  const searchParams = useSearchParams();
  const peekId = searchParams.get("peekId") || undefined;

  const publishSettings = usePublish(anchor);

  if (!publishSettings) {
    return <div>Loading...</div>; // or any other appropriate fallback UI
  }

  return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
});
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const IssuesPage = observer((props: Props) => {
const { params } = props;
const { anchor } = params;
// params
const searchParams = useSearchParams();
const peekId = searchParams.get("peekId") || undefined;
const publishSettings = usePublish(anchor);
if (!publishSettings) return null;
return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
});
const IssuesPage = observer(({ params: { anchor } }: Props) => {
const searchParams = useSearchParams();
const peekId = searchParams.get("peekId") || undefined;
const publishSettings = usePublish(anchor);
if (!publishSettings) {
return <div>Loading...</div>; // or any other appropriate fallback UI
}
return <ViewLayoutsRoot peekId={peekId} publishSettings={publishSettings} />;
});


export default IssuesPage;
10 changes: 10 additions & 0 deletions space/ce/components/issue-layouts/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PageNotFound } from "@/components/ui/not-found";
import { PublishStore } from "@/store/publish/publish.store";

type Props = {
peekId: string | undefined;
publishSettings: PublishStore;
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const ViewLayoutsRoot = (props: Props) => <PageNotFound />;
8 changes: 8 additions & 0 deletions space/ce/components/navbar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PublishStore } from "@/store/publish/publish.store";

type Props = {
publishSettings: PublishStore;
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const ViewNavbarRoot = (props: Props) => <></>;
1 change: 1 addition & 0 deletions space/ce/hooks/store/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./use-published-view";
5 changes: 5 additions & 0 deletions space/ce/hooks/store/use-published-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const useView = () => ({
// eslint-disable-next-line @typescript-eslint/no-unused-vars
fetchViewDetails: (anchor: string) => {},
viewData: {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialize viewData with meaningful defaults.

The viewData object is currently empty. Consider initializing it with meaningful default values or a structure to avoid potential issues when accessing its properties.

-  viewData: {},
+  viewData: {
+    // Add default properties here
+  },
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
viewData: {},
viewData: {
// Add default properties here
},

});
4 changes: 2 additions & 2 deletions space/core/components/issues/issue-layouts/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./kanban";
export * from "./list";
export * from "./kanban/base-kanban-root";
export * from "./list/base-list-root";
export * from "./properties";
export * from "./root";
33 changes: 33 additions & 0 deletions space/core/components/issues/issue-layouts/issue-layout-HOC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { observer } from "mobx-react";
import { TLoader } from "@plane/types";
import { LogoSpinner } from "@/components/common";

interface Props {
children: string | JSX.Element | JSX.Element[];
getGroupIssueCount: (
groupId: string | undefined,
subGroupId: string | undefined,
isSubGroupCumulative: boolean
) => number | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
}

export const IssueLayoutHOC = observer((props: Props) => {
const { getIssueLoader, getGroupIssueCount } = props;

const issueCount = getGroupIssueCount(undefined, undefined, false);

if (getIssueLoader() === "init-loader" || issueCount === undefined) {
return (
<div className="relative flex h-screen w-full items-center justify-center">
<LogoSpinner />
</div>
);
}

if (getGroupIssueCount(undefined, undefined, false) === 0) {
return <div className="flex w-full h-full items-center justify-center">No Issues Found</div>;
}

return <>{props.children}</>;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client";

import { useCallback, useMemo, useRef } from "react";
import debounce from "lodash/debounce";
import { observer } from "mobx-react";
// types
import { IIssueDisplayProperties } from "@plane/types";
// components
import { IssueLayoutHOC } from "@/components/issues/issue-layouts/issue-layout-HOC";
// hooks
import { useIssue } from "@/hooks/store";

import { KanBan } from "./default";

type Props = {
anchor: string;
};
export const IssueKanbanLayoutRoot: React.FC<Props> = observer((props: Props) => {
const { anchor } = props;
// store hooks
const { groupedIssueIds, getIssueLoader, fetchNextPublicIssues, getGroupIssueCount, getPaginationData } = useIssue();

const displayProperties: IIssueDisplayProperties = useMemo(
() => ({
key: true,
state: true,
labels: true,
priority: true,
due_date: true,
}),
[]
);

const fetchMoreIssues = useCallback(
(groupId?: string, subgroupId?: string) => {
if (getIssueLoader(groupId, subgroupId) !== "pagination") {
fetchNextPublicIssues(anchor, groupId, subgroupId);
}
},
[fetchNextPublicIssues]
);

const debouncedFetchMoreIssues = debounce(
(groupId?: string, subgroupId?: string) => fetchMoreIssues(groupId, subgroupId),
300,
{ leading: true, trailing: false }
);

const scrollableContainerRef = useRef<HTMLDivElement | null>(null);

return (
<IssueLayoutHOC getGroupIssueCount={getGroupIssueCount} getIssueLoader={getIssueLoader}>
<div
className={`horizontal-scrollbar scrollbar-lg relative flex h-full w-full bg-custom-background-90 overflow-x-auto overflow-y-hidden`}
ref={scrollableContainerRef}
>
<div className="relative h-full w-max min-w-full bg-custom-background-90">
<div className="h-full w-max">
<KanBan
groupedIssueIds={groupedIssueIds ?? {}}
displayProperties={displayProperties}
subGroupBy={null}
groupBy="state"
showEmptyGroup
scrollableContainerRef={scrollableContainerRef}
loadMoreIssues={debouncedFetchMoreIssues}
getGroupIssueCount={getGroupIssueCount}
getPaginationData={getPaginationData}
getIssueLoader={getIssueLoader}
/>
</div>
</div>
</div>
</IssueLayoutHOC>
);
});
Loading