From 761c65830c3f8152dd3cf9f231ca8b18021edc47 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 4 Jun 2024 21:00:19 +0530 Subject: [PATCH 1/7] refactor: publish project store and components --- .../project/publish-project/modal.tsx | 96 +++++++------- .../project/project-publish.service.ts | 2 +- web/store/project/project-publish.store.ts | 125 ++++++++---------- 3 files changed, 107 insertions(+), 116 deletions(-) diff --git a/web/components/project/publish-project/modal.tsx b/web/components/project/publish-project/modal.tsx index dc2c0f537..3469ca60a 100644 --- a/web/components/project/publish-project/modal.tsx +++ b/web/components/project/publish-project/modal.tsx @@ -25,6 +25,7 @@ type Props = { }; type FormData = { + anchor: string; id: string | null; comments: boolean; reactions: boolean; @@ -34,6 +35,7 @@ type FormData = { }; const defaultValues: FormData = { + anchor: "", id: null, comments: false, reactions: false, @@ -48,34 +50,27 @@ const viewOptions: { }[] = [ { key: "list", label: "List" }, { key: "kanban", label: "Kanban" }, - // { key: "calendar", label: "Calendar" }, - // { key: "gantt", label: "Gantt" }, - // { key: "spreadsheet", label: "Spreadsheet" }, ]; export const PublishProjectModal: React.FC = observer((props) => { const { isOpen, project, onClose } = props; - // hooks - // const { instance } = useInstance(); // states const [isUnPublishing, setIsUnPublishing] = useState(false); const [isUpdateRequired, setIsUpdateRequired] = useState(false); - - // const plane_deploy_url = instance?.config?.space_base_url || ""; - const SPACE_URL = (SPACE_BASE_URL === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH; - // router const router = useRouter(); const { workspaceSlug } = router.query; // store hooks const { - projectPublishSettings, - getProjectSettingsAsync, + fetchPublishSettings, + getPublishSettingsByProjectID, publishProject, - updateProjectSettingsAsync, + updatePublishSettings, unPublishProject, fetchSettingsLoader, } = useProjectPublish(); + // derived values + const projectPublishSettings = getPublishSettingsByProjectID(project.id); // form info const { control, @@ -97,44 +92,44 @@ export const PublishProjectModal: React.FC = observer((props) => { // prefill form with the saved settings if the project is already published useEffect(() => { - if (projectPublishSettings && projectPublishSettings !== "not-initialized") { - let userBoards: TProjectPublishViews[] = []; + if (!projectPublishSettings) return; - if (projectPublishSettings?.views) { - const savedViews = projectPublishSettings?.views; + let userBoards: TProjectPublishViews[] = []; - if (!savedViews) return; + if (projectPublishSettings?.view_props) { + const savedViews = projectPublishSettings?.view_props; - if (savedViews.list) userBoards.push("list"); - if (savedViews.kanban) userBoards.push("kanban"); - if (savedViews.calendar) userBoards.push("calendar"); - if (savedViews.gantt) userBoards.push("gantt"); - if (savedViews.spreadsheet) userBoards.push("spreadsheet"); + if (!savedViews) return; - userBoards = userBoards && userBoards.length > 0 ? userBoards : ["list"]; - } + if (savedViews.list) userBoards.push("list"); + if (savedViews.kanban) userBoards.push("kanban"); + if (savedViews.calendar) userBoards.push("calendar"); + if (savedViews.gantt) userBoards.push("gantt"); + if (savedViews.spreadsheet) userBoards.push("spreadsheet"); - const updatedData = { - id: projectPublishSettings?.id || null, - comments: projectPublishSettings?.comments || false, - reactions: projectPublishSettings?.reactions || false, - votes: projectPublishSettings?.votes || false, - inbox: projectPublishSettings?.inbox || null, - views: userBoards, - }; - - reset({ ...updatedData }); + userBoards = userBoards && userBoards.length > 0 ? userBoards : ["list"]; } + + const updatedData = { + id: projectPublishSettings?.id || null, + comments: projectPublishSettings?.comments || false, + reactions: projectPublishSettings?.reactions || false, + votes: projectPublishSettings?.votes || false, + inbox: projectPublishSettings?.inbox || null, + views: userBoards, + }; + + reset({ ...updatedData }); }, [reset, projectPublishSettings, isOpen]); // fetch publish settings useEffect(() => { if (!workspaceSlug || !isOpen) return; - if (projectPublishSettings === "not-initialized") { - getProjectSettingsAsync(workspaceSlug.toString(), project.id); + if (!projectPublishSettings) { + fetchPublishSettings(workspaceSlug.toString(), project.id); } - }, [isOpen, workspaceSlug, project, projectPublishSettings, getProjectSettingsAsync]); + }, [fetchPublishSettings, isOpen, project, projectPublishSettings, workspaceSlug]); const handlePublishProject = async (payload: IProjectPublishSettings) => { if (!workspaceSlug) return; @@ -145,7 +140,7 @@ export const PublishProjectModal: React.FC = observer((props) => { const handleUpdatePublishSettings = async (payload: IProjectPublishSettings) => { if (!workspaceSlug) return; - await updateProjectSettingsAsync(workspaceSlug.toString(), project.id, payload.id ?? "", payload) + await updatePublishSettings(workspaceSlug.toString(), project.id, payload.id ?? "", payload) .then((res) => { setToast({ type: TOAST_TYPE.SUCCESS, @@ -172,7 +167,7 @@ export const PublishProjectModal: React.FC = observer((props) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", - message: "Something went wrong while un-publishing the project.", + message: "Something went wrong while unpublishing the project.", }) ) .finally(() => setIsUnPublishing(false)); @@ -214,7 +209,7 @@ export const PublishProjectModal: React.FC = observer((props) => { reactions: formData.reactions, votes: formData.votes, inbox: formData.inbox, - views: { + view_props: { list: formData.views.includes("list"), kanban: formData.views.includes("kanban"), calendar: formData.views.includes("calendar"), @@ -223,13 +218,18 @@ export const PublishProjectModal: React.FC = observer((props) => { }, }; - if (project.is_deployed) await handleUpdatePublishSettings({ id: watch("id") ?? "", ...payload }); + if (project.is_deployed) + await handleUpdatePublishSettings({ + anchor: watch("anchor") ?? "", + id: watch("id") ?? "", + ...payload, + }); else await handlePublishProject(payload); }; // check if an update is required or not const checkIfUpdateIsRequired = () => { - if (!projectPublishSettings || projectPublishSettings === "not-initialized") return; + if (!projectPublishSettings || !projectPublishSettings) return; const currentSettings = projectPublishSettings; const newSettings = getValues(); @@ -245,7 +245,7 @@ export const PublishProjectModal: React.FC = observer((props) => { let viewCheckFlag = 0; viewOptions.forEach((option) => { - if (currentSettings.views[option.key] !== newSettings.views.includes(option.key)) viewCheckFlag++; + if (currentSettings.view_props?.[option.key] !== newSettings.views.includes(option.key)) viewCheckFlag++; }); if (viewCheckFlag !== 0) { @@ -256,6 +256,8 @@ export const PublishProjectModal: React.FC = observer((props) => { setIsUpdateRequired(false); }; + const SPACE_URL = (SPACE_BASE_URL === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH; + return ( @@ -293,7 +295,7 @@ export const PublishProjectModal: React.FC = observer((props) => { onClick={() => handleUnPublishProject(watch("id") ?? "")} loading={isUnPublishing} > - {isUnPublishing ? "Un-publishing..." : "Un-publish"} + {isUnPublishing ? "Unpublishing" : "Unpublish"} )} @@ -308,12 +310,12 @@ export const PublishProjectModal: React.FC = observer((props) => { ) : (
- {project.is_deployed && ( + {project.is_deployed && projectPublishSettings && ( <>
-
{`${SPACE_URL}/issues/`}
+
{`${SPACE_URL}/issues/${projectPublishSettings.anchor}`}
- +
diff --git a/web/services/project/project-publish.service.ts b/web/services/project/project-publish.service.ts index 677b37553..59c7ed089 100644 --- a/web/services/project/project-publish.service.ts +++ b/web/services/project/project-publish.service.ts @@ -34,7 +34,7 @@ export class ProjectPublishService extends APIService { projectID: string, project_publish_id: string, data: IProjectPublishSettings - ): Promise { + ): Promise { return this.patch( `/api/workspaces/${workspaceSlug}/projects/${projectID}/project-deploy-boards/${project_publish_id}/`, data diff --git a/web/store/project/project-publish.store.ts b/web/store/project/project-publish.store.ts index 4bcdf12f9..33b695a69 100644 --- a/web/store/project/project-publish.store.ts +++ b/web/store/project/project-publish.store.ts @@ -1,4 +1,5 @@ import set from "lodash/set"; +import unset from "lodash/unset"; import { observable, action, makeObservable, runInAction } from "mobx"; // types import { ProjectPublishService } from "@/services/project"; @@ -12,12 +13,13 @@ export type TProjectPublishViewsSettings = { }; export interface IProjectPublishSettings { + anchor?: string; id?: string; project?: string; comments: boolean; reactions: boolean; votes: boolean; - views: TProjectPublishViewsSettings; + view_props: TProjectPublishViewsSettings; inbox: string | null; } @@ -26,31 +28,31 @@ export interface IProjectPublishStore { generalLoader: boolean; fetchSettingsLoader: boolean; // observables - projectPublishSettings: IProjectPublishSettings | "not-initialized"; - // project settings actions - getProjectSettingsAsync: (workspaceSlug: string, projectId: string) => Promise; - updateProjectSettingsAsync: ( + publishSettingsMap: Record; // projectID => IProjectPublishSettings + // helpers + getPublishSettingsByProjectID: (projectID: string) => IProjectPublishSettings | undefined; + // actions + fetchPublishSettings: (workspaceSlug: string, projectID: string) => Promise; + updatePublishSettings: ( workspaceSlug: string, - projectId: string, + projectID: string, projectPublishId: string, data: IProjectPublishSettings - ) => Promise; - // project publish actions + ) => Promise; publishProject: ( workspaceSlug: string, - projectId: string, + projectID: string, data: IProjectPublishSettings ) => Promise; - unPublishProject: (workspaceSlug: string, projectId: string, projectPublishId: string) => Promise; + unPublishProject: (workspaceSlug: string, projectID: string, projectPublishId: string) => Promise; } export class ProjectPublishStore implements IProjectPublishStore { // states generalLoader: boolean = false; fetchSettingsLoader: boolean = false; - // actions - project_id: string | null = null; - projectPublishSettings: IProjectPublishSettings | "not-initialized" = "not-initialized"; + // observables + publishSettingsMap: Record = {}; // root store projectRootStore: ProjectRootStore; // services @@ -62,12 +64,10 @@ export class ProjectPublishStore implements IProjectPublishStore { generalLoader: observable.ref, fetchSettingsLoader: observable.ref, // observables - project_id: observable.ref, - projectPublishSettings: observable.ref, - // project settings actions - getProjectSettingsAsync: action, - updateProjectSettingsAsync: action, - // project publish actions + publishSettingsMap: observable, + // actions + fetchPublishSettings: action, + updatePublishSettings: action, publishProject: action, unPublishProject: action, }); @@ -77,29 +77,31 @@ export class ProjectPublishStore implements IProjectPublishStore { this.projectPublishService = new ProjectPublishService(); } + /** + * @description returns the publish settings of a particular project + * @param {string} projectID + * @returns {IProjectPublishSettings | undefined} + */ + getPublishSettingsByProjectID = (projectID: string): IProjectPublishSettings | undefined => + this.publishSettingsMap?.[projectID] ?? undefined; + /** * Fetches project publish settings * @param workspaceSlug - * @param projectId + * @param projectID * @returns */ - getProjectSettingsAsync = async (workspaceSlug: string, projectId: string) => { + fetchPublishSettings = async (workspaceSlug: string, projectID: string) => { try { runInAction(() => { this.fetchSettingsLoader = true; }); - const response = await this.projectPublishService.getProjectSettingsAsync(workspaceSlug, projectId); - if (response) { - runInAction(() => { - this.projectPublishSettings = response; - this.fetchSettingsLoader = false; - }); - } else { - runInAction(() => { - this.projectPublishSettings = "not-initialized"; - this.fetchSettingsLoader = false; - }); - } + const response = await this.projectPublishService.getProjectSettingsAsync(workspaceSlug, projectID); + + runInAction(() => { + set(this.publishSettingsMap, [projectID], response); + this.fetchSettingsLoader = false; + }); return response; } catch (error) { runInAction(() => { @@ -112,23 +114,21 @@ export class ProjectPublishStore implements IProjectPublishStore { /** * Publishes project and updates project publish status in the store * @param workspaceSlug - * @param projectId + * @param projectID * @param data * @returns */ - publishProject = async (workspaceSlug: string, projectId: string, data: IProjectPublishSettings) => { + publishProject = async (workspaceSlug: string, projectID: string, data: IProjectPublishSettings) => { try { runInAction(() => { this.generalLoader = true; }); - const response = await this.projectPublishService.createProjectSettingsAsync(workspaceSlug, projectId, data); - if (response) { - runInAction(() => { - this.projectPublishSettings = response; - set(this.projectRootStore.project.projectMap, [projectId, "is_deployed"], true); - this.generalLoader = false; - }); - } + const response = await this.projectPublishService.createProjectSettingsAsync(workspaceSlug, projectID, data); + runInAction(() => { + set(this.publishSettingsMap, [projectID], response); + set(this.projectRootStore.project.projectMap, [projectID, "is_deployed"], true); + this.generalLoader = false; + }); return response; } catch (error) { runInAction(() => { @@ -141,14 +141,14 @@ export class ProjectPublishStore implements IProjectPublishStore { /** * Updates project publish settings * @param workspaceSlug - * @param projectId + * @param projectID * @param projectPublishId * @param data * @returns */ - updateProjectSettingsAsync = async ( + updatePublishSettings = async ( workspaceSlug: string, - projectId: string, + projectID: string, projectPublishId: string, data: IProjectPublishSettings ) => { @@ -158,26 +158,15 @@ export class ProjectPublishStore implements IProjectPublishStore { }); const response = await this.projectPublishService.updateProjectSettingsAsync( workspaceSlug, - projectId, + projectID, projectPublishId, data ); - if (response) { - const _projectPublishSettings: IProjectPublishSettings = { - id: response?.id || null, - comments: response?.comments || false, - reactions: response?.reactions || false, - votes: response?.votes || false, - views: { ...response?.views }, - inbox: response?.inbox || null, - project: response?.project || null, - }; - runInAction(() => { - this.projectPublishSettings = _projectPublishSettings; - this.generalLoader = false; - }); - return response; - } + runInAction(() => { + set(this.publishSettingsMap, [projectID], response); + this.generalLoader = false; + }); + return response; } catch (error) { runInAction(() => { this.generalLoader = false; @@ -189,23 +178,23 @@ export class ProjectPublishStore implements IProjectPublishStore { /** * Unpublishes project and updates project publish status in the store * @param workspaceSlug - * @param projectId + * @param projectID * @param projectPublishId * @returns */ - unPublishProject = async (workspaceSlug: string, projectId: string, projectPublishId: string) => { + unPublishProject = async (workspaceSlug: string, projectID: string, projectPublishId: string) => { try { runInAction(() => { this.generalLoader = true; }); const response = await this.projectPublishService.deleteProjectSettingsAsync( workspaceSlug, - projectId, + projectID, projectPublishId ); runInAction(() => { - this.projectPublishSettings = "not-initialized"; - set(this.projectRootStore.project.projectMap, [projectId, "is_deployed"], false); + unset(this.publishSettingsMap, [projectID]); + set(this.projectRootStore.project.projectMap, [projectID, "is_deployed"], false); this.generalLoader = false; }); return response; From 2921f230bffb514b098e862b21912036c4b7b5a4 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 4 Jun 2024 21:07:49 +0530 Subject: [PATCH 2/7] fix: update layout options after fetching settings --- .../issues/navbar/issue-board-view.tsx | 47 +++++++++---------- space/store/publish/publish_list.store.ts | 10 ++-- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/space/components/issues/navbar/issue-board-view.tsx b/space/components/issues/navbar/issue-board-view.tsx index 2bb8b671c..d8ed5ef07 100644 --- a/space/components/issues/navbar/issue-board-view.tsx +++ b/space/components/issues/navbar/issue-board-view.tsx @@ -40,32 +40,31 @@ export const NavbarIssueBoardView: FC = observer((pro return ( <> - {issueLayoutViews && - Object.keys(issueLayoutViews).map((key: string) => { - const layoutKey = key as TIssueLayout; - if (layoutOptions[layoutKey]) { - return ( -
{ + const layoutKey = key as TIssueLayout; + if (layoutOptions[layoutKey]) { + return ( +
handleCurrentBoardView(layoutKey)} + title={layoutKey} + > + handleCurrentBoardView(layoutKey)} - title={layoutKey} > - - {issueLayoutViews[layoutKey]?.icon} - -
- ); - } - })} + {issueLayoutViews[layoutKey]?.icon} + +
+ ); + } + })} ); }); diff --git a/space/store/publish/publish_list.store.ts b/space/store/publish/publish_list.store.ts index 680adb809..91db19f7a 100644 --- a/space/store/publish/publish_list.store.ts +++ b/space/store/publish/publish_list.store.ts @@ -38,13 +38,15 @@ export class PublishListStore implements IPublishListStore { */ fetchPublishSettings = async (anchor: string) => { try { - const publishSettings = await this.publishService.fetchPublishSettings(anchor); + const response = await this.publishService.fetchPublishSettings(anchor); runInAction(() => { - if (publishSettings.anchor) - set(this.publishMap, [publishSettings.anchor], new PublishStore(this.store, publishSettings)); + if (response.anchor && response.view_props) { + this.store.issueFilter.updateLayoutOptions(response?.view_props); + set(this.publishMap, [response.anchor], new PublishStore(this.store, response)); + } }); - return publishSettings; + return response; } catch (error) { throw error; } From 0aa02a603df431bf2ffacd843df30e4f71a177e0 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 4 Jun 2024 21:43:36 +0530 Subject: [PATCH 3/7] chore: remove unnecessary types --- .../issues/board-views/block-due-date.tsx | 69 ++++++------------- .../issues/board-views/block-priority.tsx | 6 +- .../issues/board-views/kanban/header.tsx | 12 ++-- .../issues/board-views/list/header.tsx | 9 ++- .../filters/applied-filters/filters-list.tsx | 6 +- .../issues/filters/applied-filters/root.tsx | 2 +- .../issues/filters/applied-filters/state.tsx | 7 +- space/components/issues/filters/selection.tsx | 5 +- space/components/issues/filters/state.tsx | 7 +- .../issues/peek-overview/issue-properties.tsx | 29 +++++--- space/constants/issue.ts | 8 +-- space/constants/state.ts | 37 ++++++++++ space/helpers/date-time.helper.ts | 54 ++++++++++----- space/helpers/issue.helper.ts | 30 ++++++++ space/package.json | 1 + space/store/issue.store.ts | 8 ++- space/store/publish/publish.store.ts | 6 +- space/types/issue.d.ts | 68 +++--------------- space/types/project.d.ts | 6 -- space/types/publish.d.ts | 5 +- yarn.lock | 43 +++--------- 21 files changed, 208 insertions(+), 210 deletions(-) create mode 100644 space/constants/state.ts create mode 100644 space/helpers/issue.helper.ts diff --git a/space/components/issues/board-views/block-due-date.tsx b/space/components/issues/board-views/block-due-date.tsx index ecf229562..3b73973e7 100644 --- a/space/components/issues/board-views/block-due-date.tsx +++ b/space/components/issues/board-views/block-due-date.tsx @@ -1,59 +1,32 @@ "use client"; +import { CalendarCheck2 } from "lucide-react"; +// types +import { TStateGroups } from "@plane/types"; // helpers -import { renderFullDate } from "@/helpers/date-time.helper"; +import { cn } from "@/helpers/common.helper"; +import { renderFormattedDate } from "@/helpers/date-time.helper"; +import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper"; -export const dueDateIconDetails = ( - date: string, - stateGroup: string -): { - iconName: string; - className: string; -} => { - let iconName = "calendar_today"; - let className = ""; - - if (!date || ["completed", "cancelled"].includes(stateGroup)) { - iconName = "calendar_today"; - className = ""; - } else { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const targetDate = new Date(date); - targetDate.setHours(0, 0, 0, 0); - - const timeDifference = targetDate.getTime() - today.getTime(); - - if (timeDifference < 0) { - iconName = "event_busy"; - className = "text-red-500"; - } else if (timeDifference === 0) { - iconName = "today"; - className = "text-red-500"; - } else if (timeDifference === 24 * 60 * 60 * 1000) { - iconName = "event"; - className = "text-yellow-500"; - } else { - iconName = "calendar_today"; - className = ""; - } - } - - return { - iconName, - className, - }; +type Props = { + due_date: string; + group: TStateGroups; }; -export const IssueBlockDueDate = ({ due_date, group }: { due_date: string; group: string }) => { - const iconDetails = dueDateIconDetails(due_date, group); +export const IssueBlockDueDate = (props: Props) => { + const { due_date, group } = props; return ( -
- - {iconDetails.iconName} - - {renderFullDate(due_date)} +
+ + {renderFormattedDate(due_date)}
); }; diff --git a/space/components/issues/board-views/block-priority.tsx b/space/components/issues/board-views/block-priority.tsx index 3110930ec..b91d56bb8 100644 --- a/space/components/issues/board-views/block-priority.tsx +++ b/space/components/issues/board-views/block-priority.tsx @@ -1,11 +1,11 @@ "use client"; // types -import { issuePriorityFilter } from "@/constants/issue"; -import { TIssueFilterPriority } from "@/types/issue"; +import { TIssuePriorities } from "@plane/types"; // constants +import { issuePriorityFilter } from "@/constants/issue"; -export const IssueBlockPriority = ({ priority }: { priority: TIssueFilterPriority | null }) => { +export const IssueBlockPriority = ({ priority }: { priority: TIssuePriorities | null }) => { const priority_detail = priority != null ? issuePriorityFilter(priority) : null; if (priority_detail === null) return <>; diff --git a/space/components/issues/board-views/kanban/header.tsx b/space/components/issues/board-views/kanban/header.tsx index baf5612b3..f40812f75 100644 --- a/space/components/issues/board-views/kanban/header.tsx +++ b/space/components/issues/board-views/kanban/header.tsx @@ -1,16 +1,14 @@ "use client"; -// mobx react lite + import { observer } from "mobx-react-lite"; +// types +import { IStateLite } from "@plane/types"; // ui import { StateGroupIcon } from "@plane/ui"; // constants import { issueGroupFilter } from "@/constants/issue"; -// mobx hook -// import { useIssue } from "@/hooks/store"; -// interfaces -import { IIssueState } from "@/types/issue"; -export const IssueKanBanHeader = observer(({ state }: { state: IIssueState }) => { +export const IssueKanBanHeader = observer(({ state }: { state: IStateLite }) => { // const { getCountOfIssuesByState } = useIssue(); const stateGroup = issueGroupFilter(state.group); @@ -21,7 +19,7 @@ export const IssueKanBanHeader = observer(({ state }: { state: IIssueState }) =>
-
{state?.name}
+
{state?.name}
{/* {getCountOfIssuesByState(state.id)} */}
); diff --git a/space/components/issues/board-views/list/header.tsx b/space/components/issues/board-views/list/header.tsx index 2f8f6c018..c7dd6e50d 100644 --- a/space/components/issues/board-views/list/header.tsx +++ b/space/components/issues/board-views/list/header.tsx @@ -1,15 +1,14 @@ "use client"; + import { observer } from "mobx-react-lite"; +// types +import { IStateLite } from "@plane/types"; // ui import { StateGroupIcon } from "@plane/ui"; // constants import { issueGroupFilter } from "@/constants/issue"; -// mobx hook -// import { useIssue } from "@/hooks/store"; -// types -import { IIssueState } from "@/types/issue"; -export const IssueListHeader = observer(({ state }: { state: IIssueState }) => { +export const IssueListHeader = observer(({ state }: { state: IStateLite }) => { // const { getCountOfIssuesByState } = useIssue(); const stateGroup = issueGroupFilter(state.group); // const count = getCountOfIssuesByState(state.id); diff --git a/space/components/issues/filters/applied-filters/filters-list.tsx b/space/components/issues/filters/applied-filters/filters-list.tsx index 83d651f5d..87089c500 100644 --- a/space/components/issues/filters/applied-filters/filters-list.tsx +++ b/space/components/issues/filters/applied-filters/filters-list.tsx @@ -1,10 +1,10 @@ "use client"; -// icons import { observer } from "mobx-react-lite"; import { X } from "lucide-react"; // types -import { IIssueLabel, IIssueState, TFilters } from "@/types/issue"; +import { IStateLite } from "@plane/types"; +import { IIssueLabel, TFilters } from "@/types/issue"; // components import { AppliedPriorityFilters } from "./priority"; import { AppliedStateFilters } from "./state"; @@ -14,7 +14,7 @@ type Props = { handleRemoveAllFilters: () => void; handleRemoveFilter: (key: keyof TFilters, value: string | null) => void; labels?: IIssueLabel[] | undefined; - states?: IIssueState[] | undefined; + states?: IStateLite[] | undefined; }; export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, " "); diff --git a/space/components/issues/filters/applied-filters/root.tsx b/space/components/issues/filters/applied-filters/root.tsx index 5d6a404ed..9b6625d75 100644 --- a/space/components/issues/filters/applied-filters/root.tsx +++ b/space/components/issues/filters/applied-filters/root.tsx @@ -80,7 +80,7 @@ export const IssueAppliedFilters: FC = observer((props) => if (Object.keys(appliedFilters).length === 0) return null; return ( -
+
void; - states: IIssueState[]; + states: IStateLite[]; values: string[]; }; diff --git a/space/components/issues/filters/selection.tsx b/space/components/issues/filters/selection.tsx index a1180b0ee..926fbf5b0 100644 --- a/space/components/issues/filters/selection.tsx +++ b/space/components/issues/filters/selection.tsx @@ -4,7 +4,8 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; import { Search, X } from "lucide-react"; // types -import { IIssueState, IIssueLabel, IIssueFilterOptions, TIssueFilterKeys } from "@/types/issue"; +import { IStateLite } from "@plane/types"; +import { IIssueLabel, IIssueFilterOptions, TIssueFilterKeys } from "@/types/issue"; // components import { FilterPriority, FilterState } from "./"; @@ -13,7 +14,7 @@ type Props = { handleFilters: (key: keyof IIssueFilterOptions, value: string | string[]) => void; layoutDisplayFiltersOptions: TIssueFilterKeys[]; labels?: IIssueLabel[] | undefined; - states?: IIssueState[] | undefined; + states?: IStateLite[] | undefined; }; export const FilterSelection: React.FC = observer((props) => { diff --git a/space/components/issues/filters/state.tsx b/space/components/issues/filters/state.tsx index 24b6bb5c8..f61237eef 100644 --- a/space/components/issues/filters/state.tsx +++ b/space/components/issues/filters/state.tsx @@ -1,17 +1,18 @@ "use client"; import React, { useState } from "react"; +// types +import { IStateLite } from "@plane/types"; +// ui import { Loader, StateGroupIcon } from "@plane/ui"; // components import { FilterHeader, FilterOption } from "@/components/issues/filters/helpers"; -// types -import { IIssueState } from "@/types/issue"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; searchQuery: string; - states: IIssueState[] | undefined; + states: IStateLite[] | undefined; }; export const FilterState: React.FC = (props) => { diff --git a/space/components/issues/peek-overview/issue-properties.tsx b/space/components/issues/peek-overview/issue-properties.tsx index 08d22b312..9ec754057 100644 --- a/space/components/issues/peek-overview/issue-properties.tsx +++ b/space/components/issues/peek-overview/issue-properties.tsx @@ -1,16 +1,17 @@ +import { CalendarCheck2 } from "lucide-react"; // ui import { StateGroupIcon, TOAST_TYPE, setToast } from "@plane/ui"; -// icons +// components import { Icon } from "@/components/ui"; // constants import { issueGroupFilter, issuePriorityFilter } from "@/constants/issue"; // helpers -import { renderFullDate } from "@/helpers/date-time.helper"; +import { cn } from "@/helpers/common.helper"; +import { renderFormattedDate } from "@/helpers/date-time.helper"; +import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper"; import { copyTextToClipboard, addSpaceIfCamelCase } from "@/helpers/string.helper"; // types import { IIssue, IPeekMode } from "@/types/issue"; -// components -import { dueDateIconDetails } from "../board-views/block-due-date"; type Props = { issueDetails: IIssue; @@ -23,8 +24,6 @@ export const PeekOverviewIssueProperties: React.FC = ({ issueDetails, mod const priority = issueDetails.priority ? issuePriorityFilter(issueDetails.priority) : null; - const dueDateIcon = dueDateIconDetails(issueDetails.target_date, state.group); - const handleCopyLink = () => { const urlToCopy = window.location.href; @@ -104,11 +103,19 @@ export const PeekOverviewIssueProperties: React.FC = ({ issueDetails, mod
{issueDetails.target_date ? ( -
- - {dueDateIcon.iconName} - - {renderFullDate(issueDetails.target_date)} +
+ + {renderFormattedDate(issueDetails.target_date)}
) : ( Empty diff --git a/space/constants/issue.ts b/space/constants/issue.ts index fb9c78fcd..f96eb3e0e 100644 --- a/space/constants/issue.ts +++ b/space/constants/issue.ts @@ -1,13 +1,13 @@ -// interfaces +// types +import { TIssuePriorities } from "@plane/types"; import { TIssueLayout, TIssueLayoutViews, TIssueFilterKeys, - TIssueFilterPriority, TIssueFilterPriorityObject, TIssueFilterState, TIssueFilterStateObject, -} from "types/issue"; +} from "@/types/issue"; // issue filters export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { [key in TIssueLayout]: Record<"filters", TIssueFilterKeys[]> } = { @@ -75,7 +75,7 @@ export const issuePriorityFilters: TIssueFilterPriorityObject[] = [ }, ]; -export const issuePriorityFilter = (priorityKey: TIssueFilterPriority): TIssueFilterPriorityObject | undefined => { +export const issuePriorityFilter = (priorityKey: TIssuePriorities): TIssueFilterPriorityObject | undefined => { const currentIssuePriority: TIssueFilterPriorityObject | undefined = issuePriorityFilters && issuePriorityFilters.length > 0 ? issuePriorityFilters.find((_priority) => _priority.key === priorityKey) diff --git a/space/constants/state.ts b/space/constants/state.ts new file mode 100644 index 000000000..b0fd622be --- /dev/null +++ b/space/constants/state.ts @@ -0,0 +1,37 @@ +import { TStateGroups } from "@plane/types"; + +export const STATE_GROUPS: { + [key in TStateGroups]: { + key: TStateGroups; + label: string; + color: string; + }; +} = { + backlog: { + key: "backlog", + label: "Backlog", + color: "#d9d9d9", + }, + unstarted: { + key: "unstarted", + label: "Unstarted", + color: "#3f76ff", + }, + started: { + key: "started", + label: "Started", + color: "#f59e0b", + }, + completed: { + key: "completed", + label: "Completed", + color: "#16a34a", + }, + cancelled: { + key: "cancelled", + label: "Canceled", + color: "#dc2626", + }, +}; + +export const ARCHIVABLE_STATE_GROUPS = [STATE_GROUPS.completed.key, STATE_GROUPS.cancelled.key]; diff --git a/space/helpers/date-time.helper.ts b/space/helpers/date-time.helper.ts index f19a5358b..3930bcb83 100644 --- a/space/helpers/date-time.helper.ts +++ b/space/helpers/date-time.helper.ts @@ -1,3 +1,6 @@ +import { format, isValid } from "date-fns"; +import isNumber from "lodash/isNumber"; + export const timeAgo = (time: any) => { switch (typeof time) { case "number": @@ -14,24 +17,43 @@ export const timeAgo = (time: any) => { }; /** - * @description Returns date and month, if date is of the current year - * @description Returns date, month adn year, if date is of a different year than current - * @param {string} date - * @example renderFullDate("2023-01-01") // 1 Jan - * @example renderFullDate("2021-01-01") // 1 Jan, 2021 + * This method returns a date from string of type yyyy-mm-dd + * This method is recommended to use instead of new Date() as this does not introduce any timezone offsets + * @param date + * @returns date or undefined */ +export const getDate = (date: string | Date | undefined | null): Date | undefined => { + try { + if (!date || date === "") return; -export const renderFullDate = (date: string): string => { - if (!date) return ""; + if (typeof date !== "string" && !(date instanceof String)) return date; - const months: string[] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + const [yearString, monthString, dayString] = date.substring(0, 10).split("-"); + const year = parseInt(yearString); + const month = parseInt(monthString); + const day = parseInt(dayString); + if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return; - const currentDate: Date = new Date(); - const [year, month, day]: number[] = date.split("-").map(Number); - - const formattedMonth: string = months[month - 1]; - const formattedDay: string = day < 10 ? `0${day}` : day.toString(); - - if (currentDate.getFullYear() === year) return `${formattedDay} ${formattedMonth}`; - else return `${formattedDay} ${formattedMonth}, ${year}`; + return new Date(year, month - 1, day); + } catch (e) { + return undefined; + } +}; + +/** + * @returns {string | null} formatted date in the format of MMM dd, yyyy + * @description Returns date in the formatted format + * @param {Date | string} date + * @example renderFormattedDate("2024-01-01") // Jan 01, 2024 + */ +export const renderFormattedDate = (date: string | Date | undefined | null): string | null => { + // Parse the date to check if it is valid + const parsedDate = getDate(date); + // return if undefined + if (!parsedDate) return null; + // Check if the parsed date is valid before formatting + if (!isValid(parsedDate)) return null; // Return null for invalid dates + // Format the date in format (MMM dd, yyyy) + const formattedDate = format(parsedDate, "MMM dd, yyyy"); + return formattedDate; }; diff --git a/space/helpers/issue.helper.ts b/space/helpers/issue.helper.ts new file mode 100644 index 000000000..a5159edef --- /dev/null +++ b/space/helpers/issue.helper.ts @@ -0,0 +1,30 @@ +import { differenceInCalendarDays } from "date-fns"; +// types +import { TStateGroups } from "@plane/types"; +// constants +import { STATE_GROUPS } from "@/constants/state"; +// helpers +import { getDate } from "@/helpers/date-time.helper"; + +/** + * @description check if the issue due date should be highlighted + * @param date + * @param stateGroup + * @returns boolean + */ +export const shouldHighlightIssueDueDate = ( + date: string | Date | null, + stateGroup: TStateGroups | undefined +): boolean => { + if (!date || !stateGroup) return false; + // if the issue is completed or cancelled, don't highlight the due date + if ([STATE_GROUPS.completed.key, STATE_GROUPS.cancelled.key].includes(stateGroup)) return false; + + const parsedDate = getDate(date); + if (!parsedDate) return false; + + const targetDateDistance = differenceInCalendarDays(parsedDate, new Date()); + + // if the issue is overdue, highlight the due date + return targetDateDistance <= 0; +}; diff --git a/space/package.json b/space/package.json index e3dadbff8..0932d7abf 100644 --- a/space/package.json +++ b/space/package.json @@ -26,6 +26,7 @@ "@sentry/nextjs": "^8", "axios": "^1.3.4", "clsx": "^2.0.0", + "date-fns": "^3.6.0", "dompurify": "^3.0.11", "dotenv": "^16.3.1", "js-cookie": "^3.0.1", diff --git a/space/store/issue.store.ts b/space/store/issue.store.ts index 4595c6a1d..4f2d845b5 100644 --- a/space/store/issue.store.ts +++ b/space/store/issue.store.ts @@ -1,9 +1,11 @@ import { observable, action, makeObservable, runInAction } from "mobx"; import { computedFn } from "mobx-utils"; +// types +import { IStateLite } from "@plane/types"; // services import IssueService from "@/services/issue.service"; // types -import { IIssue, IIssueState, IIssueLabel } from "@/types/issue"; +import { IIssue, IIssueLabel } from "@/types/issue"; // store import { RootStore } from "./root.store"; @@ -12,7 +14,7 @@ export interface IIssueStore { error: any; // observables issues: IIssue[]; - states: IIssueState[]; + states: IStateLite[]; labels: IIssueLabel[]; // filter observables filteredStates: string[]; @@ -29,7 +31,7 @@ export class IssueStore implements IIssueStore { loader: boolean = false; error: any | null = null; // observables - states: IIssueState[] = []; + states: IStateLite[] = []; labels: IIssueLabel[] = []; issues: IIssue[] = []; // filter observables diff --git a/space/store/publish/publish.store.ts b/space/store/publish/publish.store.ts index 066da6390..c90eb5bf9 100644 --- a/space/store/publish/publish.store.ts +++ b/space/store/publish/publish.store.ts @@ -1,8 +1,10 @@ import { observable, makeObservable, computed } from "mobx"; +// types +import { IWorkspaceLite } from "@plane/types"; // store types import { RootStore } from "@/store/root.store"; // types -import { TProjectDetails, TViewDetails, TWorkspaceDetails } from "@/types/project"; +import { TProjectDetails, TViewDetails } from "@/types/project"; import { TPublishEntityType, TPublishSettings } from "@/types/publish"; export interface IPublishStore extends TPublishSettings { @@ -31,7 +33,7 @@ export class PublishStore implements IPublishStore { view_props: TViewDetails | undefined; votes: boolean; workspace: string | undefined; - workspace_detail: TWorkspaceDetails | undefined; + workspace_detail: IWorkspaceLite | undefined; constructor( private store: RootStore, diff --git a/space/types/issue.d.ts b/space/types/issue.d.ts index 00bc740d5..3f2c41451 100644 --- a/space/types/issue.d.ts +++ b/space/types/issue.d.ts @@ -1,14 +1,19 @@ +import { IStateLite, IWorkspaceLite, TIssuePriorities } from "@plane/types"; + export type TIssueLayout = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt"; export type TIssueLayoutOptions = { [key in TIssueLayout]: boolean; }; export type TIssueLayoutViews = { - [key in TIssueLayout]: { title: string; icon: string; className: string }; + [key in TIssueLayout]: { + title: string; + icon: string; + className: string; + }; }; -export type TIssueFilterPriority = "urgent" | "high" | "medium" | "low" | "none"; export type TIssueFilterPriorityObject = { - key: TIssueFilterPriority; + key: TIssuePriorities; title: string; className: string; icon: string; @@ -30,7 +35,7 @@ export type TDisplayFilters = { export type TFilters = { state: TIssueFilterState[]; - priority: TIssueFilterPriority[]; + priority: TIssuePriorities[]; labels: string[]; }; @@ -44,7 +49,7 @@ export type TIssueQueryFilters = Partial; export type TIssueQueryFiltersParams = Partial>; export type TIssuesResponse = { - states: IIssueState[]; + states: IStateLite[]; labels: IIssueLabel[]; issues: IIssue[]; }; @@ -74,13 +79,6 @@ export interface IIssue { export type IPeekMode = "side" | "modal" | "full"; -export interface IIssueState { - id: string; - name: string; - group: TIssueGroupKey; - color: string; -} - export interface IIssueLabel { id: string; name: string; @@ -121,7 +119,7 @@ export interface Comment { updated_at: Date; updated_by: string; workspace: string; - workspace_detail: WorkspaceDetail; + workspace_detail: IWorkspaceLite; } export interface IIssueReaction { @@ -182,52 +180,8 @@ export interface ProjectDetail { description: string; } -export interface WorkspaceDetail { - name: string; - slug: string; - id: string; -} - -export interface IssueDetailType { - [issueId: string]: { - issue: IIssue; - comments: Comment[]; - reactions: any[]; - votes: any[]; - }; -} - -export type TIssueGroupByOptions = "state" | "priority" | "labels" | null; - -export type TIssueParams = "priority" | "state" | "labels"; - export interface IIssueFilterOptions { state?: string[] | null; labels?: string[] | null; priority?: string[] | null; } - -// issues -export interface IGroupedIssues { - [group_id: string]: string[]; -} - -export interface ISubGroupedIssues { - [sub_grouped_id: string]: { - [group_id: string]: string[]; - }; -} - -export type TUnGroupedIssues = string[]; - -export interface IIssueResponse { - [issue_id: string]: IIssue; -} - -export type TLoader = "init-loader" | "mutation" | undefined; - -export interface ViewFlags { - enableQuickAdd: boolean; - enableIssueCreation: boolean; - enableInlineEditing: boolean; -} diff --git a/space/types/project.d.ts b/space/types/project.d.ts index 3ef979c05..c0ae02583 100644 --- a/space/types/project.d.ts +++ b/space/types/project.d.ts @@ -1,11 +1,5 @@ import { TLogoProps } from "@plane/types"; -export type TWorkspaceDetails = { - name: string; - slug: string; - id: string; -}; - export type TViewDetails = { list: boolean; gantt: boolean; diff --git a/space/types/publish.d.ts b/space/types/publish.d.ts index d9c1387c4..726412978 100644 --- a/space/types/publish.d.ts +++ b/space/types/publish.d.ts @@ -1,4 +1,5 @@ -import { TProjectDetails, TViewDetails, TWorkspaceDetails } from "./project"; +import { IWorkspaceLite } from "@plane/types"; +import { TProjectDetails, TViewDetails } from "./project"; export type TPublishEntityType = "project"; @@ -19,5 +20,5 @@ export type TPublishSettings = { view_props: TViewDetails | undefined; votes: boolean; workspace: string | undefined; - workspace_detail: TWorkspaceDetails | undefined; + workspace_detail: IWorkspaceLite | undefined; }; diff --git a/yarn.lock b/yarn.lock index 05aec74bf..a2f58cf6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6281,6 +6281,11 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" +date-fns@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" + integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -10681,7 +10686,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -"prettier-fallback@npm:prettier@^3": +"prettier-fallback@npm:prettier@^3", prettier@^3.1.1, prettier@^3.2.5, prettier@latest: version "3.3.0" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.0.tgz#d173ea0524a691d4c0b1181752f2b46724328cdf" integrity sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g== @@ -10708,11 +10713,6 @@ prettier@^2.8.8: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -prettier@^3.1.1, prettier@^3.2.5, prettier@latest: - version "3.3.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.0.tgz#d173ea0524a691d4c0b1181752f2b46724328cdf" - integrity sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g== - pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -12112,16 +12112,7 @@ string-argv@~0.3.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12217,14 +12208,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -13648,16 +13632,7 @@ workbox-window@6.6.1, workbox-window@^6.5.4: "@types/trusted-types" "^2.0.2" workbox-core "6.6.1" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 899d49b1f010fdecd458cf3962b0cb5f3e5a5e09 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 4 Jun 2024 22:13:05 +0530 Subject: [PATCH 4/7] style: peek overview --- .../issues/peek-overview/header.tsx | 46 ++++++++------ .../issues/peek-overview/issue-details.tsx | 6 +- .../issues/peek-overview/issue-properties.tsx | 61 ++++++++----------- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/space/components/issues/peek-overview/header.tsx b/space/components/issues/peek-overview/header.tsx index 0e9b93ab9..3ad07e06b 100644 --- a/space/components/issues/peek-overview/header.tsx +++ b/space/components/issues/peek-overview/header.tsx @@ -1,10 +1,9 @@ import React from "react"; import { observer } from "mobx-react-lite"; -import { MoveRight } from "lucide-react"; +import { Link2, MoveRight } from "lucide-react"; import { Listbox, Transition } from "@headlessui/react"; // ui -import { setToast, TOAST_TYPE } from "@plane/ui"; -import { Icon } from "@/components/ui"; +import { CenterPanelIcon, FullScreenPanelIcon, setToast, SidePanelIcon, TOAST_TYPE } from "@plane/ui"; // helpers import { copyTextToClipboard } from "@/helpers/string.helper"; // hooks @@ -18,21 +17,21 @@ type Props = { issueDetails: IIssue | undefined; }; -const peekModes: { +const PEEK_MODES: { key: IPeekMode; - icon: string; + icon: any; label: string; }[] = [ - { key: "side", icon: "side_navigation", label: "Side Peek" }, + { key: "side", icon: SidePanelIcon, label: "Side Peek" }, { key: "modal", - icon: "dialogs", - label: "Modal Peek", + icon: CenterPanelIcon, + label: "Modal", }, { key: "full", - icon: "nearby", - label: "Full Screen Peek", + icon: FullScreenPanelIcon, + label: "Full Screen", }, ]; @@ -47,20 +46,22 @@ export const PeekOverviewHeader: React.FC = observer((props) => { copyTextToClipboard(urlToCopy).then(() => { setToast({ - type: TOAST_TYPE.INFO, + type: TOAST_TYPE.SUCCESS, title: "Link copied!", - message: "Issue link copied to clipboard", + message: "Issue link copied to clipboard.", }); }); }; + const Icon = PEEK_MODES.find((m) => m.key === peekMode)?.icon ?? SidePanelIcon; + return ( <>
{peekMode === "side" && ( - )} = observer((props) => { onChange={(val) => setPeekMode(val)} className="relative flex-shrink-0 text-left" > - - m.key === peekMode)?.icon ?? ""} className="text-[1rem]" /> + + = observer((props) => { >
- {peekModes.map((mode) => ( + {PEEK_MODES.map((mode) => ( = observer((props) => {
{isClipboardWriteAllowed && (peekMode === "side" || peekMode === "modal") && (
-
)} diff --git a/space/components/issues/peek-overview/issue-details.tsx b/space/components/issues/peek-overview/issue-details.tsx index 4dc8d84a6..97a659554 100644 --- a/space/components/issues/peek-overview/issue-details.tsx +++ b/space/components/issues/peek-overview/issue-details.tsx @@ -16,10 +16,10 @@ export const PeekOverviewIssueDetails: React.FC = (props) => { return (
-
- {issueDetails.project_detail.identifier}-{issueDetails.sequence_id} +
+ {issueDetails.project_detail?.identifier}-{issueDetails?.sequence_id}
-

{issueDetails.name}

+

{issueDetails.name}

{description !== "" && description !== "

" && ( = ({ issueDetails, mode }) => { const state = issueDetails.state_detail; - const stateGroup = issueGroupFilter(state.group); const priority = issueDetails.priority ? issuePriorityFilter(issueDetails.priority) : null; @@ -50,28 +49,22 @@ export const PeekOverviewIssueProperties: React.FC = ({ issueDetails, mod
)} -
-
-
- - State +
+
+
+ + State
-
- {stateGroup && ( -
-
- - {addSpaceIfCamelCase(state?.name ?? "")} -
-
- )} +
+ + {addSpaceIfCamelCase(state?.name ?? "")}
-
-
- - Priority +
+
+ + Priority
= ({ issueDetails, mod
-
-
- - Due date + +
+
+ + Due date
{issueDetails.target_date ? (
{renderFormattedDate(issueDetails.target_date)} From 83a1b0cf1fb2f99bcfba068764185aad87dcab9d Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Wed, 5 Jun 2024 00:03:58 +0530 Subject: [PATCH 5/7] refactor: components folder structure --- .../[workspaceSlug]/[projectId]/layout.tsx | 4 +- .../app/[workspaceSlug]/[projectId]/page.tsx | 8 +- space/app/error.tsx | 53 ++++--- space/app/issues/[anchor]/layout.tsx | 8 +- space/app/issues/[anchor]/page.tsx | 8 +- space/components/account/user-logged-in.tsx | 4 +- space/components/common/index.ts | 1 - .../common/latest-feature-block.tsx | 40 ----- space/components/instance/index.ts | 1 - space/components/instance/not-ready-view.tsx | 62 -------- .../issues/board-views/block-downvotes.tsx | 10 -- .../issues/board-views/block-labels.tsx | 19 --- .../issues/board-views/block-state.tsx | 18 --- .../issues/board-views/block-upvotes.tsx | 8 - .../issues/board-views/calendar/index.tsx | 1 - .../issues/board-views/gantt/index.tsx | 1 - .../issues/board-views/spreadsheet/index.tsx | 1 - space/components/issues/index.ts | 2 + .../components/issues/issue-layouts/index.ts | 4 + .../kanban/block.tsx | 8 +- .../kanban/header.tsx | 11 +- .../issues/issue-layouts/kanban/index.ts | 3 + .../kanban/root.tsx} | 7 +- .../list/block.tsx | 7 +- .../list/header.tsx | 13 +- .../issues/issue-layouts/list/index.ts | 3 + .../index.tsx => issue-layouts/list/root.tsx} | 11 +- .../properties/due-date.tsx} | 0 .../issues/issue-layouts/properties/index.ts | 4 + .../issue-layouts/properties/labels.tsx | 17 +++ .../properties/priority.tsx} | 0 .../issues/issue-layouts/properties/state.tsx | 11 ++ .../issue-layouts/root.tsx} | 17 +-- space/components/issues/navbar/controls.tsx | 6 +- space/components/issues/navbar/index.ts | 5 + .../issues/navbar/issue-board-view.tsx | 70 --------- .../issues/navbar/layout-selection.tsx | 67 +++++++++ .../issues/navbar/{index.tsx => root.tsx} | 8 +- space/components/ui/dropdown.tsx | 142 ------------------ space/components/ui/index.ts | 1 - space/components/views/index.ts | 1 - space/constants/issue.ts | 86 ++--------- space/constants/workspace.ts | 12 -- space/helpers/emoji.helper.tsx | 20 --- space/helpers/string.helper.ts | 4 +- space/hooks/use-mention.tsx | 4 +- space/types/app.d.ts | 14 -- space/types/issue.d.ts | 19 +-- space/types/publish.d.ts | 2 +- space/types/theme.d.ts | 4 - space/types/user.d.ts | 30 ---- 51 files changed, 218 insertions(+), 642 deletions(-) delete mode 100644 space/components/common/latest-feature-block.tsx delete mode 100644 space/components/instance/not-ready-view.tsx delete mode 100644 space/components/issues/board-views/block-downvotes.tsx delete mode 100644 space/components/issues/board-views/block-labels.tsx delete mode 100644 space/components/issues/board-views/block-state.tsx delete mode 100644 space/components/issues/board-views/block-upvotes.tsx delete mode 100644 space/components/issues/board-views/calendar/index.tsx delete mode 100644 space/components/issues/board-views/gantt/index.tsx delete mode 100644 space/components/issues/board-views/spreadsheet/index.tsx create mode 100644 space/components/issues/index.ts create mode 100644 space/components/issues/issue-layouts/index.ts rename space/components/issues/{board-views => issue-layouts}/kanban/block.tsx (87%) rename space/components/issues/{board-views => issue-layouts}/kanban/header.tsx (69%) create mode 100644 space/components/issues/issue-layouts/kanban/index.ts rename space/components/issues/{board-views/kanban/index.tsx => issue-layouts/kanban/root.tsx} (84%) rename space/components/issues/{board-views => issue-layouts}/list/block.tsx (86%) rename space/components/issues/{board-views => issue-layouts}/list/header.tsx (62%) create mode 100644 space/components/issues/issue-layouts/list/index.ts rename space/components/issues/{board-views/list/index.tsx => issue-layouts/list/root.tsx} (68%) rename space/components/issues/{board-views/block-due-date.tsx => issue-layouts/properties/due-date.tsx} (100%) create mode 100644 space/components/issues/issue-layouts/properties/index.ts create mode 100644 space/components/issues/issue-layouts/properties/labels.tsx rename space/components/issues/{board-views/block-priority.tsx => issue-layouts/properties/priority.tsx} (100%) create mode 100644 space/components/issues/issue-layouts/properties/state.tsx rename space/components/{views/project-details.tsx => issues/issue-layouts/root.tsx} (80%) create mode 100644 space/components/issues/navbar/index.ts delete mode 100644 space/components/issues/navbar/issue-board-view.tsx create mode 100644 space/components/issues/navbar/layout-selection.tsx rename space/components/issues/navbar/{index.tsx => root.tsx} (87%) delete mode 100644 space/components/ui/dropdown.tsx delete mode 100644 space/constants/workspace.ts delete mode 100644 space/types/app.d.ts delete mode 100644 space/types/theme.d.ts delete mode 100644 space/types/user.d.ts diff --git a/space/app/[workspaceSlug]/[projectId]/layout.tsx b/space/app/[workspaceSlug]/[projectId]/layout.tsx index a0b44946c..4c3da9f94 100644 --- a/space/app/[workspaceSlug]/[projectId]/layout.tsx +++ b/space/app/[workspaceSlug]/[projectId]/layout.tsx @@ -6,10 +6,10 @@ type Props = { }; }; -const ProjectIssuesLayout = async (props: Props) => { +const IssuesLayout = async (props: Props) => { const { children } = props; return <>{children}; }; -export default ProjectIssuesLayout; +export default IssuesLayout; diff --git a/space/app/[workspaceSlug]/[projectId]/page.tsx b/space/app/[workspaceSlug]/[projectId]/page.tsx index d54f6514c..b95855be4 100644 --- a/space/app/[workspaceSlug]/[projectId]/page.tsx +++ b/space/app/[workspaceSlug]/[projectId]/page.tsx @@ -17,15 +17,15 @@ type Props = { }; }; -const ProjectIssuesPage = (props: Props) => { +const IssuesPage = (props: Props) => { const { params } = props; const { workspaceSlug, projectId } = params; // states const [error, setError] = useState(false); // params const searchParams = useSearchParams(); - const board = searchParams.get("board") || undefined; - const peekId = searchParams.get("peekId") || undefined; + const board = searchParams.get("board"); + const peekId = searchParams.get("peekId"); useEffect(() => { if (!workspaceSlug || !projectId) return; @@ -50,4 +50,4 @@ const ProjectIssuesPage = (props: Props) => { return ; }; -export default ProjectIssuesPage; +export default IssuesPage; diff --git a/space/app/error.tsx b/space/app/error.tsx index 8ef727655..e47a1af1d 100644 --- a/space/app/error.tsx +++ b/space/app/error.tsx @@ -1,38 +1,47 @@ "use client"; -import Image from "next/image"; -import { useTheme } from "next-themes"; +// ui import { Button } from "@plane/ui"; -// assets -import InstanceFailureDarkImage from "@/public/instance/instance-failure-dark.svg"; -import InstanceFailureImage from "@/public/instance/instance-failure.svg"; - -export default function InstanceError() { - const { resolvedTheme } = useTheme(); - - const instanceImage = resolvedTheme === "dark" ? InstanceFailureDarkImage : InstanceFailureImage; +const ErrorPage = () => { const handleRetry = () => { window.location.reload(); }; return ( -
-
-
- Plane instance failure image -

Unable to fetch instance details.

-

- We were unable to fetch the details of the instance.
- Fret not, it might just be a connectivity issue. +

+
+
+

Exception Detected!

+

+ We{"'"}re Sorry! An exception has been detected, and our engineering team has been notified. We apologize + for any inconvenience this may have caused. Please reach out to our engineering team at{" "} + + support@plane.so + {" "} + or on our{" "} + + Discord + {" "} + server for further assistance.

-
- + {/* */}
); -} +}; + +export default ErrorPage; diff --git a/space/app/issues/[anchor]/layout.tsx b/space/app/issues/[anchor]/layout.tsx index 4e232ef60..91291e481 100644 --- a/space/app/issues/[anchor]/layout.tsx +++ b/space/app/issues/[anchor]/layout.tsx @@ -5,7 +5,7 @@ import Image from "next/image"; import useSWR from "swr"; // components import { LogoSpinner } from "@/components/common"; -import IssueNavbar from "@/components/issues/navbar"; +import { IssuesNavbarRoot } from "@/components/issues"; // hooks import { usePublish, usePublishList } from "@/hooks/store"; // assets @@ -18,7 +18,7 @@ type Props = { }; }; -const ProjectIssuesLayout = observer((props: Props) => { +const IssuesLayout = observer((props: Props) => { const { children, params } = props; // params const { anchor } = params; @@ -33,7 +33,7 @@ const ProjectIssuesLayout = observer((props: Props) => { return (
- +
{children}
{ ); }); -export default ProjectIssuesLayout; +export default IssuesLayout; diff --git a/space/app/issues/[anchor]/page.tsx b/space/app/issues/[anchor]/page.tsx index ac726a00c..b3c9353e6 100644 --- a/space/app/issues/[anchor]/page.tsx +++ b/space/app/issues/[anchor]/page.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite"; import { useSearchParams } from "next/navigation"; // components -import { ProjectDetailsView } from "@/components/views"; +import { IssuesLayoutsRoot } from "@/components/issues"; // hooks import { usePublish } from "@/hooks/store"; @@ -13,7 +13,7 @@ type Props = { }; }; -const ProjectIssuesPage =observer ((props: Props) => { +const IssuesPage = observer((props: Props) => { const { params } = props; const { anchor } = params; // params @@ -24,7 +24,7 @@ const ProjectIssuesPage =observer ((props: Props) => { if (!publishSettings) return null; - return ; + return ; }); -export default ProjectIssuesPage; +export default IssuesPage; diff --git a/space/components/account/user-logged-in.tsx b/space/components/account/user-logged-in.tsx index 5a44cb55f..5975d73b6 100644 --- a/space/components/account/user-logged-in.tsx +++ b/space/components/account/user-logged-in.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite"; import Image from "next/image"; import { useTheme } from "next-themes"; // components -import { UserAvatar } from "@/components/issues/navbar/user-avatar"; +import { UserAvatar } from "@/components/issues"; // hooks import { useUser } from "@/hooks/store"; // assets @@ -25,7 +25,7 @@ export const UserLoggedIn = observer(() => { return (
-
+
Plane logo
diff --git a/space/components/common/index.ts b/space/components/common/index.ts index c4ea97f3c..1949c069b 100644 --- a/space/components/common/index.ts +++ b/space/components/common/index.ts @@ -1,3 +1,2 @@ -export * from "./latest-feature-block"; export * from "./project-logo"; export * from "./logo-spinner"; diff --git a/space/components/common/latest-feature-block.tsx b/space/components/common/latest-feature-block.tsx deleted file mode 100644 index c1b5db954..000000000 --- a/space/components/common/latest-feature-block.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import Image from "next/image"; -import Link from "next/link"; -import { useTheme } from "next-themes"; -// icons -import { Lightbulb } from "lucide-react"; -// images -import latestFeatures from "public/onboarding/onboarding-pages.svg"; - -export const LatestFeatureBlock = () => { - const { resolvedTheme } = useTheme(); - - return ( - <> -
- -

- Pages gets a facelift! Write anything and use Galileo to help you start.{" "} - - Learn more - -

-
-
-
- Plane Issues -
-
- - ); -}; diff --git a/space/components/instance/index.ts b/space/components/instance/index.ts index 6568894f0..be80bc669 100644 --- a/space/components/instance/index.ts +++ b/space/components/instance/index.ts @@ -1,2 +1 @@ -export * from "./not-ready-view"; export * from "./instance-failure-view"; diff --git a/space/components/instance/not-ready-view.tsx b/space/components/instance/not-ready-view.tsx deleted file mode 100644 index be46a9473..000000000 --- a/space/components/instance/not-ready-view.tsx +++ /dev/null @@ -1,62 +0,0 @@ -"use client"; - -import { FC } from "react"; -import Image from "next/image"; -import Link from "next/link"; -import { useTheme } from "next-themes"; -// ui -import { Button } from "@plane/ui"; -// helper -import { GOD_MODE_URL, SPACE_BASE_PATH } from "@/helpers/common.helper"; -// images -import PlaneTakeOffImage from "@/public/instance/plane-takeoff.png"; -import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg"; -import PlaneBackgroundPattern from "public/auth/background-pattern.svg"; -import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.png"; -import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.png"; - -export const InstanceNotReady: FC = () => { - const { resolvedTheme } = useTheme(); - const patternBackground = resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern; - - const logo = resolvedTheme === "light" ? BlackHorizontalLogo : WhiteHorizontalLogo; - - return ( -
- ); -}; diff --git a/space/components/issues/board-views/block-downvotes.tsx b/space/components/issues/board-views/block-downvotes.tsx deleted file mode 100644 index 4326a8823..000000000 --- a/space/components/issues/board-views/block-downvotes.tsx +++ /dev/null @@ -1,10 +0,0 @@ -"use client"; - -export const IssueBlockDownVotes = ({ number }: { number: number }) => ( -
- - arrow_upward_alt - - {number} -
-); diff --git a/space/components/issues/board-views/block-labels.tsx b/space/components/issues/board-views/block-labels.tsx deleted file mode 100644 index 05f6a039f..000000000 --- a/space/components/issues/board-views/block-labels.tsx +++ /dev/null @@ -1,19 +0,0 @@ -"use client"; - -export const IssueBlockLabels = ({ labels }: any) => ( -
- {labels && - labels.length > 0 && - labels.map((_label: any) => ( -
-
-
-
{_label?.name}
-
-
- ))} -
-); diff --git a/space/components/issues/board-views/block-state.tsx b/space/components/issues/board-views/block-state.tsx deleted file mode 100644 index 39b10ceb0..000000000 --- a/space/components/issues/board-views/block-state.tsx +++ /dev/null @@ -1,18 +0,0 @@ -// ui -import { StateGroupIcon } from "@plane/ui"; -// constants -import { issueGroupFilter } from "@/constants/issue"; - -export const IssueBlockState = ({ state }: any) => { - const stateGroup = issueGroupFilter(state.group); - - if (stateGroup === null) return <>; - return ( -
-
- -
{state?.name}
-
-
- ); -}; diff --git a/space/components/issues/board-views/block-upvotes.tsx b/space/components/issues/board-views/block-upvotes.tsx deleted file mode 100644 index 3927acac4..000000000 --- a/space/components/issues/board-views/block-upvotes.tsx +++ /dev/null @@ -1,8 +0,0 @@ -"use client"; - -export const IssueBlockUpVotes = ({ number }: { number: number }) => ( -
- arrow_upward_alt - {number} -
-); diff --git a/space/components/issues/board-views/calendar/index.tsx b/space/components/issues/board-views/calendar/index.tsx deleted file mode 100644 index 0edeca96c..000000000 --- a/space/components/issues/board-views/calendar/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export const IssueCalendarView = () =>
; diff --git a/space/components/issues/board-views/gantt/index.tsx b/space/components/issues/board-views/gantt/index.tsx deleted file mode 100644 index 5da924b2c..000000000 --- a/space/components/issues/board-views/gantt/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export const IssueGanttView = () =>
; diff --git a/space/components/issues/board-views/spreadsheet/index.tsx b/space/components/issues/board-views/spreadsheet/index.tsx deleted file mode 100644 index 45ebf2792..000000000 --- a/space/components/issues/board-views/spreadsheet/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export const IssueSpreadsheetView = () =>
; diff --git a/space/components/issues/index.ts b/space/components/issues/index.ts new file mode 100644 index 000000000..6aee62097 --- /dev/null +++ b/space/components/issues/index.ts @@ -0,0 +1,2 @@ +export * from "./issue-layouts"; +export * from "./navbar"; diff --git a/space/components/issues/issue-layouts/index.ts b/space/components/issues/issue-layouts/index.ts new file mode 100644 index 000000000..5ab6813cd --- /dev/null +++ b/space/components/issues/issue-layouts/index.ts @@ -0,0 +1,4 @@ +export * from "./kanban"; +export * from "./list"; +export * from "./properties"; +export * from "./root"; diff --git a/space/components/issues/board-views/kanban/block.tsx b/space/components/issues/issue-layouts/kanban/block.tsx similarity index 87% rename from space/components/issues/board-views/kanban/block.tsx rename to space/components/issues/issue-layouts/kanban/block.tsx index e7481e359..b17deddcd 100644 --- a/space/components/issues/board-views/kanban/block.tsx +++ b/space/components/issues/issue-layouts/kanban/block.tsx @@ -4,9 +4,7 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; import { useRouter, useSearchParams } from "next/navigation"; // components -import { IssueBlockDueDate } from "@/components/issues/board-views/block-due-date"; -import { IssueBlockPriority } from "@/components/issues/board-views/block-priority"; -import { IssueBlockState } from "@/components/issues/board-views/block-state"; +import { IssueBlockDueDate, IssueBlockPriority, IssueBlockState } from "@/components/issues"; // helpers import { queryParamGenerator } from "@/helpers/query-param-generator"; // hooks @@ -14,13 +12,13 @@ import { useIssueDetails, usePublish } from "@/hooks/store"; // interfaces import { IIssue } from "@/types/issue"; -type IssueKanBanBlockProps = { +type Props = { anchor: string; issue: IIssue; params: any; }; -export const IssueKanBanBlock: FC = observer((props) => { +export const IssueKanBanBlock: FC = observer((props) => { const { anchor, issue } = props; // router const router = useRouter(); diff --git a/space/components/issues/board-views/kanban/header.tsx b/space/components/issues/issue-layouts/kanban/header.tsx similarity index 69% rename from space/components/issues/board-views/kanban/header.tsx rename to space/components/issues/issue-layouts/kanban/header.tsx index f40812f75..ee5433d68 100644 --- a/space/components/issues/board-views/kanban/header.tsx +++ b/space/components/issues/issue-layouts/kanban/header.tsx @@ -5,14 +5,13 @@ import { observer } from "mobx-react-lite"; import { IStateLite } from "@plane/types"; // ui import { StateGroupIcon } from "@plane/ui"; -// constants -import { issueGroupFilter } from "@/constants/issue"; -export const IssueKanBanHeader = observer(({ state }: { state: IStateLite }) => { - // const { getCountOfIssuesByState } = useIssue(); - const stateGroup = issueGroupFilter(state.group); +type Props = { + state: IStateLite; +}; - if (stateGroup === null) return <>; +export const IssueKanBanHeader: React.FC = observer((props) => { + const { state } = props; return (
diff --git a/space/components/issues/issue-layouts/kanban/index.ts b/space/components/issues/issue-layouts/kanban/index.ts new file mode 100644 index 000000000..62874fbda --- /dev/null +++ b/space/components/issues/issue-layouts/kanban/index.ts @@ -0,0 +1,3 @@ +export * from "./block"; +export * from "./header"; +export * from "./root"; diff --git a/space/components/issues/board-views/kanban/index.tsx b/space/components/issues/issue-layouts/kanban/root.tsx similarity index 84% rename from space/components/issues/board-views/kanban/index.tsx rename to space/components/issues/issue-layouts/kanban/root.tsx index 4f9867588..e0a5593e9 100644 --- a/space/components/issues/board-views/kanban/index.tsx +++ b/space/components/issues/issue-layouts/kanban/root.tsx @@ -3,18 +3,17 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; // components -import { IssueKanBanBlock } from "@/components/issues/board-views/kanban/block"; -import { IssueKanBanHeader } from "@/components/issues/board-views/kanban/header"; +import { IssueKanBanBlock, IssueKanBanHeader } from "@/components/issues"; // ui import { Icon } from "@/components/ui"; // mobx hook import { useIssue } from "@/hooks/store"; -type IssueKanbanViewProps = { +type Props = { anchor: string; }; -export const IssueKanbanView: FC = observer((props) => { +export const IssueKanbanLayoutRoot: FC = observer((props) => { const { anchor } = props; // store hooks const { states, getFilteredIssuesByState } = useIssue(); diff --git a/space/components/issues/board-views/list/block.tsx b/space/components/issues/issue-layouts/list/block.tsx similarity index 86% rename from space/components/issues/board-views/list/block.tsx rename to space/components/issues/issue-layouts/list/block.tsx index 98e5299f2..38f9f7fd6 100644 --- a/space/components/issues/board-views/list/block.tsx +++ b/space/components/issues/issue-layouts/list/block.tsx @@ -3,10 +3,7 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; import { useRouter, useSearchParams } from "next/navigation"; // components -import { IssueBlockDueDate } from "@/components/issues/board-views/block-due-date"; -import { IssueBlockLabels } from "@/components/issues/board-views/block-labels"; -import { IssueBlockPriority } from "@/components/issues/board-views/block-priority"; -import { IssueBlockState } from "@/components/issues/board-views/block-state"; +import { IssueBlockDueDate, IssueBlockLabels, IssueBlockPriority, IssueBlockState } from "@/components/issues"; // helpers import { queryParamGenerator } from "@/helpers/query-param-generator"; // hook @@ -20,7 +17,7 @@ type IssueListBlockProps = { issue: IIssue; }; -export const IssueListBlock: FC = observer((props) => { +export const IssueListLayoutBlock: FC = observer((props) => { const { anchor, issue } = props; const searchParams = useSearchParams(); // query params diff --git a/space/components/issues/board-views/list/header.tsx b/space/components/issues/issue-layouts/list/header.tsx similarity index 62% rename from space/components/issues/board-views/list/header.tsx rename to space/components/issues/issue-layouts/list/header.tsx index c7dd6e50d..a038050a9 100644 --- a/space/components/issues/board-views/list/header.tsx +++ b/space/components/issues/issue-layouts/list/header.tsx @@ -1,19 +1,18 @@ "use client"; +import React from "react"; import { observer } from "mobx-react-lite"; // types import { IStateLite } from "@plane/types"; // ui import { StateGroupIcon } from "@plane/ui"; -// constants -import { issueGroupFilter } from "@/constants/issue"; -export const IssueListHeader = observer(({ state }: { state: IStateLite }) => { - // const { getCountOfIssuesByState } = useIssue(); - const stateGroup = issueGroupFilter(state.group); - // const count = getCountOfIssuesByState(state.id); +type Props = { + state: IStateLite; +}; - if (stateGroup === null) return <>; +export const IssueListLayoutHeader: React.FC = observer((props) => { + const { state } = props; return (
diff --git a/space/components/issues/issue-layouts/list/index.ts b/space/components/issues/issue-layouts/list/index.ts new file mode 100644 index 000000000..62874fbda --- /dev/null +++ b/space/components/issues/issue-layouts/list/index.ts @@ -0,0 +1,3 @@ +export * from "./block"; +export * from "./header"; +export * from "./root"; diff --git a/space/components/issues/board-views/list/index.tsx b/space/components/issues/issue-layouts/list/root.tsx similarity index 68% rename from space/components/issues/board-views/list/index.tsx rename to space/components/issues/issue-layouts/list/root.tsx index 92e184be1..02cd25b40 100644 --- a/space/components/issues/board-views/list/index.tsx +++ b/space/components/issues/issue-layouts/list/root.tsx @@ -2,16 +2,15 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; // components -import { IssueListBlock } from "@/components/issues/board-views/list/block"; -import { IssueListHeader } from "@/components/issues/board-views/list/header"; +import { IssueListLayoutBlock, IssueListLayoutHeader } from "@/components/issues"; // mobx hook import { useIssue } from "@/hooks/store"; -type IssueListViewProps = { +type Props = { anchor: string; }; -export const IssueListView: FC = observer((props) => { +export const IssuesListLayoutRoot: FC = observer((props) => { const { anchor } = props; // store hooks const { states, getFilteredIssuesByState } = useIssue(); @@ -23,11 +22,11 @@ export const IssueListView: FC = observer((props) => { return (
- + {issues && issues.length > 0 ? (
{issues.map((issue) => ( - + ))}
) : ( diff --git a/space/components/issues/board-views/block-due-date.tsx b/space/components/issues/issue-layouts/properties/due-date.tsx similarity index 100% rename from space/components/issues/board-views/block-due-date.tsx rename to space/components/issues/issue-layouts/properties/due-date.tsx diff --git a/space/components/issues/issue-layouts/properties/index.ts b/space/components/issues/issue-layouts/properties/index.ts new file mode 100644 index 000000000..de78f9966 --- /dev/null +++ b/space/components/issues/issue-layouts/properties/index.ts @@ -0,0 +1,4 @@ +export * from "./due-date"; +export * from "./labels"; +export * from "./priority"; +export * from "./state"; diff --git a/space/components/issues/issue-layouts/properties/labels.tsx b/space/components/issues/issue-layouts/properties/labels.tsx new file mode 100644 index 000000000..75c32c4a0 --- /dev/null +++ b/space/components/issues/issue-layouts/properties/labels.tsx @@ -0,0 +1,17 @@ +"use client"; + +export const IssueBlockLabels = ({ labels }: any) => ( +
+ {labels?.map((_label: any) => ( +
+
+
+
{_label?.name}
+
+
+ ))} +
+); diff --git a/space/components/issues/board-views/block-priority.tsx b/space/components/issues/issue-layouts/properties/priority.tsx similarity index 100% rename from space/components/issues/board-views/block-priority.tsx rename to space/components/issues/issue-layouts/properties/priority.tsx diff --git a/space/components/issues/issue-layouts/properties/state.tsx b/space/components/issues/issue-layouts/properties/state.tsx new file mode 100644 index 000000000..3c60640af --- /dev/null +++ b/space/components/issues/issue-layouts/properties/state.tsx @@ -0,0 +1,11 @@ +// ui +import { StateGroupIcon } from "@plane/ui"; + +export const IssueBlockState = ({ state }: any) => ( +
+
+ +
{state?.name}
+
+
+); diff --git a/space/components/views/project-details.tsx b/space/components/issues/issue-layouts/root.tsx similarity index 80% rename from space/components/views/project-details.tsx rename to space/components/issues/issue-layouts/root.tsx index 27fb3b7ce..e53986c85 100644 --- a/space/components/views/project-details.tsx +++ b/space/components/issues/issue-layouts/root.tsx @@ -6,11 +6,7 @@ import Image from "next/image"; import { useSearchParams } from "next/navigation"; import useSWR from "swr"; // components -import { IssueCalendarView } from "@/components/issues/board-views/calendar"; -import { IssueGanttView } from "@/components/issues/board-views/gantt"; -import { IssueKanbanView } from "@/components/issues/board-views/kanban"; -import { IssueListView } from "@/components/issues/board-views/list"; -import { IssueSpreadsheetView } from "@/components/issues/board-views/spreadsheet"; +import { IssueKanbanLayoutRoot, IssuesListLayoutRoot } from "@/components/issues"; import { IssueAppliedFilters } from "@/components/issues/filters/applied-filters/root"; import { IssuePeekOverview } from "@/components/issues/peek-overview"; // hooks @@ -20,12 +16,12 @@ import { PublishStore } from "@/store/publish/publish.store"; // assets import SomethingWentWrongImage from "public/something-went-wrong.svg"; -type ProjectDetailsViewProps = { +type Props = { peekId: string | undefined; publishSettings: PublishStore; }; -export const ProjectDetailsView: FC = observer((props) => { +export const IssuesLayoutsRoot: FC = observer((props) => { const { peekId, publishSettings } = props; // query params const searchParams = useSearchParams(); @@ -84,17 +80,14 @@ export const ProjectDetailsView: FC = observer((props) {activeLayout === "list" && (
- +
)} {activeLayout === "kanban" && (
- +
)} - {activeLayout === "calendar" && } - {activeLayout === "spreadsheet" && } - {activeLayout === "gantt" && }
) )} diff --git a/space/components/issues/navbar/controls.tsx b/space/components/issues/navbar/controls.tsx index a1f9265ea..25f2edfb0 100644 --- a/space/components/issues/navbar/controls.tsx +++ b/space/components/issues/navbar/controls.tsx @@ -4,10 +4,8 @@ import { useEffect, FC } from "react"; import { observer } from "mobx-react-lite"; import { useRouter, useSearchParams } from "next/navigation"; // components +import { IssuesLayoutSelection, NavbarTheme, UserAvatar } from "@/components/issues"; import { IssueFiltersDropdown } from "@/components/issues/filters"; -import { NavbarIssueBoardView } from "@/components/issues/navbar/issue-board-view"; -import { NavbarTheme } from "@/components/issues/navbar/theme"; -import { UserAvatar } from "@/components/issues/navbar/user-avatar"; // helpers import { queryParamGenerator } from "@/helpers/query-param-generator"; // hooks @@ -105,7 +103,7 @@ export const NavbarControls: FC = observer((props) => { <> {/* issue views */}
- +
{/* issue filters */} diff --git a/space/components/issues/navbar/index.ts b/space/components/issues/navbar/index.ts new file mode 100644 index 000000000..e1bb02d91 --- /dev/null +++ b/space/components/issues/navbar/index.ts @@ -0,0 +1,5 @@ +export * from "./controls"; +export * from "./layout-selection"; +export * from "./root"; +export * from "./theme"; +export * from "./user-avatar"; diff --git a/space/components/issues/navbar/issue-board-view.tsx b/space/components/issues/navbar/issue-board-view.tsx deleted file mode 100644 index d8ed5ef07..000000000 --- a/space/components/issues/navbar/issue-board-view.tsx +++ /dev/null @@ -1,70 +0,0 @@ -"use client"; - -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -import { useRouter, useSearchParams } from "next/navigation"; -// constants -import { issueLayoutViews } from "@/constants/issue"; -// helpers -import { queryParamGenerator } from "@/helpers/query-param-generator"; -// hooks -import { useIssueFilter } from "@/hooks/store"; -// mobx -import { TIssueLayout } from "@/types/issue"; - -type NavbarIssueBoardViewProps = { - anchor: string; -}; - -export const NavbarIssueBoardView: FC = observer((props) => { - const { anchor } = props; - // router - const router = useRouter(); - const searchParams = useSearchParams(); - // query params - const labels = searchParams.get("labels") || undefined; - const state = searchParams.get("state") || undefined; - const priority = searchParams.get("priority") || undefined; - const peekId = searchParams.get("peekId") || undefined; - // hooks - const { layoutOptions, getIssueFilters, updateIssueFilters } = useIssueFilter(); - // derived values - const issueFilters = getIssueFilters(anchor); - const activeLayout = issueFilters?.display_filters?.layout || undefined; - - const handleCurrentBoardView = (boardView: TIssueLayout) => { - updateIssueFilters(anchor, "display_filters", "layout", boardView); - const { queryParam } = queryParamGenerator({ board: boardView, peekId, priority, state, labels }); - router.push(`/issues/${anchor}?${queryParam}`); - }; - - return ( - <> - {Object.keys(issueLayoutViews).map((key: string) => { - const layoutKey = key as TIssueLayout; - if (layoutOptions[layoutKey]) { - return ( -
handleCurrentBoardView(layoutKey)} - title={layoutKey} - > - - {issueLayoutViews[layoutKey]?.icon} - -
- ); - } - })} - - ); -}); diff --git a/space/components/issues/navbar/layout-selection.tsx b/space/components/issues/navbar/layout-selection.tsx new file mode 100644 index 000000000..1989710b5 --- /dev/null +++ b/space/components/issues/navbar/layout-selection.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { FC } from "react"; +import { observer } from "mobx-react-lite"; +import { useRouter, useSearchParams } from "next/navigation"; +// ui +import { Tooltip } from "@plane/ui"; +// constants +import { ISSUE_LAYOUTS } from "@/constants/issue"; +// helpers +import { queryParamGenerator } from "@/helpers/query-param-generator"; +// hooks +import { useIssueFilter } from "@/hooks/store"; +// mobx +import { TIssueLayout } from "@/types/issue"; + +type Props = { + anchor: string; +}; + +export const IssuesLayoutSelection: FC = observer((props) => { + const { anchor } = props; + // router + const router = useRouter(); + const searchParams = useSearchParams(); + // query params + const labels = searchParams.get("labels"); + const state = searchParams.get("state"); + const priority = searchParams.get("priority"); + const peekId = searchParams.get("peekId"); + // hooks + const { layoutOptions, getIssueFilters, updateIssueFilters } = useIssueFilter(); + // derived values + const issueFilters = getIssueFilters(anchor); + const activeLayout = issueFilters?.display_filters?.layout || undefined; + + const handleCurrentBoardView = (boardView: TIssueLayout) => { + updateIssueFilters(anchor, "display_filters", "layout", boardView); + const { queryParam } = queryParamGenerator({ board: boardView, peekId, priority, state, labels }); + router.push(`/issues/${anchor}?${queryParam}`); + }; + + return ( +
+ {ISSUE_LAYOUTS.map((layout) => { + if (!layoutOptions[layout.key]) return; + + return ( + + + + ); + })} +
+ ); +}); diff --git a/space/components/issues/navbar/index.tsx b/space/components/issues/navbar/root.tsx similarity index 87% rename from space/components/issues/navbar/index.tsx rename to space/components/issues/navbar/root.tsx index 048e169e0..1d1a294d9 100644 --- a/space/components/issues/navbar/index.tsx +++ b/space/components/issues/navbar/root.tsx @@ -4,15 +4,15 @@ import { observer } from "mobx-react-lite"; import { Briefcase } from "lucide-react"; // components import { ProjectLogo } from "@/components/common"; -import { NavbarControls } from "@/components/issues/navbar/controls"; +import { NavbarControls } from "@/components/issues"; // store import { PublishStore } from "@/store/publish/publish.store"; -type IssueNavbarProps = { +type Props = { publishSettings: PublishStore; }; -const IssueNavbar: FC = observer((props) => { +export const IssuesNavbarRoot: FC = observer((props) => { const { publishSettings } = props; // hooks const { project_details } = publishSettings; @@ -41,5 +41,3 @@ const IssueNavbar: FC = observer((props) => {
); }); - -export default IssueNavbar; diff --git a/space/components/ui/dropdown.tsx b/space/components/ui/dropdown.tsx deleted file mode 100644 index 788627094..000000000 --- a/space/components/ui/dropdown.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { Fragment, useState, useRef } from "react"; -import Link from "next/link"; -import { Check, ChevronLeft } from "lucide-react"; -import { Popover, Transition } from "@headlessui/react"; -// hooks -import useOutSideClick from "hooks/use-outside-click"; - -type ItemOptionType = { - display: React.ReactNode; - as?: "button" | "link" | "div"; - href?: string; - isSelected?: boolean; - onClick?: () => void; - children?: ItemOptionType[] | null; -}; - -type DropdownItemProps = { - item: ItemOptionType; -}; - -type DropDownListProps = { - open: boolean; - handleClose?: () => void; - items: ItemOptionType[]; -}; - -type DropdownProps = { - button: React.ReactNode | (() => React.ReactNode); - items: ItemOptionType[]; -}; - -const DropdownList: React.FC = (props) => { - const { open, items, handleClose } = props; - - const ref = useRef(null); - - useOutSideClick(ref, () => { - if (handleClose) handleClose(); - }); - - return ( - - - -
- {items.map((item, index) => ( - - ))} -
-
-
-
- ); -}; - -const DropdownItem: React.FC = (props) => { - const { item } = props; - const { display, children, as: itemAs, href, onClick, isSelected } = item; - - const [open, setOpen] = useState(false); - - return ( -
- {(!itemAs || itemAs === "button" || itemAs === "div") && ( - - )} - - {itemAs === "link" && {display}} - - {children && setOpen(false)} items={children} />} -
- ); -}; - -const Dropdown: React.FC = (props) => { - const { button, items } = props; - - return ( - - {({ open }) => ( - <> - - {typeof button === "function" ? button() : button} - - - - -
- {items.map((item, index) => ( - - ))} -
-
-
- - )} -
- ); -}; - -export { Dropdown }; diff --git a/space/components/ui/index.ts b/space/components/ui/index.ts index 1e523d5dd..ccd2303c4 100644 --- a/space/components/ui/index.ts +++ b/space/components/ui/index.ts @@ -1,3 +1,2 @@ -export * from "./dropdown"; export * from "./icon"; export * from "./reaction-selector"; diff --git a/space/components/views/index.ts b/space/components/views/index.ts index 251de14e3..97ccf7649 100644 --- a/space/components/views/index.ts +++ b/space/components/views/index.ts @@ -1,2 +1 @@ export * from "./auth"; -export * from "./project-details"; diff --git a/space/constants/issue.ts b/space/constants/issue.ts index f96eb3e0e..77297946f 100644 --- a/space/constants/issue.ts +++ b/space/constants/issue.ts @@ -1,13 +1,7 @@ +import { Calendar, GanttChartSquare, Kanban, List, Sheet } from "lucide-react"; // types import { TIssuePriorities } from "@plane/types"; -import { - TIssueLayout, - TIssueLayoutViews, - TIssueFilterKeys, - TIssueFilterPriorityObject, - TIssueFilterState, - TIssueFilterStateObject, -} from "@/types/issue"; +import { TIssueLayout, TIssueFilterKeys, TIssueFilterPriorityObject } from "@/types/issue"; // issue filters export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { [key in TIssueLayout]: Record<"filters", TIssueFilterKeys[]> } = { @@ -28,20 +22,18 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { [key in TIssueLayout]: Record<"f }, }; -export const issueLayoutViews: Partial = { - list: { - title: "List View", - icon: "format_list_bulleted", - className: "", - }, - kanban: { - title: "Board View", - icon: "grid_view", - className: "", - }, -}; +export const ISSUE_LAYOUTS: { + key: TIssueLayout; + title: string; + icon: any; +}[] = [ + { key: "list", title: "List", icon: List }, + { key: "kanban", title: "Kanban", icon: Kanban }, + { key: "calendar", title: "Calendar", icon: Calendar }, + { key: "spreadsheet", title: "Spreadsheet", icon: Sheet }, + { key: "gantt", title: "Gantt chart", icon: GanttChartSquare }, +]; -// issue priority filters export const issuePriorityFilters: TIssueFilterPriorityObject[] = [ { key: "urgent", @@ -84,55 +76,3 @@ export const issuePriorityFilter = (priorityKey: TIssuePriorities): TIssueFilter if (currentIssuePriority) return currentIssuePriority; return undefined; }; - -// issue group filters -export const issueGroupColors: { - [key in TIssueFilterState]: string; -} = { - backlog: "#d9d9d9", - unstarted: "#3f76ff", - started: "#f59e0b", - completed: "#16a34a", - cancelled: "#dc2626", -}; - -export const issueGroups: TIssueFilterStateObject[] = [ - { - key: "backlog", - title: "Backlog", - color: "#d9d9d9", - className: `text-[#d9d9d9] bg-[#d9d9d9]/10`, - }, - { - key: "unstarted", - title: "Unstarted", - color: "#3f76ff", - className: `text-[#3f76ff] bg-[#3f76ff]/10`, - }, - { - key: "started", - title: "Started", - color: "#f59e0b", - className: `text-[#f59e0b] bg-[#f59e0b]/10`, - }, - { - key: "completed", - title: "Completed", - color: "#16a34a", - className: `text-[#16a34a] bg-[#16a34a]/10`, - }, - { - key: "cancelled", - title: "Cancelled", - color: "#dc2626", - className: `text-[#dc2626] bg-[#dc2626]/10`, - }, -]; - -export const issueGroupFilter = (issueKey: TIssueFilterState): TIssueFilterStateObject | undefined => { - const currentIssueStateGroup: TIssueFilterStateObject | undefined = - issueGroups && issueGroups.length > 0 ? issueGroups.find((group) => group.key === issueKey) : undefined; - - if (currentIssueStateGroup) return currentIssueStateGroup; - return undefined; -}; diff --git a/space/constants/workspace.ts b/space/constants/workspace.ts deleted file mode 100644 index 5ae5a7cf4..000000000 --- a/space/constants/workspace.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const USER_ROLES = [ - { value: "Product / Project Manager", label: "Product / Project Manager" }, - { value: "Development / Engineering", label: "Development / Engineering" }, - { value: "Founder / Executive", label: "Founder / Executive" }, - { value: "Freelancer / Consultant", label: "Freelancer / Consultant" }, - { value: "Marketing / Growth", label: "Marketing / Growth" }, - { value: "Sales / Business Development", label: "Sales / Business Development" }, - { value: "Support / Operations", label: "Support / Operations" }, - { value: "Student / Professor", label: "Student / Professor" }, - { value: "Human Resources", label: "Human Resources" }, - { value: "Other", label: "Other" }, -]; diff --git a/space/helpers/emoji.helper.tsx b/space/helpers/emoji.helper.tsx index 7c9f3cfcb..d5f9d1b5a 100644 --- a/space/helpers/emoji.helper.tsx +++ b/space/helpers/emoji.helper.tsx @@ -1,23 +1,3 @@ -export const getRandomEmoji = () => { - const emojis = [ - "8986", - "9200", - "128204", - "127773", - "127891", - "127947", - "128076", - "128077", - "128187", - "128188", - "128512", - "128522", - "128578", - ]; - - return emojis[Math.floor(Math.random() * emojis.length)]; -}; - export const renderEmoji = ( emoji: | string diff --git a/space/helpers/string.helper.ts b/space/helpers/string.helper.ts index 525a9fc99..f6319bc75 100644 --- a/space/helpers/string.helper.ts +++ b/space/helpers/string.helper.ts @@ -3,7 +3,7 @@ import DOMPurify from "dompurify"; export const addSpaceIfCamelCase = (str: string) => str.replace(/([a-z])([A-Z])/g, "$1 $2"); const fallbackCopyTextToClipboard = (text: string) => { - var textArea = document.createElement("textarea"); + const textArea = document.createElement("textarea"); textArea.value = text; // Avoid scrolling to bottom @@ -18,7 +18,7 @@ const fallbackCopyTextToClipboard = (text: string) => { try { // FIXME: Even though we are using this as a fallback, execCommand is deprecated 👎. We should find a better way to do this. // https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand - var successful = document.execCommand("copy"); + document.execCommand("copy"); } catch (err) {} document.body.removeChild(textArea); diff --git a/space/hooks/use-mention.tsx b/space/hooks/use-mention.tsx index 8b2d69720..9e33f7d90 100644 --- a/space/hooks/use-mention.tsx +++ b/space/hooks/use-mention.tsx @@ -1,7 +1,9 @@ import { useRef, useEffect } from "react"; import useSWR from "swr"; +// types import { IUser } from "@plane/types"; -import { UserService } from "services/user.service"; +// services +import { UserService } from "@/services/user.service"; export const useMention = () => { const userService = new UserService(); diff --git a/space/types/app.d.ts b/space/types/app.d.ts deleted file mode 100644 index bd4af3b0c..000000000 --- a/space/types/app.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface IAppConfig { - email_password_login: boolean; - file_size_limit: number; - google_client_id: string | null; - github_app_name: string | null; - github_client_id: string | null; - magic_login: boolean; - slack_client_id: string | null; - posthog_api_key: string | null; - posthog_host: string | null; - has_openai_configured: boolean; - has_unsplash_configured: boolean; - is_self_managed: boolean; -} diff --git a/space/types/issue.d.ts b/space/types/issue.d.ts index 3f2c41451..5b729d1c0 100644 --- a/space/types/issue.d.ts +++ b/space/types/issue.d.ts @@ -1,16 +1,9 @@ -import { IStateLite, IWorkspaceLite, TIssuePriorities } from "@plane/types"; +import { IStateLite, IWorkspaceLite, TIssuePriorities, TStateGroups } from "@plane/types"; export type TIssueLayout = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt"; export type TIssueLayoutOptions = { [key in TIssueLayout]: boolean; }; -export type TIssueLayoutViews = { - [key in TIssueLayout]: { - title: string; - icon: string; - className: string; - }; -}; export type TIssueFilterPriorityObject = { key: TIssuePriorities; @@ -19,14 +12,6 @@ export type TIssueFilterPriorityObject = { icon: string; }; -export type TIssueFilterState = "backlog" | "unstarted" | "started" | "completed" | "cancelled"; -export type TIssueFilterStateObject = { - key: TIssueFilterState; - title: string; - color: string; - className: string; -}; - export type TIssueFilterKeys = "priority" | "state" | "labels"; export type TDisplayFilters = { @@ -34,7 +19,7 @@ export type TDisplayFilters = { }; export type TFilters = { - state: TIssueFilterState[]; + state: TStateGroups[]; priority: TIssuePriorities[]; labels: string[]; }; diff --git a/space/types/publish.d.ts b/space/types/publish.d.ts index 726412978..2dc3793f6 100644 --- a/space/types/publish.d.ts +++ b/space/types/publish.d.ts @@ -1,5 +1,5 @@ import { IWorkspaceLite } from "@plane/types"; -import { TProjectDetails, TViewDetails } from "./project"; +import { TProjectDetails, TViewDetails } from "@/types/project"; export type TPublishEntityType = "project"; diff --git a/space/types/theme.d.ts b/space/types/theme.d.ts deleted file mode 100644 index ca306be51..000000000 --- a/space/types/theme.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IThemeStore { - theme: string; - setTheme: (theme: "light" | "dark" | string) => void; -} diff --git a/space/types/user.d.ts b/space/types/user.d.ts deleted file mode 100644 index d58827876..000000000 --- a/space/types/user.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface IUser { - avatar: string; - cover_image: string | null; - created_at: Date; - created_location: string; - date_joined: Date; - email: string; - display_name: string; - first_name: string; - id: string; - is_email_verified: boolean; - is_onboarded: boolean; - is_tour_completed: boolean; - last_location: string; - last_login: Date; - last_name: string; - mobile_number: string; - role: string; - is_password_autoset: boolean; - onboarding_step: { - workspace_join?: boolean; - profile_complete?: boolean; - workspace_create?: boolean; - workspace_invite?: boolean; - }; - token: string; - updated_at: Date; - username: string; - user_timezone: string; -} From 1e59b2789c007904ac773ff7e8f74836e2239dee Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Wed, 5 Jun 2024 01:21:56 +0530 Subject: [PATCH 6/7] fix: redirect from old path --- space/app/[workspaceSlug]/[projectId]/layout.tsx | 4 +++- space/app/[workspaceSlug]/[projectId]/page.tsx | 11 ++++++----- space/helpers/actions.ts | 5 ----- 3 files changed, 9 insertions(+), 11 deletions(-) delete mode 100644 space/helpers/actions.ts diff --git a/space/app/[workspaceSlug]/[projectId]/layout.tsx b/space/app/[workspaceSlug]/[projectId]/layout.tsx index 4c3da9f94..a205d599f 100644 --- a/space/app/[workspaceSlug]/[projectId]/layout.tsx +++ b/space/app/[workspaceSlug]/[projectId]/layout.tsx @@ -1,3 +1,5 @@ +"use client"; + type Props = { children: React.ReactNode; params: { @@ -6,7 +8,7 @@ type Props = { }; }; -const IssuesLayout = async (props: Props) => { +const IssuesLayout = (props: Props) => { const { children } = props; return <>{children}; diff --git a/space/app/[workspaceSlug]/[projectId]/page.tsx b/space/app/[workspaceSlug]/[projectId]/page.tsx index b95855be4..04d60fce9 100644 --- a/space/app/[workspaceSlug]/[projectId]/page.tsx +++ b/space/app/[workspaceSlug]/[projectId]/page.tsx @@ -1,11 +1,9 @@ "use client"; import { useEffect, useState } from "react"; -import { notFound, useSearchParams } from "next/navigation"; +import { notFound, useSearchParams, useRouter } from "next/navigation"; // components import { LogoSpinner } from "@/components/common"; -// helpers -import { navigate } from "@/helpers/actions"; // services import PublishService from "@/services/publish.service"; const publishService = new PublishService(); @@ -22,6 +20,8 @@ const IssuesPage = (props: Props) => { const { workspaceSlug, projectId } = params; // states const [error, setError] = useState(false); + // router + const router = useRouter(); // params const searchParams = useSearchParams(); const board = searchParams.get("board"); @@ -39,11 +39,12 @@ const IssuesPage = (props: Props) => { if (board) params.append("board", board); if (peekId) params.append("peekId", peekId); if (params.toString()) url += `?${params.toString()}`; - navigate(url); + router.push(url); + // navigate(url); } else throw Error("Invalid entity name"); }) .catch(() => setError(true)); - }, [board, peekId, projectId, workspaceSlug]); + }, [board, peekId, projectId, router, workspaceSlug]); if (error) notFound(); diff --git a/space/helpers/actions.ts b/space/helpers/actions.ts deleted file mode 100644 index 5e85029ca..000000000 --- a/space/helpers/actions.ts +++ /dev/null @@ -1,5 +0,0 @@ -"use server"; - -import { redirect } from "next/navigation"; - -export const navigate = async (path: string) => redirect(path); From 20995aac5371b2807c4f08732b7bae1d9cf20c01 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Wed, 5 Jun 2024 15:09:01 +0530 Subject: [PATCH 7/7] chore: make the whole issue block clickable --- .../issues/issue-layouts/kanban/block.tsx | 31 +++++++++---------- .../issues/issue-layouts/list/block.tsx | 24 +++++++------- .../issues/issue-layouts/properties/state.tsx | 2 +- space/styles/globals.css | 17 ++++++++++ 4 files changed, 45 insertions(+), 29 deletions(-) diff --git a/space/components/issues/issue-layouts/kanban/block.tsx b/space/components/issues/issue-layouts/kanban/block.tsx index b17deddcd..ac03823b4 100644 --- a/space/components/issues/issue-layouts/kanban/block.tsx +++ b/space/components/issues/issue-layouts/kanban/block.tsx @@ -2,7 +2,8 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; -import { useRouter, useSearchParams } from "next/navigation"; +import Link from "next/link"; +import { useSearchParams } from "next/navigation"; // components import { IssueBlockDueDate, IssueBlockPriority, IssueBlockState } from "@/components/issues"; // helpers @@ -20,37 +21,35 @@ type Props = { export const IssueKanBanBlock: FC = observer((props) => { const { anchor, issue } = props; - // router - const router = useRouter(); const searchParams = useSearchParams(); // query params - const board = searchParams.get("board") || undefined; - const state = searchParams.get("state") || undefined; - const priority = searchParams.get("priority") || undefined; - const labels = searchParams.get("labels") || undefined; + const board = searchParams.get("board"); + const state = searchParams.get("state"); + const priority = searchParams.get("priority"); + const labels = searchParams.get("labels"); // store hooks const { project_details } = usePublish(anchor); const { setPeekId } = useIssueDetails(); + const { queryParam } = queryParamGenerator({ board, peekId: issue.id, priority, state, labels }); + const handleBlockClick = () => { setPeekId(issue.id); - const { queryParam } = queryParamGenerator({ board, peekId: issue.id, priority, state, labels }); - router.push(`/issues/${anchor}?${queryParam}`); }; return ( -
+ {/* id */}
{project_details?.identifier}-{issue?.sequence_id}
{/* name */} -
+
{issue.name}
@@ -74,6 +73,6 @@ export const IssueKanBanBlock: FC = observer((props) => {
)}
-
+ ); }); diff --git a/space/components/issues/issue-layouts/list/block.tsx b/space/components/issues/issue-layouts/list/block.tsx index 38f9f7fd6..8c241753d 100644 --- a/space/components/issues/issue-layouts/list/block.tsx +++ b/space/components/issues/issue-layouts/list/block.tsx @@ -1,16 +1,16 @@ "use client"; import { FC } from "react"; import { observer } from "mobx-react-lite"; -import { useRouter, useSearchParams } from "next/navigation"; +import Link from "next/link"; +import { useSearchParams } from "next/navigation"; // components import { IssueBlockDueDate, IssueBlockLabels, IssueBlockPriority, IssueBlockState } from "@/components/issues"; // helpers import { queryParamGenerator } from "@/helpers/query-param-generator"; // hook import { useIssueDetails, usePublish } from "@/hooks/store"; -// interfaces +// types import { IIssue } from "@/types/issue"; -// store type IssueListBlockProps = { anchor: string; @@ -19,8 +19,8 @@ type IssueListBlockProps = { export const IssueListLayoutBlock: FC = observer((props) => { const { anchor, issue } = props; - const searchParams = useSearchParams(); // query params + const searchParams = useSearchParams(); const board = searchParams.get("board") || undefined; const state = searchParams.get("state") || undefined; const priority = searchParams.get("priority") || undefined; @@ -28,25 +28,25 @@ export const IssueListLayoutBlock: FC = observer((props) => // store hooks const { setPeekId } = useIssueDetails(); const { project_details } = usePublish(anchor); - // router - const router = useRouter(); + const { queryParam } = queryParamGenerator({ board, peekId: issue.id, priority, state, labels }); const handleBlockClick = () => { setPeekId(issue.id); - - const { queryParam } = queryParamGenerator({ board, peekId: issue.id, priority, state, labels }); - router.push(`/issues/${anchor}?${queryParam}`); }; return ( -
+
{/* id */}
{project_details?.identifier}-{issue?.sequence_id}
{/* name */} -
+
{issue.name}
@@ -80,6 +80,6 @@ export const IssueListLayoutBlock: FC = observer((props) =>
)}
-
+ ); }); diff --git a/space/components/issues/issue-layouts/properties/state.tsx b/space/components/issues/issue-layouts/properties/state.tsx index 3c60640af..b80f1f3df 100644 --- a/space/components/issues/issue-layouts/properties/state.tsx +++ b/space/components/issues/issue-layouts/properties/state.tsx @@ -3,7 +3,7 @@ import { StateGroupIcon } from "@plane/ui"; export const IssueBlockState = ({ state }: any) => (
-
+
{state?.name}
diff --git a/space/styles/globals.css b/space/styles/globals.css index 47804b768..0b41d8481 100644 --- a/space/styles/globals.css +++ b/space/styles/globals.css @@ -302,6 +302,23 @@ } } +* { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + font-variant-ligatures: none; + -webkit-font-variant-ligatures: none; + text-rendering: optimizeLegibility; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +body { + color: rgba(var(--color-text-100)); +} + ::-webkit-scrollbar { width: 5px; height: 5px;