refactor: MobX store structure (#3228)
* query params from router as computed * chore: setup workspace store and sub-stores * chore: update router query store * chore: update store types * fix: pages store changes * change observables and retain object reference * fix build errors * chore: changed the structure of workspace, project, cycle, module and pages * fix: pages fixes * fix: merge conflicts resolved * chore: fixed workspace list * chore: update workspace store accroding to the new response * fix: adding page details to store * fix: adding new contexts and providers * dev: issues store and filters in new store * dev: optimised the issue fetching in issue base store * chore: project views id mapped * update lodash set to directly run inside runInaction since it mutates the object * fix: context changes * code refactor kanban for better mainatinability * optimize Kanban for performance * chore: implemented hooks for all the created stores * chore: removed bridge id * css change and refactor * chore: update cycle store structure * chore: implement the new label root store * chore: removed object structure * chore: implement project view hook * Kanban new store implementation for project issues * fix project root for kanban * feat: workspace and project members endpoint (#3092) * fix: merge conflicts resolved * issue properties optimization * chore: user stores * chore: create new store context and update hooks * chore: setup inbox store and implement router store * chore: initialize and implement project estimate store * chore: initialize global view store * kanban and list view optimization * chore: use new cycle and module store. (#3172) * chore: use new cycle and module store. * chore: minor improvements. * Revert "chore: merge develop" This reverts commit 9d2e0e29e7370b55b48fc2fee4fd126093a6cc48, reversing changes made to 9595493c42be3ea0ddd17b23a0b124555075c062. * chore: implement useGlobalView hook * refactor: projects & inbox store instances (#3179) * refactor: projects & inbox store instances * fix: formatting * fix: action usage * chore: implement useProjectState hook. (#3185) * dev: issue, cycle store optimiation * fix build for code * dev: removed dummy variables * dev: issue store * fix: adding todos * chore: removing legacy store * dev: issues store types and typos * chore: cycle module user properties * fix legacy store deletion issues * chore: change POST to PATCH * fix issues rendering for project root * chore: removed workspace details in workpsaceinvite * chore: created models for display properties * chore: setup member store and implement it everywhere * refactor: module store (#3202) * refactor: cycle store (#3192) * refator: cycle store * some more improvements. * chore: implement useLabel hook. (#3190) * refactor: inbox & project related stores. (#3193) * refactor: inbox -> filter, issues, inoxes & project -> publish, projects store * refactor: workspace-project-id name * fix kanban dropdown overlapping issue * fix kanban layout minor re rendering * chore: implement useMember store everywhere * chore: create and implement editor mention store * chore: removed the issue view user property * chore: created at id changed * dev: segway intgegration (#3132) * feat: implemented rabbitmq * dev: initialize segway with queue setup * dev: import refactors * dev: create communication with the segway server * dev: create new workers * dev: create celery node queue for consuming messages from django * dev: node to celery connection * dev: setup segway and django connection * dev: refactor the structure and add database integration to the app * dev: add external id and source added --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> * dev: github importer (#3205) * dev: initiate github import * dev: github importer all issues import * dev: github comments and links for the imported issues * dev: update controller to use logger and spread the resultData in getAllEntities * dev: removed console log * dev: update code structure and sync functions * dev: updated retry logic when exception * dev: add imported data as well * dev: update logger and repo fetch * dev: update jira integration to new structure * dev: update migrations * dev: update the reason field * chore: workspace object id removed * chore: view's creation fixed * refactor: mobx store improvements. (#3213) * fix: state and label errors * chore: remove legacy code * fix: branch build fix (#3214) * branch build fix for release-* in case of space,backend,proxy * fixes * chore: update store names and types * fix - file size limit not work on plane.settings.production (#3160) * fix - file size limit not work on plane.settings.production * fix - file size limit not work on plane.settings.production * fix - file size limit not work on plane.settings.production, move to common.py --------- Co-authored-by: luanduongtel4vn <hoangluan@tel4vn.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> * style: instance admin email settings ui & ux update. (#3186) * refactor: use-user-auth hook (#3215) * refactor: use-user-auth hook * fix: user store currentUserLoader * refactor: project-view & application related stores (#3207) * refactor: project-view & application related stores * rename: projectViews -> projectViewIds * fix: project-view favourite state in store * chore: remove unnecessary hooks and contexts (#3217) * chore: update issue assignee property component * chore: bug fixes & improvement (#3218) * chore: draft issue validation added to prevent saving empty or whitespace title * chore: resolve scrolling issue in page empty state * chore: kanban layout quick add issue improvement * fix: bugs & improvements (#3189) * fix: workspace invitation modal form values reset * fix: profile sidebar avatar letter * [refactor] Editor code refactoring (#3194) * removed relative imports from editor core * Update issue widget file paths and imports to use kebab case instead of camel case, to align with coding conventions and improve consistency. * Update Tiptap core and extensions versions to 2.1.13 and Tiptap React version to 2.1.13. Update Tiptap table imports to use the new location in package @tiptap/pm/tables. Update AlertLabel component to use the new type definition for LucideIcon. * updated lock file * removed default exports from editor/core * fixed injecting css into the core package itself * seperated css code to have single source of origin wrt to the package * removed default imports from document editor * all instances using index as key while mapping fixed * Update Lite Text Editor package.json to remove @plane/editor-types as a dependency. Update Lite Text Editor index.ts to update the import of IMentionSuggestion and IMentionHighlight from @plane/editor-types to @plane/editor-core. Update Lite Text Editor ui/index.tsx to update the import of UploadImage, DeleteImage, IMentionSuggestion, and RestoreImage from @plane/editor-types to @plane/editor-core. Update Lite Text Editor ui/menus/fixed-menu/index.tsx to update the import of UploadImage from @plane/editor-types to @plane/editor-core. Update turbo.json to remove @plane/editor-types#build as a dependency for @plane/lite-text-editor#build, @plane/rich-text-editor#build, and @plane/document-editor#build. * Remove deprecated import and adjust tippy.js usage in the slash-commands.tsx file of the editor extensions package. * Update dependencies in `rich-text-editor/package.json`, remove `@plane/editor-types` and add `@plane/editor-core` in `rich-text-editor/src/index.ts`, and update imports in `rich-text-editor/src/ui/extensions/index.tsx` and `rich-text-editor/src/ui/index.tsx` to use `@plane/editor-core` instead of `@plane/editor-types`. * Update package.json dependencies and add new types for image deletion, upload, restore, mention highlight, mention suggestion, and slash command item. * Update import statements in various files to use the new package "@plane/editor-core" instead of "@plane/editor-types". * fixed document editor to follow conventions * Refactor imports in the Rich Text Editor package to use relative paths instead of absolute paths. - Updated imports in `index.ts`, `ui/index.tsx`, and `ui/menus/bubble-menu/index.tsx` to use relative paths. - Updated `tsconfig.json` to include the `baseUrl` compiler option and adjust the `include` and `exclude` paths. * Refactor Lite Text Editor code to use relative import paths instead of absolute import paths. * Added LucideIconType to the exports in index.ts for use in other files. Created a new file lucide-icon.ts which contains the type LucideIconType. Updated the icon type in HeadingOneItem in menu-items/index.tsx to use LucideIconType. Updated the Icon type in AlertLabel in alert-label.tsx to use LucideIconType. Updated the Icon type in VerticalDropdownItemProps in vertical-dropdown-menu.tsx to use LucideIconType. Updated the Icon type in BubbleMenuItem in fixed-menu/index.tsx to use LucideIconType. Deleted the file tooltip.tsx since it is no longer used. Updated the Icon type in BubbleMenuItem in bubble-menu/index.tsx to use LucideIconType. * ♻️ refactor: simplify rendering logic in slash-commands.tsx The rendering logic in the file "slash-commands.tsx" has been simplified. Previously, the code used inline positioning for the popup, but it has now been removed. Instead of appending the popup to the document body, it is now appended to the element with the ID "tiptap-container". The "flip" option has also been removed. These changes have improved the readability and maintainability of the code. * fixed build errors caused due to core's internal imports * regression: fixed pages not saving issue and not duplicating with proper content issue * build: Update @tiptap dependencies Updated the @tiptap dependencies in the package.json files of `document-editor`, `extensions`, and `rich-text-editor` packages to version 2.1.13. * 🚑 fix: Correct appendTo selector in slash-commands.tsx Update the `appendTo` function call in `slash-commands.tsx` to use the correct selector `#editor-container` instead of `#tiptap-container`. This ensures that the component is appended to the appropriate container in the editor extension. Note: The commit message assumes that the change is a fix for an issue or error. If it's not a fix, please provide more context so that an appropriate commit type can be determined. * style: email placeholder changed across the platform (#3206) * style: email placeholder changed across the platform * fix: placeholder text * dev: updated new filter endpoints and restructured issue and issue filters store * implement issues and replace useMobxStore * remove all store legacy references * dev: updated the orderby and subgroupby filters data * dev:added projectId in issue filters for consistency * fix more build errors * dev: updated profile issues * dev: removed store legacy * dev: active cycle issues in the cycle issue store * fix additional build errors and memoize issueActions in each layout component * change store enums * remove all useMobxStore references * fix more build errors * dev: reverted workspace invitation * fix: build errors and warnings * fix: optimistic update for instant operations (#3221) * fix: update functions failed case * fix: typo * chore: revert back to optimistic update approach for all `update related actions` (#3219) * fix: merge conflicts resolved * chore: update memberMap logic in components * add assignees to kanban groups and properties * dev: migration fixes * final bit of optimization on list view * change all TODOs that are to be done before this release to FIXME * change base Kanban TODOs that are to be done before this release to FIXME * dev: add fields and expand for app serializers * dev: issue detail store * dev: update issue serializer to return object ids * fix: Instance key added in settings and converted issues list api to arry instead of dict * fix: removing segway files * dev: control expand through query parameters * revert: github importer * Revert "dev: segway intgegration (#3132)" This reverts commit 1cc18a09156d1790d114061dbac8c901e0f2754c. * dev: remove migrations for segway * dev: issue structure change and created workspacebasemodel * dev: issue detail serializer * fix: changed workspace dict * dev: updated new issue structure * chore: build fix * dev: issue detail store refactor * dev: created list endpoint for issue-relation * dev: added issue attachments in issue detail store * dev: added issue activity computed * fix: build error * chore: peek overview modal context added * chore: build error fix * dev: added sub_issues in issue details store * dev: added complete issue serializer for sub issues * dev: resolved type errors in issue root store * dev: changed the issue relation structure * chore: new global dropdowns * chore: build error fix * chore: cycle and module selection if disabled * dev: removed unnecessary code from the workspace root * chore: build error fix * chore: issue relation remove endpoint * fix: build error * dev: typos and implemented issue relation store * fix: yarn lock updated * style: update the UI of all the dropdowns * fix: state store fixes * fix: key issue * fix: state store console logs removed * refactor: member dropdowns * fix: moving types to packages * fix: dropdown arrow positioning * dev: removed logs * style: label dropdown * chore: restrict description notifications * chore: description changes * chore: update spreadsheet layout dropdowns * fix: build errors * chore: duplicate key change * fix: ui bugs * chore: relation activity change * chore: comment activity changes * chore: blocking issue removal * chore: added project_id for relation * chore: issue relation store and component * chore: issue redirection issue in the issue realtion in detail page * chore: created activity changed * chore: issue links new store implementation on the issue detail * chore: issue relation deletion acitivity changed * chore: issue attachments new store implementation on the issue detail * chore: workspace level issues * fix: build errors --------- Co-authored-by: rahulramesha <rahulramesham@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Co-authored-by: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Co-authored-by: Hoang Luan <luandnh98@gmail.com> Co-authored-by: luanduongtel4vn <hoangluan@tel4vn.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com> Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
parent
1539340113
commit
804b7d8663
940 changed files with 26378 additions and 34411 deletions
|
|
@ -1,31 +1,22 @@
|
|||
import { List } from "./default";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue";
|
||||
import { FC } from "react";
|
||||
import { IIssue, IProject } from "types";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { Spinner } from "@plane/ui";
|
||||
import { IQuickActionProps } from "./list-view-types";
|
||||
import {
|
||||
ICycleIssuesFilterStore,
|
||||
ICycleIssuesStore,
|
||||
IModuleIssuesFilterStore,
|
||||
IModuleIssuesStore,
|
||||
IProfileIssuesFilterStore,
|
||||
IProfileIssuesStore,
|
||||
IProjectArchivedIssuesStore,
|
||||
IProjectDraftIssuesStore,
|
||||
IProjectIssuesFilterStore,
|
||||
IProjectIssuesStore,
|
||||
IViewIssuesFilterStore,
|
||||
IViewIssuesStore,
|
||||
} from "store/issues";
|
||||
import { FC, useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { IIssueResponse } from "store/issues/types";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IssuePeekOverview } from "components/issues";
|
||||
import { useRouter } from "next/router";
|
||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { IProjectIssues, IProjectIssuesFilter } from "store/issue/project";
|
||||
import { ICycleIssues, ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssues, IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProfileIssues, IProfileIssuesFilter } from "store/issue/profile";
|
||||
import { IProjectViewIssues, IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { IDraftIssuesFilter, IDraftIssues } from "store/issue/draft";
|
||||
import { IArchivedIssuesFilter, IArchivedIssues } from "store/issue/archived";
|
||||
// components
|
||||
import { IQuickActionProps } from "./list-view-types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
|
||||
enum EIssueActions {
|
||||
UPDATE = "update",
|
||||
|
|
@ -34,145 +25,128 @@ enum EIssueActions {
|
|||
}
|
||||
|
||||
interface IBaseListRoot {
|
||||
issueFilterStore:
|
||||
| IProjectIssuesFilterStore
|
||||
| IModuleIssuesFilterStore
|
||||
| ICycleIssuesFilterStore
|
||||
| IViewIssuesFilterStore
|
||||
| IProfileIssuesFilterStore;
|
||||
issueStore:
|
||||
| IProjectIssuesStore
|
||||
| IModuleIssuesStore
|
||||
| ICycleIssuesStore
|
||||
| IViewIssuesStore
|
||||
| IProjectArchivedIssuesStore
|
||||
| IProjectDraftIssuesStore
|
||||
| IProfileIssuesStore;
|
||||
issuesFilter:
|
||||
| IProjectIssuesFilter
|
||||
| IModuleIssuesFilter
|
||||
| ICycleIssuesFilter
|
||||
| IProjectViewIssuesFilter
|
||||
| IProfileIssuesFilter
|
||||
| IDraftIssuesFilter
|
||||
| IArchivedIssuesFilter;
|
||||
issues:
|
||||
| IProjectIssues
|
||||
| ICycleIssues
|
||||
| IModuleIssues
|
||||
| IProjectViewIssues
|
||||
| IProfileIssues
|
||||
| IDraftIssues
|
||||
| IArchivedIssues;
|
||||
QuickActions: FC<IQuickActionProps>;
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (group_by: string | null, issue: IIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (group_by: string | null, issue: IIssue) => Promise<void>;
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
getProjects: (projectStore: IProjectStore) => IProject[] | null;
|
||||
viewId?: string;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
currentStore: TCreateModalStoreTypes;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
canEditPropertiesBasedOnProject?: (projectId: string) => boolean;
|
||||
}
|
||||
|
||||
export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||
const {
|
||||
issueFilterStore,
|
||||
issueStore,
|
||||
issuesFilter,
|
||||
issues,
|
||||
QuickActions,
|
||||
issueActions,
|
||||
getProjects,
|
||||
viewId,
|
||||
currentStore,
|
||||
addIssuesToView,
|
||||
canEditPropertiesBasedOnProject,
|
||||
} = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, peekIssueId, peekProjectId } = router.query;
|
||||
// mobx store
|
||||
const {
|
||||
project: projectStore,
|
||||
projectMember: { projectMembers },
|
||||
projectState: projectStateStore,
|
||||
projectLabel: { projectLabels },
|
||||
user: userStore,
|
||||
} = useMobxStore();
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
|
||||
const { currentProjectRole } = userStore;
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const { issueMap } = useIssues();
|
||||
|
||||
const issueIds = issueStore?.getIssuesIds || [];
|
||||
const issues = issueStore?.getIssues;
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {};
|
||||
const canEditProperties = (projectId: string | undefined) => {
|
||||
const isEditingAllowedBasedOnProject =
|
||||
canEditPropertiesBasedOnProject && projectId ? canEditPropertiesBasedOnProject(projectId) : isEditingAllowed;
|
||||
const issueIds = issues?.groupedIssueIds || [];
|
||||
|
||||
return enableInlineEditing && isEditingAllowedBasedOnProject;
|
||||
};
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
|
||||
const canEditProperties = useCallback(
|
||||
(projectId: string | undefined) => {
|
||||
const isEditingAllowedBasedOnProject =
|
||||
canEditPropertiesBasedOnProject && projectId ? canEditPropertiesBasedOnProject(projectId) : isEditingAllowed;
|
||||
|
||||
return enableInlineEditing && isEditingAllowedBasedOnProject;
|
||||
},
|
||||
[canEditPropertiesBasedOnProject, enableInlineEditing, isEditingAllowed]
|
||||
);
|
||||
|
||||
const displayFilters = issuesFilter?.issueFilters?.displayFilters;
|
||||
const displayProperties = issuesFilter?.issueFilters?.displayProperties;
|
||||
|
||||
const displayFilters = issueFilterStore?.issueFilters?.displayFilters;
|
||||
const group_by = displayFilters?.group_by || null;
|
||||
const showEmptyGroup = displayFilters?.show_empty_groups ?? false;
|
||||
|
||||
const displayProperties = issueFilterStore?.issueFilters?.displayProperties;
|
||||
const handleIssues = useCallback(
|
||||
async (issue: TIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
await issueActions[action]!(issue);
|
||||
}
|
||||
},
|
||||
[issueActions]
|
||||
);
|
||||
|
||||
const states = projectStateStore?.projectStates;
|
||||
const priorities = ISSUE_PRIORITIES;
|
||||
const labels = projectLabels;
|
||||
const stateGroups = ISSUE_STATE_GROUPS;
|
||||
const projects = getProjects(projectStore);
|
||||
const members = projectMembers?.map((m) => m.member) ?? null;
|
||||
const handleIssues = async (issue: IIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
await issueActions[action]!(group_by, issue);
|
||||
}
|
||||
};
|
||||
const renderQuickActions = useCallback(
|
||||
(issue: TIssue) => (
|
||||
<QuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||
handleUpdate={
|
||||
issueActions[EIssueActions.UPDATE] ? async (data) => handleIssues(data, EIssueActions.UPDATE) : undefined
|
||||
}
|
||||
handleRemoveFromView={
|
||||
issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined
|
||||
}
|
||||
/>
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[handleIssues]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{issueStore?.loader === "init-loader" ? (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<div className={`relative h-full w-full bg-custom-background-90`}>
|
||||
<List
|
||||
issues={issues as unknown as IIssueResponse}
|
||||
group_by={group_by}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={(group_by, issue) => (
|
||||
<QuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||
handleUpdate={
|
||||
issueActions[EIssueActions.UPDATE]
|
||||
? async (data) => handleIssues(data, EIssueActions.UPDATE)
|
||||
: undefined
|
||||
}
|
||||
handleRemoveFromView={
|
||||
issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
displayProperties={displayProperties}
|
||||
states={states}
|
||||
stateGroups={stateGroups}
|
||||
priorities={priorities}
|
||||
labels={labels}
|
||||
members={members}
|
||||
projects={projects}
|
||||
issueIds={issueIds}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
viewId={viewId}
|
||||
quickAddCallback={issueStore?.quickAddIssue}
|
||||
enableIssueQuickAdd={!!enableQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={`relative h-full w-full bg-custom-background-90`}>
|
||||
<List
|
||||
issuesMap={issueMap}
|
||||
displayProperties={displayProperties}
|
||||
group_by={group_by}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={renderQuickActions}
|
||||
issueIds={issueIds}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
viewId={viewId}
|
||||
quickAddCallback={issues?.quickAddIssue}
|
||||
enableIssueQuickAdd={!!enableQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{workspaceSlug && peekIssueId && peekProjectId && (
|
||||
{/* {workspaceSlug && peekIssueId && peekProjectId && (
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={peekProjectId.toString()}
|
||||
issueId={peekIssueId.toString()}
|
||||
handleIssue={async (issueToUpdate, action: EIssueActions) =>
|
||||
await handleIssues(issueToUpdate as IIssue, action)
|
||||
}
|
||||
handleIssue={async (issueToUpdate) => await handleIssues(issueToUpdate as TIssue, EIssueActions.UPDATE)}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,54 +1,54 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { ListProperties } from "./properties";
|
||||
import { IssueProperties } from "../properties/all-properties";
|
||||
// ui
|
||||
import { Spinner, Tooltip } from "@plane/ui";
|
||||
// types
|
||||
import { IIssue, IIssueDisplayProperties } from "types";
|
||||
import { TIssue, IIssueDisplayProperties, TIssueMap } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { useProject } from "hooks/store";
|
||||
|
||||
interface IssueBlockProps {
|
||||
columnId: string;
|
||||
|
||||
issue: IIssue;
|
||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||
quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode;
|
||||
issueId: string;
|
||||
issuesMap: TIssueMap;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
}
|
||||
|
||||
export const IssueBlock: React.FC<IssueBlockProps> = (props) => {
|
||||
const { columnId, issue, handleIssues, quickActions, displayProperties, canEditProperties } = props;
|
||||
export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlockProps) => {
|
||||
const { issuesMap, issueId, handleIssues, quickActions, displayProperties, canEditProperties } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const updateIssue = (group_by: string | null, issueToUpdate: IIssue) => {
|
||||
const updateIssue = (issueToUpdate: TIssue) => {
|
||||
handleIssues(issueToUpdate, EIssueActions.UPDATE);
|
||||
};
|
||||
|
||||
const handleIssuePeekOverview = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
const issue = issuesMap[issueId];
|
||||
|
||||
if (!issue) return null;
|
||||
|
||||
const handleIssuePeekOverview = () => {
|
||||
const { query } = router;
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
const issueUrl = `/${issue.workspace_detail.slug}/projects/${issue.project_detail.id}/issues/${issue?.id}`;
|
||||
window.open(issueUrl, "_blank"); // Open link in a new tab
|
||||
} else {
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project },
|
||||
});
|
||||
}
|
||||
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project_id },
|
||||
});
|
||||
};
|
||||
|
||||
const canEditIssueProperties = canEditProperties(issue.project);
|
||||
const canEditIssueProperties = canEditProperties(issue.project_id);
|
||||
const { getProjectById } = useProject();
|
||||
const projectDetails = getProjectById(issue.project_id);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="relative flex items-center gap-3 bg-custom-background-100 p-3 text-sm w-full"
|
||||
onClick={handleIssuePeekOverview}
|
||||
>
|
||||
<div className="relative flex items-center gap-3 bg-custom-background-100 p-3 text-sm">
|
||||
{displayProperties && displayProperties?.key && (
|
||||
<div className="flex-shrink-0 text-xs font-medium text-custom-text-300">
|
||||
{issue?.project_detail?.identifier}-{issue.sequence_id}
|
||||
{projectDetails?.identifier}-{issue.sequence_id}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -56,7 +56,10 @@ export const IssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||
<div className="absolute left-0 top-0 z-[99999] h-full w-full animate-pulse bg-custom-background-100/20" />
|
||||
)}
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||
<div className="line-clamp-1 w-full cursor-pointer text-sm font-medium text-custom-text-100 text-left">
|
||||
<div
|
||||
className="line-clamp-1 w-full cursor-pointer text-sm font-medium text-custom-text-100"
|
||||
onClick={handleIssuePeekOverview}
|
||||
>
|
||||
{issue.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
|
@ -64,14 +67,14 @@ export const IssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||
<div className="ml-auto flex flex-shrink-0 items-center gap-2">
|
||||
{!issue?.tempId ? (
|
||||
<>
|
||||
<ListProperties
|
||||
columnId={columnId}
|
||||
<IssueProperties
|
||||
className="relative flex items-center gap-2 overflow-x-auto whitespace-nowrap"
|
||||
issue={issue}
|
||||
isReadonly={!canEditIssueProperties}
|
||||
isReadOnly={!canEditIssueProperties}
|
||||
handleIssues={updateIssue}
|
||||
displayProperties={displayProperties}
|
||||
/>
|
||||
{quickActions(!columnId && columnId === "null" ? null : columnId, issue)}
|
||||
{quickActions(issue)}
|
||||
</>
|
||||
) : (
|
||||
<div className="h-4 w-4">
|
||||
|
|
@ -79,7 +82,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,41 +2,39 @@ import { FC } from "react";
|
|||
// components
|
||||
import { IssueBlock } from "components/issues";
|
||||
// types
|
||||
import { IIssue, IIssueDisplayProperties } from "types";
|
||||
import { IIssueResponse, IGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
||||
import { TGroupedIssues, TIssue, IIssueDisplayProperties, TIssueMap, TUnGroupedIssues } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
|
||||
interface Props {
|
||||
columnId: string;
|
||||
issueIds: IGroupedIssues | TUnGroupedIssues | any;
|
||||
issues: IIssueResponse;
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||
issuesMap: TIssueMap;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||
quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
}
|
||||
|
||||
export const IssueBlocksList: FC<Props> = (props) => {
|
||||
const { columnId, issueIds, issues, handleIssues, quickActions, displayProperties, canEditProperties } = props;
|
||||
const { issueIds, issuesMap, handleIssues, quickActions, displayProperties, canEditProperties } = props;
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full divide-y-[0.5px] divide-custom-border-200">
|
||||
{issueIds && issueIds.length > 0 ? (
|
||||
issueIds.map(
|
||||
(issueId: string) =>
|
||||
issueId != undefined &&
|
||||
issues[issueId] && (
|
||||
<IssueBlock
|
||||
key={issues[issueId].id}
|
||||
columnId={columnId}
|
||||
issue={issues[issueId]}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
canEditProperties={canEditProperties}
|
||||
displayProperties={displayProperties}
|
||||
/>
|
||||
)
|
||||
)
|
||||
issueIds.map((issueId: string) => {
|
||||
if (!issueId) return null;
|
||||
|
||||
return (
|
||||
<IssueBlock
|
||||
key={issueId}
|
||||
issueId={issueId}
|
||||
issuesMap={issuesMap}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
canEditProperties={canEditProperties}
|
||||
displayProperties={displayProperties}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="bg-custom-background-100 p-3 text-sm text-custom-text-400">No issues</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,29 @@
|
|||
import React from "react";
|
||||
// components
|
||||
import { ListGroupByHeaderRoot } from "./headers/group-by-root";
|
||||
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
||||
// hooks
|
||||
import { useLabel, useMember, useProject, useProjectState } from "hooks/store";
|
||||
// types
|
||||
import { IIssue, IIssueDisplayProperties, IIssueLabel, IProject, IState, IUserLite } from "types";
|
||||
import { IIssueResponse, IGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
||||
import {
|
||||
GroupByColumnTypes,
|
||||
TGroupedIssues,
|
||||
TIssue,
|
||||
IIssueDisplayProperties,
|
||||
TIssueMap,
|
||||
TUnGroupedIssues,
|
||||
} from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
// constants
|
||||
import { getValueFromObject } from "constants/issue";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||
import { getGroupByColumns } from "../utils";
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
|
||||
export interface IGroupByList {
|
||||
issueIds: IGroupedIssues | TUnGroupedIssues | any;
|
||||
issues: any;
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||
issuesMap: TIssueMap;
|
||||
group_by: string | null;
|
||||
list: any;
|
||||
listKey: string;
|
||||
states: IState[] | null;
|
||||
is_list?: boolean;
|
||||
handleIssues: (issue: IIssue, action: EIssueActions) => Promise<void>;
|
||||
quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
enableIssueQuickAdd: boolean;
|
||||
showEmptyGroup?: boolean;
|
||||
|
|
@ -27,24 +31,21 @@ export interface IGroupByList {
|
|||
quickAddCallback?: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: IIssue,
|
||||
data: TIssue,
|
||||
viewId?: string
|
||||
) => Promise<IIssue | undefined>;
|
||||
) => Promise<TIssue | undefined>;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
currentStore: TCreateModalStoreTypes;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
viewId?: string;
|
||||
}
|
||||
|
||||
const GroupByList: React.FC<IGroupByList> = (props) => {
|
||||
const {
|
||||
issueIds,
|
||||
issues,
|
||||
issuesMap,
|
||||
group_by,
|
||||
list,
|
||||
listKey,
|
||||
is_list = false,
|
||||
states,
|
||||
handleIssues,
|
||||
quickActions,
|
||||
displayProperties,
|
||||
|
|
@ -57,17 +58,26 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||
currentStore,
|
||||
addIssuesToView,
|
||||
} = props;
|
||||
// store hooks
|
||||
const member = useMember();
|
||||
const project = useProject();
|
||||
const projectLabel = useLabel();
|
||||
const projectState = useProjectState();
|
||||
|
||||
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, projectLabel, projectState, member, true);
|
||||
|
||||
if (!list) return null;
|
||||
|
||||
const prePopulateQuickAddData = (groupByKey: string | null, value: any) => {
|
||||
const defaultState = states?.find((state) => state.default);
|
||||
if (groupByKey === null) return { state: defaultState?.id };
|
||||
const defaultState = projectState.projectStates?.find((state) => state.default);
|
||||
if (groupByKey === null) return { state_id: defaultState?.id };
|
||||
else {
|
||||
if (groupByKey === "state") return { state: groupByKey === "state" ? value : defaultState?.id };
|
||||
else return { state: defaultState?.id, [groupByKey]: value };
|
||||
else return { state_id: defaultState?.id, [groupByKey]: value };
|
||||
}
|
||||
};
|
||||
|
||||
const validateEmptyIssueGroups = (issues: IIssue[]) => {
|
||||
const validateEmptyIssueGroups = (issues: TIssue[]) => {
|
||||
const issuesCount = issues?.length || 0;
|
||||
if (!showEmptyGroup && issuesCount <= 0) return false;
|
||||
return true;
|
||||
|
|
@ -79,29 +89,24 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||
list.length > 0 &&
|
||||
list.map(
|
||||
(_list: any) =>
|
||||
validateEmptyIssueGroups(is_list ? issueIds : issueIds?.[getValueFromObject(_list, listKey) as string]) && (
|
||||
<div key={getValueFromObject(_list, listKey) as string} className={`flex flex-shrink-0 flex-col`}>
|
||||
validateEmptyIssueGroups(is_list ? issueIds : issueIds?.[_list.id]) && (
|
||||
<div key={_list.id} className={`flex flex-shrink-0 flex-col`}>
|
||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-3 py-1">
|
||||
<ListGroupByHeaderRoot
|
||||
column_id={getValueFromObject(_list, listKey) as string}
|
||||
column_value={_list}
|
||||
group_by={group_by}
|
||||
issues_count={
|
||||
is_list
|
||||
? issueIds?.length || 0
|
||||
: issueIds?.[getValueFromObject(_list, listKey) as string]?.length || 0
|
||||
}
|
||||
<HeaderGroupByCard
|
||||
icon={_list.icon}
|
||||
title={_list.name || ""}
|
||||
count={is_list ? issueIds?.length || 0 : issueIds?.[_list.id]?.length || 0}
|
||||
issuePayload={_list.payload}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{issues && (
|
||||
{issueIds && (
|
||||
<IssueBlocksList
|
||||
columnId={getValueFromObject(_list, listKey) as string}
|
||||
issueIds={is_list ? issueIds || 0 : issueIds?.[getValueFromObject(_list, listKey) as string] || 0}
|
||||
issues={issues}
|
||||
issueIds={is_list ? issueIds || 0 : issueIds?.[_list.id] || 0}
|
||||
issuesMap={issuesMap}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
|
|
@ -112,7 +117,7 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||
{enableIssueQuickAdd && !disableIssueCreation && (
|
||||
<div className="sticky bottom-0 z-[1] w-full flex-shrink-0">
|
||||
<ListQuickAddIssueForm
|
||||
prePopulatedData={prePopulateQuickAddData(group_by, getValueFromObject(_list, listKey))}
|
||||
prePopulatedData={prePopulateQuickAddData(group_by, _list.id)}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
/>
|
||||
|
|
@ -126,37 +131,31 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||
};
|
||||
|
||||
export interface IList {
|
||||
issueIds: IGroupedIssues | TUnGroupedIssues | any;
|
||||
issues: IIssueResponse | undefined;
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||
issuesMap: TIssueMap;
|
||||
group_by: string | null;
|
||||
handleIssues: (issue: IIssue, action: EIssueActions) => Promise<void>;
|
||||
quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
showEmptyGroup: boolean;
|
||||
enableIssueQuickAdd: boolean;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
states: IState[] | null;
|
||||
labels: IIssueLabel[] | null;
|
||||
members: IUserLite[] | null;
|
||||
projects: IProject[] | null;
|
||||
stateGroups: any;
|
||||
priorities: any;
|
||||
quickAddCallback?: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: IIssue,
|
||||
data: TIssue,
|
||||
viewId?: string
|
||||
) => Promise<IIssue | undefined>;
|
||||
) => Promise<TIssue | undefined>;
|
||||
viewId?: string;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
currentStore: TCreateModalStoreTypes;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
}
|
||||
|
||||
export const List: React.FC<IList> = (props) => {
|
||||
const {
|
||||
issueIds,
|
||||
issues,
|
||||
issuesMap,
|
||||
group_by,
|
||||
handleIssues,
|
||||
quickActions,
|
||||
|
|
@ -167,194 +166,28 @@ export const List: React.FC<IList> = (props) => {
|
|||
enableIssueQuickAdd,
|
||||
canEditProperties,
|
||||
disableIssueCreation,
|
||||
states,
|
||||
stateGroups,
|
||||
priorities,
|
||||
labels,
|
||||
members,
|
||||
projects,
|
||||
currentStore,
|
||||
addIssuesToView,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full">
|
||||
{group_by === null && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as TUnGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={[{ id: `null`, title: `All Issues` }]}
|
||||
listKey={`id`}
|
||||
is_list={true}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "project" && projects && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={projects}
|
||||
listKey={`id`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "state" && states && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={states}
|
||||
listKey={`id`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "state_detail.group" && stateGroups && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={stateGroups}
|
||||
listKey={`key`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "priority" && priorities && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={priorities}
|
||||
listKey={`key`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "labels" && labels && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={[...labels, { id: "None", name: "None" }]}
|
||||
listKey={`id`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "assignees" && members && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={[...members, { id: "None", display_name: "None" }]}
|
||||
listKey={`id`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "created_by" && members && (
|
||||
<GroupByList
|
||||
issueIds={issueIds as IGroupedIssues}
|
||||
issues={issues}
|
||||
group_by={group_by}
|
||||
list={members}
|
||||
listKey={`id`}
|
||||
states={states}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
<GroupByList
|
||||
issueIds={issueIds as TUnGroupedIssues}
|
||||
issuesMap={issuesMap}
|
||||
group_by={group_by}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
// ui
|
||||
import { Avatar } from "@plane/ui";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IAssigneesHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="md" />;
|
||||
|
||||
export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const assignee = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{assignee && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon user={assignee} />}
|
||||
title={assignee?.display_name || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{ assignees: [assignee?.member?.id] }}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
import { Icon } from "./assignee";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface ICreatedByHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const createdBy = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{createdBy && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon user={createdBy} />}
|
||||
title={createdBy?.display_name || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{ created_by: createdBy?.member?.id }}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IEmptyHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
export const EmptyHeader: React.FC<IEmptyHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
return (
|
||||
<HeaderGroupByCard
|
||||
title={column_value?.title || "All Issues"}
|
||||
count={issues_count}
|
||||
issuePayload={{}}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
// lucide icons
|
||||
import { CircleDashed, Plus } from "lucide-react";
|
||||
|
|
@ -10,18 +9,19 @@ import { CustomMenu } from "@plane/ui";
|
|||
// mobx
|
||||
import { observer } from "mobx-react-lite";
|
||||
// types
|
||||
import { IIssue, ISearchIssueResponse } from "types";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { TIssue, ISearchIssueResponse } from "@plane/types";
|
||||
import useToast from "hooks/use-toast";
|
||||
import { useState } from "react";
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
|
||||
interface IHeaderGroupByCard {
|
||||
icon?: React.ReactNode;
|
||||
title: string;
|
||||
count: number;
|
||||
issuePayload: Partial<IIssue>;
|
||||
issuePayload: Partial<TIssue>;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
currentStore: TCreateModalStoreTypes;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
}
|
||||
|
||||
export const HeaderGroupByCard = observer(
|
||||
|
|
@ -29,9 +29,9 @@ export const HeaderGroupByCard = observer(
|
|||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, moduleId, cycleId } = router.query;
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const [openExistingIssueListModal, setOpenExistingIssueListModal] = React.useState(false);
|
||||
const [openExistingIssueListModal, setOpenExistingIssueListModal] = useState(false);
|
||||
|
||||
const isDraftIssue = router.pathname.includes("draft-issue");
|
||||
|
||||
|
|
@ -45,14 +45,15 @@ export const HeaderGroupByCard = observer(
|
|||
|
||||
const issues = data.map((i) => i.id);
|
||||
|
||||
addIssuesToView &&
|
||||
addIssuesToView(issues)?.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Selected issues could not be added to the cycle. Please try again.",
|
||||
});
|
||||
try {
|
||||
addIssuesToView && addIssuesToView(issues);
|
||||
} catch (error) {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Selected issues could not be added to the cycle. Please try again.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
// components
|
||||
import { EmptyHeader } from "./empty-group";
|
||||
import { ProjectHeader } from "./project";
|
||||
import { StateHeader } from "./state";
|
||||
import { StateGroupHeader } from "./state-group";
|
||||
import { AssigneesHeader } from "./assignee";
|
||||
import { PriorityHeader } from "./priority";
|
||||
import { LabelHeader } from "./label";
|
||||
import { CreatedByHeader } from "./created-by";
|
||||
// mobx
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IListGroupByHeaderRoot {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
group_by: string | null;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
export const ListGroupByHeaderRoot: React.FC<IListGroupByHeaderRoot> = observer((props) => {
|
||||
const { column_id, column_value, group_by, issues_count, disableIssueCreation, currentStore, addIssuesToView } =
|
||||
props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!group_by && group_by === null && (
|
||||
<EmptyHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
{group_by && group_by === "project" && (
|
||||
<ProjectHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{group_by && group_by === "state" && (
|
||||
<StateHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
{group_by && group_by === "state_detail.group" && (
|
||||
<StateGroupHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
{group_by && group_by === "priority" && (
|
||||
<PriorityHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
{group_by && group_by === "labels" && (
|
||||
<LabelHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
{group_by && group_by === "assignees" && (
|
||||
<AssigneesHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
{group_by && group_by === "created_by" && (
|
||||
<CreatedByHeader
|
||||
column_id={column_id}
|
||||
column_value={column_value}
|
||||
issues_count={issues_count}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface ILabelHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
const Icon = ({ color }: any) => (
|
||||
<div className="h-[12px] w-[12px] rounded-full" style={{ backgroundColor: color ? color : "#666" }} />
|
||||
);
|
||||
|
||||
export const LabelHeader: FC<ILabelHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const label = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{column_value && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon color={label?.color || null} />}
|
||||
title={column_value?.name || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{ labels: [label.id] }}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { AlertCircle, SignalHigh, SignalMedium, SignalLow, Ban } from "lucide-react";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IPriorityHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
const Icon = ({ priority }: any) => (
|
||||
<div className="h-full w-full">
|
||||
{priority === "urgent" ? (
|
||||
<div className="flex h-full w-full items-center justify-center overflow-hidden rounded-sm border border-red-500 bg-red-500 text-white">
|
||||
<AlertCircle size={14} strokeWidth={2} />
|
||||
</div>
|
||||
) : priority === "high" ? (
|
||||
<div className="flex h-full w-full items-center justify-center overflow-hidden rounded-sm border border-red-500/20 bg-red-500/10 text-red-500">
|
||||
<SignalHigh size={14} strokeWidth={2} className="pl-[3px]" />
|
||||
</div>
|
||||
) : priority === "medium" ? (
|
||||
<div className="flex h-full w-full items-center justify-center overflow-hidden rounded-sm border border-orange-500/20 bg-orange-500/10 text-orange-500">
|
||||
<SignalMedium size={14} strokeWidth={2} className="pl-[3px]" />
|
||||
</div>
|
||||
) : priority === "low" ? (
|
||||
<div className="flex h-full w-full items-center justify-center overflow-hidden rounded-sm border border-green-500/20 bg-green-500/10 text-green-500">
|
||||
<SignalLow size={14} strokeWidth={2} className="pl-[3px]" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center overflow-hidden rounded-sm border border-custom-border-400/20 bg-custom-text-400/10 text-custom-text-400">
|
||||
<Ban size={14} strokeWidth={2} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const priority = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{priority && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon priority={priority?.key} />}
|
||||
title={priority?.title || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{ priority: priority?.key }}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
// emoji helper
|
||||
import { renderEmoji } from "helpers/emoji.helper";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IProjectHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
const Icon = ({ emoji }: any) => <div className="h-6 w-6">{renderEmoji(emoji)}</div>;
|
||||
|
||||
export const ProjectHeader: FC<IProjectHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const project = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{project && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon emoji={project?.emoji} />}
|
||||
title={project?.name || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{ project: project?.id ?? "" }}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
// ui
|
||||
import { StateGroupIcon } from "@plane/ui";
|
||||
// helpers
|
||||
import { capitalizeFirstLetter } from "helpers/string.helper";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IStateGroupHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => (
|
||||
<div className="h-[14px] w-[14px] rounded-full">
|
||||
<StateGroupIcon stateGroup={stateGroup} color={color || null} width="14" height="14" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const StateGroupHeader: FC<IStateGroupHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const stateGroup = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{stateGroup && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon stateGroup={stateGroup?.key} />}
|
||||
title={capitalizeFirstLetter(stateGroup?.key) || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{}}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./group-by-card";
|
||||
import { Icon } from "./state-group";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export interface IStateHeader {
|
||||
column_id: string;
|
||||
column_value: any;
|
||||
issues_count: number;
|
||||
disableIssueCreation?: boolean;
|
||||
currentStore: EProjectStore;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
export const StateHeader: FC<IStateHeader> = observer((props) => {
|
||||
const { column_value, issues_count, disableIssueCreation, currentStore, addIssuesToView } = props;
|
||||
|
||||
const state = column_value ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{state && (
|
||||
<HeaderGroupByCard
|
||||
icon={<Icon stateGroup={state?.group} color={state?.color} />}
|
||||
title={state?.name || ""}
|
||||
count={issues_count}
|
||||
issuePayload={{ state: state?.id }}
|
||||
disableIssueCreation={disableIssueCreation}
|
||||
currentStore={currentStore}
|
||||
addIssuesToView={addIssuesToView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
export interface IQuickActionProps {
|
||||
issue: IIssue;
|
||||
issue: TIssue;
|
||||
handleDelete: () => Promise<void>;
|
||||
handleUpdate?: (data: IIssue) => Promise<void>;
|
||||
handleUpdate?: (data: TIssue) => Promise<void>;
|
||||
handleRemoveFromView?: () => Promise<void>;
|
||||
customActionButton?: React.ReactElement;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,168 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Layers, Link, Paperclip } from "lucide-react";
|
||||
// components
|
||||
import { IssuePropertyState } from "../properties/state";
|
||||
import { IssuePropertyPriority } from "../properties/priority";
|
||||
import { IssuePropertyLabels } from "../properties/labels";
|
||||
import { IssuePropertyAssignee } from "../properties/assignee";
|
||||
import { IssuePropertyEstimates } from "../properties/estimates";
|
||||
import { IssuePropertyDate } from "../properties/date";
|
||||
// ui
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// types
|
||||
import { IIssue, IIssueDisplayProperties, IState, TIssuePriorities } from "types";
|
||||
|
||||
export interface IListProperties {
|
||||
columnId: string;
|
||||
issue: IIssue;
|
||||
handleIssues: (group_by: string | null, issue: IIssue) => void;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
isReadonly?: boolean;
|
||||
}
|
||||
|
||||
export const ListProperties: FC<IListProperties> = observer((props) => {
|
||||
const { columnId: group_id, issue, handleIssues, displayProperties, isReadonly } = props;
|
||||
|
||||
const handleState = (state: IState) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, state: state.id });
|
||||
};
|
||||
|
||||
const handlePriority = (value: TIssuePriorities) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, priority: value });
|
||||
};
|
||||
|
||||
const handleLabel = (ids: string[]) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, labels: ids });
|
||||
};
|
||||
|
||||
const handleAssignee = (ids: string[]) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, assignees: ids });
|
||||
};
|
||||
|
||||
const handleStartDate = (date: string | null) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, start_date: date });
|
||||
};
|
||||
|
||||
const handleTargetDate = (date: string | null) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, target_date: date });
|
||||
};
|
||||
|
||||
const handleEstimate = (value: number | null) => {
|
||||
handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, estimate_point: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center gap-2 overflow-x-auto whitespace-nowrap">
|
||||
{/* basic properties */}
|
||||
{/* state */}
|
||||
{displayProperties && displayProperties?.state && (
|
||||
<IssuePropertyState
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.state || null}
|
||||
defaultOptions={issue?.state_detail ? [issue.state_detail] : []}
|
||||
hideDropdownArrow
|
||||
onChange={handleState}
|
||||
disabled={isReadonly}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* priority */}
|
||||
{displayProperties && displayProperties?.priority && (
|
||||
<IssuePropertyPriority
|
||||
value={issue?.priority || null}
|
||||
onChange={handlePriority}
|
||||
disabled={isReadonly}
|
||||
hideDropdownArrow
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* label */}
|
||||
{displayProperties && displayProperties?.labels && (
|
||||
<IssuePropertyLabels
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.labels || null}
|
||||
defaultOptions={issue?.label_details ? issue.label_details : []}
|
||||
onChange={handleLabel}
|
||||
disabled={isReadonly}
|
||||
hideDropdownArrow
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* assignee */}
|
||||
{displayProperties && displayProperties?.assignee && (
|
||||
<IssuePropertyAssignee
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.assignees || null}
|
||||
defaultOptions={issue?.assignee_details ? issue.assignee_details : []}
|
||||
hideDropdownArrow
|
||||
onChange={handleAssignee}
|
||||
disabled={isReadonly}
|
||||
multiple
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* start date */}
|
||||
{displayProperties && displayProperties?.start_date && (
|
||||
<IssuePropertyDate
|
||||
value={issue?.start_date || null}
|
||||
onChange={(date) => handleStartDate(date)}
|
||||
disabled={isReadonly}
|
||||
type="start_date"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* target/due date */}
|
||||
{displayProperties && displayProperties?.due_date && (
|
||||
<IssuePropertyDate
|
||||
value={issue?.target_date || null}
|
||||
onChange={(date) => handleTargetDate(date)}
|
||||
disabled={isReadonly}
|
||||
type="target_date"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* estimates */}
|
||||
{displayProperties && displayProperties?.estimate && (
|
||||
<IssuePropertyEstimates
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.estimate_point || null}
|
||||
hideDropdownArrow
|
||||
onChange={handleEstimate}
|
||||
disabled={isReadonly}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* extra render properties */}
|
||||
{/* sub-issues */}
|
||||
{displayProperties && displayProperties?.sub_issue_count && !!issue?.sub_issues_count && (
|
||||
<Tooltip tooltipHeading="Sub-issues" tooltipContent={`${issue.sub_issues_count}`}>
|
||||
<div className="flex h-5 flex-shrink-0 cursor-default items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
||||
<Layers className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||
<div className="text-xs">{issue.sub_issues_count}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* attachments */}
|
||||
{displayProperties && displayProperties?.attachment_count && !!issue?.attachment_count && (
|
||||
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
|
||||
<div className="flex h-5 flex-shrink-0 cursor-default items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
||||
<Paperclip className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||
<div className="text-xs">{issue.attachment_count}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* link */}
|
||||
{displayProperties && displayProperties?.link && !!issue?.link_count && (
|
||||
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
|
||||
<div className="flex h-5 flex-shrink-0 cursor-default items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
||||
<Link className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||
<div className="text-xs">{issue.link_count}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
@ -4,13 +4,12 @@ import { useForm } from "react-hook-form";
|
|||
import { PlusIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useProject, useWorkspace } from "hooks/store";
|
||||
import useToast from "hooks/use-toast";
|
||||
import useKeypress from "hooks/use-keypress";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
// store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// constants
|
||||
import { IIssue, IProject } from "types";
|
||||
import { TIssue, IProject } from "@plane/types";
|
||||
// types
|
||||
import { createIssuePayload } from "helpers/issue.helper";
|
||||
|
||||
|
|
@ -44,31 +43,28 @@ const Inputs: FC<IInputProps> = (props) => {
|
|||
};
|
||||
|
||||
interface IListQuickAddIssueForm {
|
||||
prePopulatedData?: Partial<IIssue>;
|
||||
prePopulatedData?: Partial<TIssue>;
|
||||
quickAddCallback?: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: IIssue,
|
||||
data: TIssue,
|
||||
viewId?: string
|
||||
) => Promise<IIssue | undefined>;
|
||||
) => Promise<TIssue | undefined>;
|
||||
viewId?: string;
|
||||
}
|
||||
|
||||
const defaultValues: Partial<IIssue> = {
|
||||
const defaultValues: Partial<TIssue> = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
export const ListQuickAddIssueForm: FC<IListQuickAddIssueForm> = observer((props) => {
|
||||
const { prePopulatedData, quickAddCallback, viewId } = props;
|
||||
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const { workspace: workspaceStore, project: projectStore } = useMobxStore();
|
||||
|
||||
const workspaceDetail = (workspaceSlug && workspaceStore.getWorkspaceBySlug(workspaceSlug)) || null;
|
||||
const projectDetail: IProject | null =
|
||||
(workspaceSlug && projectId && projectStore.getProjectById(workspaceSlug, projectId)) || null;
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// store hooks
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProjectDetails } = useProject();
|
||||
|
||||
const ref = useRef<HTMLFormElement>(null);
|
||||
|
||||
|
|
@ -85,24 +81,25 @@ export const ListQuickAddIssueForm: FC<IListQuickAddIssueForm> = observer((props
|
|||
setFocus,
|
||||
register,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<IIssue>({ defaultValues });
|
||||
} = useForm<TIssue>({ defaultValues });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) reset({ ...defaultValues });
|
||||
}, [isOpen, reset]);
|
||||
|
||||
const onSubmitHandler = async (formData: IIssue) => {
|
||||
if (isSubmitting || !workspaceDetail || !projectDetail) return;
|
||||
const onSubmitHandler = async (formData: TIssue) => {
|
||||
if (isSubmitting || !currentWorkspace || !currentProjectDetails || !workspaceSlug || !projectId) return;
|
||||
|
||||
reset({ ...defaultValues });
|
||||
|
||||
const payload = createIssuePayload(workspaceDetail, projectDetail, {
|
||||
const payload = createIssuePayload(currentWorkspace, currentProjectDetails, {
|
||||
...(prePopulatedData ?? {}),
|
||||
...formData,
|
||||
});
|
||||
|
||||
try {
|
||||
quickAddCallback && (await quickAddCallback(workspaceSlug, projectId, { ...payload }, viewId));
|
||||
quickAddCallback &&
|
||||
(await quickAddCallback(workspaceSlug.toString(), projectId.toString(), { ...payload }, viewId));
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
|
|
@ -130,7 +127,12 @@ export const ListQuickAddIssueForm: FC<IListQuickAddIssueForm> = observer((props
|
|||
onSubmit={handleSubmit(onSubmitHandler)}
|
||||
className="flex w-full items-center gap-x-3 border-[0.5px] border-t-0 border-custom-border-100 bg-custom-background-100 px-3"
|
||||
>
|
||||
<Inputs formKey={"name"} register={register} setFocus={setFocus} projectDetail={projectDetail} />
|
||||
<Inputs
|
||||
formKey={"name"}
|
||||
register={register}
|
||||
setFocus={setFocus}
|
||||
projectDetail={currentProjectDetails ?? null}
|
||||
/>
|
||||
</form>
|
||||
<div className="px-3 py-2 text-xs italic text-custom-text-200">{`Press 'Enter' to add another issue`}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,46 +1,40 @@
|
|||
import { FC } from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { ArchivedIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export const ArchivedIssueListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const { projectArchivedIssues: archivedIssueStore, projectArchivedIssuesFilter: archivedIssueFiltersStore } =
|
||||
useMobxStore();
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await archivedIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
};
|
||||
|
||||
const getProjects = (projectStore: IProjectStore) => {
|
||||
if (!workspaceSlug) return null;
|
||||
return projectStore?.projects[workspaceSlug.toString()] || null;
|
||||
};
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, projectId]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={archivedIssueFiltersStore}
|
||||
issueStore={archivedIssueStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ArchivedIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
currentStore={EProjectStore.PROJECT}
|
||||
currentStore={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,65 +1,58 @@
|
|||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// hooks
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { CycleIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export interface ICycleListLayout {}
|
||||
|
||||
export const CycleListLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
// store
|
||||
const {
|
||||
cycleIssues: cycleIssueStore,
|
||||
cycleIssuesFilter: cycleIssueFilterStore,
|
||||
cycle: { fetchCycleWithId },
|
||||
} = useMobxStore();
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await cycleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, cycleId);
|
||||
fetchCycleWithId(workspaceSlug, issue.project, cycleId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await cycleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, cycleId);
|
||||
fetchCycleWithId(workspaceSlug, issue.project, cycleId);
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
||||
fetchCycleWithId(workspaceSlug, issue.project, cycleId);
|
||||
},
|
||||
};
|
||||
const getProjects = (projectStore: IProjectStore) => {
|
||||
if (!workspaceSlug) return null;
|
||||
return projectStore?.projects[workspaceSlug] || null;
|
||||
};
|
||||
await issues.removeIssueFromCycle(workspaceSlug.toString(), issue.project_id, cycleId.toString(), issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, cycleId]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={cycleIssueFilterStore}
|
||||
issueStore={cycleIssueStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
viewId={cycleId}
|
||||
currentStore={EProjectStore.CYCLE}
|
||||
addIssuesToView={(issues: string[]) => cycleIssueStore.addIssueToCycle(workspaceSlug, cycleId, issues)}
|
||||
viewId={cycleId?.toString()}
|
||||
currentStore={EIssuesStoreType.CYCLE}
|
||||
addIssuesToView={(issueIds: string[]) => {
|
||||
if (!workspaceSlug || !projectId || !cycleId) throw new Error();
|
||||
return issues.addIssueToCycle(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), issueIds);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import { FC } from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export const DraftIssueListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
|
|
@ -20,31 +19,31 @@ export const DraftIssueListLayout: FC = observer(() => {
|
|||
if (!workspaceSlug || !projectId) return null;
|
||||
|
||||
// store
|
||||
const { projectDraftIssuesFilter: projectIssuesFilterStore, projectDraftIssues: projectIssuesStore } = useMobxStore();
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
await issues.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
};
|
||||
|
||||
const getProjects = (projectStore: IProjectStore) => projectStore.workspaceProjects;
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, projectId]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={projectIssuesFilterStore}
|
||||
issueStore={projectIssuesStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
currentStore={EProjectStore.PROJECT}
|
||||
currentStore={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,66 +1,58 @@
|
|||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { ModuleIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export interface IModuleListLayout {}
|
||||
|
||||
export const ModuleListLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||
|
||||
const {
|
||||
moduleIssues: moduleIssueStore,
|
||||
moduleIssuesFilter: moduleIssueFilterStore,
|
||||
module: { fetchModuleDetails },
|
||||
} = useMobxStore();
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await moduleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, moduleId);
|
||||
fetchModuleDetails(workspaceSlug, issue.project, moduleId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
||||
fetchModuleDetails(workspaceSlug, issue.project, moduleId);
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id);
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
||||
fetchModuleDetails(workspaceSlug, issue.project, moduleId);
|
||||
},
|
||||
};
|
||||
|
||||
const getProjects = (projectStore: IProjectStore) => {
|
||||
if (!workspaceSlug) return null;
|
||||
return projectStore?.projects[workspaceSlug] || null;
|
||||
};
|
||||
await issues.removeIssueFromModule(workspaceSlug.toString(), issue.project_id, moduleId.toString(), issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, moduleId]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={moduleIssueFilterStore}
|
||||
issueStore={moduleIssueStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
viewId={moduleId}
|
||||
currentStore={EProjectStore.MODULE}
|
||||
addIssuesToView={(issues: string[]) => moduleIssueStore.addIssueToModule(workspaceSlug, moduleId, issues)}
|
||||
viewId={moduleId?.toString()}
|
||||
currentStore={EIssuesStoreType.MODULE}
|
||||
addIssuesToView={(issueIds: string[]) => {
|
||||
if (!workspaceSlug || !projectId || !moduleId) throw new Error();
|
||||
return issues.addIssueToModule(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), issueIds);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,64 +1,58 @@
|
|||
import { FC } from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
// components
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export const ProfileIssuesListLayout: FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, userId } = router.query as { workspaceSlug: string; userId: string };
|
||||
// store hooks
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROFILE);
|
||||
|
||||
// store
|
||||
const {
|
||||
workspaceProfileIssuesFilter: profileIssueFiltersStore,
|
||||
workspaceProfileIssues: profileIssuesStore,
|
||||
workspaceMember: { currentWorkspaceUserProjectsRole },
|
||||
} = useMobxStore();
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await profileIssuesStore.updateIssue(workspaceSlug, userId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
await issues.updateIssue(workspaceSlug, userId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await profileIssuesStore.removeIssue(workspaceSlug, issue.project, issue.id, userId);
|
||||
},
|
||||
};
|
||||
|
||||
const getProjects = (projectStore: IProjectStore) => projectStore.workspaceProjects;
|
||||
await issues.removeIssue(workspaceSlug, issue.project_id, issue.id, userId);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, userId]
|
||||
);
|
||||
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) => {
|
||||
const currentProjectRole = currentWorkspaceUserProjectsRole && currentWorkspaceUserProjectsRole[projectId];
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
|
||||
|
||||
console.log(
|
||||
projectId,
|
||||
currentWorkspaceUserProjectsRole,
|
||||
!!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER
|
||||
);
|
||||
return !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={profileIssueFiltersStore}
|
||||
issueStore={profileIssuesStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
currentStore={EProjectStore.PROFILE}
|
||||
currentStore={EIssuesStoreType.PROFILE}
|
||||
canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import { FC } from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export const ListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
|
|
@ -20,31 +19,32 @@ export const ListLayout: FC = observer(() => {
|
|||
if (!workspaceSlug || !projectId) return null;
|
||||
|
||||
// store
|
||||
const { projectIssuesFilter: projectIssuesFilterStore, projectIssues: projectIssuesStore } = useMobxStore();
|
||||
const { issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
await issues.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
};
|
||||
|
||||
const getProjects = (projectStore: IProjectStore) => projectStore.workspaceProjects;
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[issues]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={projectIssuesFilterStore}
|
||||
issueStore={projectIssuesStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
currentStore={EProjectStore.PROJECT}
|
||||
currentStore={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,53 +1,50 @@
|
|||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { RootStore } from "store/root";
|
||||
import { useIssues } from "hooks/store";
|
||||
// constants
|
||||
import { useRouter } from "next/router";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { IProjectStore } from "store/project";
|
||||
import { IIssue } from "types";
|
||||
import { TIssue } from "@plane/types";
|
||||
// components
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export interface IViewListLayout {}
|
||||
|
||||
export const ProjectViewListLayout: React.FC = observer(() => {
|
||||
const { viewIssues: projectViewIssueStore, viewIssuesFilter: projectViewIssueFilterStore }: RootStore =
|
||||
useMobxStore();
|
||||
// store
|
||||
const { issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
if (!workspaceSlug || !projectId) return null;
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await projectViewIssueStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
await issues.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await projectViewIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
};
|
||||
|
||||
const getProjects = (projectStore: IProjectStore) => projectStore.workspaceProjects;
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, projectId]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issueFilterStore={projectViewIssueFilterStore}
|
||||
issueStore={projectViewIssueStore}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
getProjects={getProjects}
|
||||
currentStore={EProjectStore.PROJECT_VIEW}
|
||||
currentStore={EIssuesStoreType.PROJECT_VIEW}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue