From 43c75f4457d3dd8fe7756aeeccd493f091cbf228 Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Thu, 14 Mar 2024 12:43:29 +0530 Subject: [PATCH 01/71] fix: rendering Issue in kanban swimlanes view for unassigned cycle or Modules (#3965) --- web/store/issue/helpers/issue-helper.store.ts | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/web/store/issue/helpers/issue-helper.store.ts b/web/store/issue/helpers/issue-helper.store.ts index a267ac9c8..563fe9bb5 100644 --- a/web/store/issue/helpers/issue-helper.store.ts +++ b/web/store/issue/helpers/issue-helper.store.ts @@ -1,16 +1,17 @@ -import orderBy from "lodash/orderBy"; import get from "lodash/get"; import indexOf from "lodash/indexOf"; import isEmpty from "lodash/isEmpty"; +import orderBy from "lodash/orderBy"; import values from "lodash/values"; -// types -import { TIssue, TIssueMap, TIssueGroupByOptions, TIssueOrderByOptions } from "@plane/types"; -import { IIssueRootStore } from "../root.store"; // constants import { ISSUE_PRIORITIES } from "constants/issue"; import { STATE_GROUPS } from "constants/state"; // helpers import { renderFormattedPayloadDate } from "helpers/date-time.helper"; +// types +import { TIssue, TIssueMap, TIssueGroupByOptions, TIssueOrderByOptions } from "@plane/types"; +// store +import { IIssueRootStore } from "../root.store"; export type TIssueDisplayFilterOptions = Exclude | "target_date"; @@ -62,35 +63,35 @@ export class IssueHelperStore implements TIssueHelperStore { issues: TIssueMap, isCalendarIssues: boolean = false ) => { - const _issues: { [group_id: string]: string[] } = {}; - if (!groupBy) return _issues; + const currentIssues: { [group_id: string]: string[] } = {}; + if (!groupBy) return currentIssues; this.issueDisplayFiltersDefaultData(groupBy).forEach((group) => { - _issues[group] = []; + currentIssues[group] = []; }); const projectIssues = this.issuesSortWithOrderBy(issues, orderBy); for (const issue in projectIssues) { - const _issue = projectIssues[issue]; + const currentIssue = projectIssues[issue]; let groupArray = []; if (groupBy === "state_detail.group") { - const state_group = - this.rootStore?.stateDetails?.find((_state) => _state.id === _issue?.state_id)?.group || "None"; + // if groupBy state_detail.group is coming from the project level the we are using stateDetails from root store else we are looping through the stateMap + const state_group = (this.rootStore?.stateMap || {})?.[currentIssue?.state_id]?.group || "None"; groupArray = [state_group]; } else { - const groupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[groupBy]); - groupArray = groupValue !== undefined ? this.getGroupArray(groupValue, isCalendarIssues) : []; + const groupValue = get(currentIssue, ISSUE_FILTER_DEFAULT_DATA[groupBy]); + groupArray = groupValue !== undefined ? this.getGroupArray(groupValue, isCalendarIssues) : ["None"]; } for (const group of groupArray) { - if (group && _issues[group]) _issues[group].push(_issue.id); - else if (group) _issues[group] = [_issue.id]; + if (group && currentIssues[group]) currentIssues[group].push(currentIssue.id); + else if (group) currentIssues[group] = [currentIssue.id]; } } - return _issues; + return currentIssues; }; subGroupedIssues = ( @@ -99,45 +100,47 @@ export class IssueHelperStore implements TIssueHelperStore { orderBy: TIssueOrderByOptions, issues: TIssueMap ) => { - const _issues: { [sub_group_id: string]: { [group_id: string]: string[] } } = {}; - if (!subGroupBy || !groupBy) return _issues; + const currentIssues: { [sub_group_id: string]: { [group_id: string]: string[] } } = {}; + if (!subGroupBy || !groupBy) return currentIssues; - this.issueDisplayFiltersDefaultData(subGroupBy).forEach((sub_group: any) => { + this.issueDisplayFiltersDefaultData(subGroupBy).forEach((sub_group) => { const groupByIssues: { [group_id: string]: string[] } = {}; this.issueDisplayFiltersDefaultData(groupBy).forEach((group) => { groupByIssues[group] = []; }); - _issues[sub_group] = groupByIssues; + currentIssues[sub_group] = groupByIssues; }); const projectIssues = this.issuesSortWithOrderBy(issues, orderBy); for (const issue in projectIssues) { - const _issue = projectIssues[issue]; + const currentIssue = projectIssues[issue]; let subGroupArray = []; let groupArray = []; if (subGroupBy === "state_detail.group" || groupBy === "state_detail.group") { - const state_group = - this.rootStore?.stateDetails?.find((_state) => _state.id === _issue?.state_id)?.group || "None"; + const state_group = (this.rootStore?.stateMap || {})?.[currentIssue?.state_id]?.group || "None"; + subGroupArray = [state_group]; groupArray = [state_group]; } else { - const subGroupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[subGroupBy]); - const groupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[groupBy]); - subGroupArray = subGroupValue != undefined ? this.getGroupArray(subGroupValue) : []; - groupArray = groupValue != undefined ? this.getGroupArray(groupValue) : []; + const subGroupValue = get(currentIssue, ISSUE_FILTER_DEFAULT_DATA[subGroupBy]); + const groupValue = get(currentIssue, ISSUE_FILTER_DEFAULT_DATA[groupBy]); + + subGroupArray = subGroupValue != undefined ? this.getGroupArray(subGroupValue) : ["None"]; + groupArray = groupValue != undefined ? this.getGroupArray(groupValue) : ["None"]; } for (const subGroup of subGroupArray) { for (const group of groupArray) { - if (subGroup && group && _issues?.[subGroup]?.[group]) _issues[subGroup][group].push(_issue.id); - else if (subGroup && group && _issues[subGroup]) _issues[subGroup][group] = [_issue.id]; - else if (subGroup && group) _issues[subGroup] = { [group]: [_issue.id] }; + if (subGroup && group && currentIssues?.[subGroup]?.[group]) + currentIssues[subGroup][group].push(currentIssue.id); + else if (subGroup && group && currentIssues[subGroup]) currentIssues[subGroup][group] = [currentIssue.id]; + else if (subGroup && group) currentIssues[subGroup] = { [group]: [currentIssue.id] }; } } } - return _issues; + return currentIssues; }; unGroupedIssues = (orderBy: TIssueOrderByOptions, issues: TIssueMap) => @@ -215,8 +218,8 @@ export class IssueHelperStore implements TIssueHelperStore { const moduleMap = this.rootStore?.moduleMap; if (!moduleMap) break; for (const dataId of dataIdsArray) { - const _module = moduleMap[dataId]; - if (_module && _module.name) dataValues.push(_module.name.toLocaleLowerCase()); + const currentModule = moduleMap[dataId]; + if (currentModule && currentModule.name) dataValues.push(currentModule.name.toLocaleLowerCase()); } break; case "cycle_id": @@ -233,10 +236,10 @@ export class IssueHelperStore implements TIssueHelperStore { } /** - * This Method is mainly used to filter out empty values in the begining + * This Method is mainly used to filter out empty values in the beginning * @param key key of the value that is to be checked if empty * @param object any object in which the key's value is to be checked - * @returns 1 if emoty, 0 if not empty + * @returns 1 if empty, 0 if not empty */ getSortOrderToFilterEmptyValues(key: string, object: any) { const value = object?.[key]; @@ -388,7 +391,7 @@ export class IssueHelperStore implements TIssueHelperStore { getGroupArray(value: boolean | number | string | string[] | null, isDate: boolean = false): string[] { if (!value || value === null || value === undefined) return ["None"]; if (Array.isArray(value)) - if (value.length) return value; + if (value && value.length) return value; else return ["None"]; else if (typeof value === "boolean") return [value ? "True" : "False"]; else if (typeof value === "number") return [value.toString()]; From 0bc4b6cece3c66d6376ea0951ab17b46caffe81e Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:16:07 +0530 Subject: [PATCH 02/71] chore: ai modal disclamier added (#3970) --- .../core/modals/gpt-assistant-popover.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/web/components/core/modals/gpt-assistant-popover.tsx b/web/components/core/modals/gpt-assistant-popover.tsx index 590015e12..deffd883a 100644 --- a/web/components/core/modals/gpt-assistant-popover.tsx +++ b/web/components/core/modals/gpt-assistant-popover.tsx @@ -7,6 +7,8 @@ import useToast from "hooks/use-toast"; import { usePopper } from "react-popper"; // ui import { Button, Input } from "@plane/ui"; +// icons +import { AlertCircle } from "lucide-react"; // components import { RichReadOnlyEditorWithRef } from "@plane/rich-text-editor"; import { Popover, Transition } from "@headlessui/react"; @@ -250,8 +252,17 @@ export const GptAssistantPopover: React.FC = (props) => { /> )} /> -
- {responseActionButton} +
+ {responseActionButton ? ( + <>{responseActionButton} + ) : ( + <> +
+ +

By using this feature, you consent to sharing the message with a 3rd party service.

+
+ + )}
- {/*
+
Turn SSL {Boolean(parseInt(watch("EMAIL_USE_SSL"))) ? "off" : "on"} @@ -242,7 +242,7 @@ export const InstanceEmailForm: FC = (props) => { )} />
-
*/} +
diff --git a/web/pages/god-mode/authorization.tsx b/web/pages/god-mode/authorization.tsx index f4eeefc65..3f18ab9d2 100644 --- a/web/pages/god-mode/authorization.tsx +++ b/web/pages/god-mode/authorization.tsx @@ -91,12 +91,11 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
{ - // Boolean(parseInt(enableMagicLogin)) === true - // ? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0") - // : updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1"); - // }} - onChange={() => {}} + onChange={() => { + Boolean(parseInt(enableMagicLogin)) === true + ? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0") + : updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1"); + }} size="sm" disabled={isSubmitting} /> From aed87ef4721485ca03705612a20ca166f24b5c10 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 18 Mar 2024 19:48:44 +0530 Subject: [PATCH 08/71] fix: build branch workflow dispatch trigger changes --- .github/workflows/build-branch.yml | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml index 0d8d2af09..2298f9558 100644 --- a/.github/workflows/build-branch.yml +++ b/.github/workflows/build-branch.yml @@ -2,27 +2,6 @@ name: Branch Build on: workflow_dispatch: - inputs: - build-web: - required: false - description: "Build Web" - type: boolean - default: false - build-space: - required: false - description: "Build Space" - type: boolean - default: false - build-api: - required: false - description: "Build API" - type: boolean - default: false - build-proxy: - required: false - description: "Build Proxy" - type: boolean - default: false push: branches: - master @@ -95,7 +74,7 @@ jobs: - nginx/** branch_build_push_frontend: - if: ${{ needs.branch_build_setup.outputs.build_frontend == 'true' || github.event.inputs.build-web=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} + if: ${{ needs.branch_build_setup.outputs.build_frontend == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} runs-on: ubuntu-20.04 needs: [branch_build_setup] env: @@ -147,7 +126,7 @@ jobs: DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} branch_build_push_space: - if: ${{ needs.branch_build_setup.outputs.build_space == 'true' || github.event.inputs.build-space=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} + if: ${{ needs.branch_build_setup.outputs.build_space == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} runs-on: ubuntu-20.04 needs: [branch_build_setup] env: @@ -199,7 +178,7 @@ jobs: DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} branch_build_push_backend: - if: ${{ needs.branch_build_setup.outputs.build_backend == 'true' || github.event.inputs.build-api=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} + if: ${{ needs.branch_build_setup.outputs.build_backend == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} runs-on: ubuntu-20.04 needs: [branch_build_setup] env: @@ -251,7 +230,7 @@ jobs: DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} branch_build_push_proxy: - if: ${{ needs.branch_build_setup.outputs.build_proxy == 'true' || github.event.inputs.build-web=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} + if: ${{ needs.branch_build_setup.outputs.build_proxy == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} runs-on: ubuntu-20.04 needs: [branch_build_setup] env: From aa3702cd46439087f081af4e4d94d65c9660cb15 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 18 Mar 2024 19:50:58 +0530 Subject: [PATCH 09/71] fix: codeql check workflow changes --- .github/workflows/codeql.yml | 64 +++++++++++++++++------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9f6ab1bfb..92b44af5b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,12 +2,11 @@ name: "CodeQL" on: push: - branches: [ 'develop', 'preview', 'master' ] + branches: ["master"] pull_request: - # The branches below must be a subset of the branches above - branches: [ 'develop', 'preview', 'master' ] + branches: ["develop", "preview", "master"] schedule: - - cron: '53 19 * * 5' + - cron: "53 19 * * 5" jobs: analyze: @@ -21,45 +20,44 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'python', 'javascript' ] + language: ["python", "javascript"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Use only 'java' to analyze code written in Java, Kotlin or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 1a462711e11858c58f1e562657781ae6fb4100db Mon Sep 17 00:00:00 2001 From: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:40:20 +0530 Subject: [PATCH 10/71] [WEB-761] fix: Time zone date misalignment (#3986) * fix date time misalignment * fix failing build * fix gantt chart view position fix * comments for getDate method * remove new Date() where not required * changes from my self review --- .../src/ui/components/editor-header.tsx | 2 +- .../src/ui/components/info-popover.tsx | 4 +- .../core/filters/date-filter-modal.tsx | 65 +++++++++++-------- .../core/sidebar/progress-chart.tsx | 8 +-- .../cycles/active-cycle-details.tsx | 17 +++-- web/components/cycles/cycles-board-card.tsx | 6 +- web/components/cycles/cycles-list-item.tsx | 6 +- web/components/cycles/form.tsx | 6 +- .../cycles/gantt-chart/cycles-list-layout.tsx | 5 +- web/components/cycles/sidebar.tsx | 13 ++-- .../widgets/issue-panels/issue-list-item.tsx | 14 ++-- web/components/dropdowns/date.tsx | 6 +- web/components/exporter/single-export.tsx | 5 +- web/components/gantt-chart/chart/root.tsx | 4 +- web/components/gantt-chart/types/index.ts | 4 +- .../gantt-chart/views/month-view.ts | 10 +-- web/components/inbox/inbox-issue-actions.tsx | 28 ++++---- web/components/inbox/inbox-issue-status.tsx | 4 +- .../issues/issue-detail/inbox/sidebar.tsx | 4 +- .../issues/issue-detail/sidebar.tsx | 7 +- .../calendar/dropdowns/months-dropdown.tsx | 8 ++- .../properties/all-properties.tsx | 10 +-- .../spreadsheet/columns/due-date-column.tsx | 6 +- .../spreadsheet/columns/start-date-column.tsx | 4 +- web/components/issues/issue-modal/form.tsx | 8 +-- .../issues/peek-overview/properties.tsx | 6 +- web/components/modules/form.tsx | 6 +- .../gantt-chart/modules-list-layout.tsx | 5 +- web/components/modules/module-card-item.tsx | 6 +- web/components/modules/module-list-item.tsx | 6 +- web/components/modules/sidebar.tsx | 52 ++++++++------- .../notifications/notification-card.tsx | 2 +- .../select-snooze-till-modal.tsx | 4 +- web/constants/inbox.tsx | 7 +- web/contexts/user-notification-context.tsx | 4 +- web/helpers/date-time.helper.ts | 62 +++++++++++++----- web/helpers/filter.helper.ts | 34 ++++++++++ web/helpers/issue.helper.ts | 9 ++- .../projects/[projectId]/pages/[pageId].tsx | 3 +- web/store/cycle.store.ts | 15 +++-- web/store/project-page.store.ts | 20 +++--- 41 files changed, 303 insertions(+), 192 deletions(-) diff --git a/packages/editor/document-editor/src/ui/components/editor-header.tsx b/packages/editor/document-editor/src/ui/components/editor-header.tsx index a322ddddc..aaa4c7be3 100644 --- a/packages/editor/document-editor/src/ui/components/editor-header.tsx +++ b/packages/editor/document-editor/src/ui/components/editor-header.tsx @@ -72,7 +72,7 @@ export const EditorHeader = (props: IEditorHeader) => { Icon={Archive} backgroundColor="bg-blue-500/20" textColor="text-blue-500" - label={`Archived at ${new Date(archivedAt).toLocaleString()}`} + label={`Archived at ${archivedAt.toLocaleString()}`} /> )} diff --git a/packages/editor/document-editor/src/ui/components/info-popover.tsx b/packages/editor/document-editor/src/ui/components/info-popover.tsx index f78dd3473..9a17a9376 100644 --- a/packages/editor/document-editor/src/ui/components/info-popover.tsx +++ b/packages/editor/document-editor/src/ui/components/info-popover.tsx @@ -52,14 +52,14 @@ export const InfoPopover: React.FC = (props) => {
Last updated on
- {renderDate(new Date(documentDetails.last_updated_at))} + {renderDate(documentDetails.last_updated_at)}
Created on
- {renderDate(new Date(documentDetails.created_on))} + {renderDate(documentDetails.created_on)}
diff --git a/web/components/core/filters/date-filter-modal.tsx b/web/components/core/filters/date-filter-modal.tsx index c5238ec1c..3e2f3ff6a 100644 --- a/web/components/core/filters/date-filter-modal.tsx +++ b/web/components/core/filters/date-filter-modal.tsx @@ -8,7 +8,7 @@ import { DateFilterSelect } from "./date-filter-select"; // ui import { Button } from "@plane/ui"; // helpers -import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate, renderFormattedDate, getDate } from "helpers/date-time.helper"; type Props = { title: string; @@ -43,7 +43,10 @@ export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, o handleClose(); }; - const isInvalid = watch("filterType") === "range" ? new Date(watch("date1")) > new Date(watch("date2")) : false; + const date1 = getDate(watch("date1")); + const date2 = getDate(watch("date1")); + + const isInvalid = watch("filterType") === "range" && date1 && date2 ? date1 > date2 : false; return ( @@ -86,35 +89,45 @@ export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, o ( - onChange(date)} - mode="single" - disabled={[ - { after: new Date(watch("date2")) } - ]} - className="border border-custom-border-200 p-3 rounded-md" - /> - )} + render={({ field: { value, onChange } }) => { + const dateValue = getDate(value); + const date2Value = getDate(watch("date2")); + return ( + { + if (!date) return; + onChange(date); + }} + mode="single" + disabled={date2Value ? [{ after: date2Value }] : undefined} + className="border border-custom-border-200 p-3 rounded-md" + /> + ); + }} /> {watch("filterType") === "range" && ( ( - onChange(date)} - mode="single" - disabled={[ - { before: new Date(watch("date1")) } - ]} - className="border border-custom-border-200 p-3 rounded-md" - /> - )} + render={({ field: { value, onChange } }) => { + const dateValue = getDate(value); + const date1Value = getDate(watch("date1")); + return ( + { + if (!date) return; + onChange(date); + }} + mode="single" + disabled={date1Value ? [{ before: date1Value }] : undefined} + className="border border-custom-border-200 p-3 rounded-md" + /> + ); + }} /> )}
diff --git a/web/components/core/sidebar/progress-chart.tsx b/web/components/core/sidebar/progress-chart.tsx index ca21756fc..b0ff4ba73 100644 --- a/web/components/core/sidebar/progress-chart.tsx +++ b/web/components/core/sidebar/progress-chart.tsx @@ -3,7 +3,7 @@ import { eachDayOfInterval, isValid } from "date-fns"; // ui import { LineGraph } from "components/ui"; // helpers -import { renderFormattedDateWithoutYear } from "helpers/date-time.helper"; +import { getDate, renderFormattedDateWithoutYear } from "helpers/date-time.helper"; //types import { TCompletionChartDistribution } from "@plane/types"; @@ -47,11 +47,11 @@ const ProgressChart: React.FC = ({ distribution, startDate, endDate, tota })); const generateXAxisTickValues = () => { - const start = new Date(startDate); - const end = new Date(endDate); + const start = getDate(startDate); + const end = getDate(endDate); let dates: Date[] = []; - if (isValid(start) && isValid(end)) { + if (start && end && isValid(start) && isValid(end)) { dates = eachDayOfInterval({ start, end }); } diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index 425ce7df3..b5d6523ed 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -27,7 +27,12 @@ import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // icons import { ArrowRight, CalendarCheck, CalendarDays, Star, Target } from "lucide-react"; // helpers -import { renderFormattedDate, findHowManyDaysLeft, renderFormattedDateWithoutYear } from "helpers/date-time.helper"; +import { + renderFormattedDate, + findHowManyDaysLeft, + renderFormattedDateWithoutYear, + getDate, +} from "helpers/date-time.helper"; import { truncateText } from "helpers/string.helper"; // types import { ICycle, TCycleGroups } from "@plane/types"; @@ -102,8 +107,10 @@ export const ActiveCycleDetails: React.FC = observer((props /> ); - const endDate = new Date(activeCycle.end_date ?? ""); - const startDate = new Date(activeCycle.start_date ?? ""); + const endDate = getDate(activeCycle.end_date); + const startDate = getDate(activeCycle.start_date); + const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0; + const cycleStatus = activeCycle.status.toLowerCase() as TCycleGroups; const groupedIssues: any = { backlog: activeCycle.backlog_issues, @@ -113,8 +120,6 @@ export const ActiveCycleDetails: React.FC = observer((props cancelled: activeCycle.cancelled_issues, }; - const cycleStatus = activeCycle.status.toLowerCase() as TCycleGroups; - const handleAddToFavorites = (e: MouseEvent) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; @@ -151,8 +156,6 @@ export const ActiveCycleDetails: React.FC = observer((props color: group.color, })); - const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0; - return (
diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index 7d6b1e000..2b3e4bdc8 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -12,7 +12,7 @@ import { Avatar, AvatarGroup, CustomMenu, Tooltip, LayersIcon, CycleGroupIcon } // icons import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react"; // helpers -import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper"; +import { findHowManyDaysLeft, getDate, renderFormattedDate } from "helpers/date-time.helper"; import { copyTextToClipboard } from "helpers/string.helper"; // constants import { CYCLE_STATUS } from "constants/cycle"; @@ -50,8 +50,8 @@ export const CyclesBoardCard: FC = observer((props) => { const cycleStatus = cycleDetails.status.toLocaleLowerCase(); const isCompleted = cycleStatus === "completed"; - const endDate = new Date(cycleDetails.end_date ?? ""); - const startDate = new Date(cycleDetails.start_date ?? ""); + const endDate = getDate(cycleDetails.end_date); + const startDate = getDate(cycleDetails.start_date); const isDateValid = cycleDetails.start_date || cycleDetails.end_date; const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 31958cd84..98cf7b1aa 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -12,7 +12,7 @@ import { CustomMenu, Tooltip, CircularProgressIndicator, CycleGroupIcon, AvatarG // icons import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react"; // helpers -import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper"; +import { findHowManyDaysLeft, getDate, renderFormattedDate } from "helpers/date-time.helper"; import { copyTextToClipboard } from "helpers/string.helper"; // constants import { CYCLE_STATUS } from "constants/cycle"; @@ -137,8 +137,8 @@ export const CyclesListItem: FC = observer((props) => { // TODO: change this logic once backend fix the response const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft"; const isCompleted = cycleStatus === "completed"; - const endDate = new Date(cycleDetails.end_date ?? ""); - const startDate = new Date(cycleDetails.start_date ?? ""); + const endDate = getDate(cycleDetails.end_date); + const startDate = getDate(cycleDetails.start_date); const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; diff --git a/web/components/cycles/form.tsx b/web/components/cycles/form.tsx index 799d80438..9941d551d 100644 --- a/web/components/cycles/form.tsx +++ b/web/components/cycles/form.tsx @@ -5,7 +5,7 @@ import { DateRangeDropdown, ProjectDropdown } from "components/dropdowns"; // ui import { Button, Input, TextArea } from "@plane/ui"; // helpers -import { renderFormattedPayloadDate } from "helpers/date-time.helper"; +import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper"; // types import { ICycle } from "@plane/types"; @@ -135,8 +135,8 @@ export const CycleForm: React.FC = (props) => { className="h-7" minDate={new Date()} value={{ - from: startDateValue ? new Date(startDateValue) : undefined, - to: endDateValue ? new Date(endDateValue) : undefined, + from: getDate(startDateValue), + to: getDate(endDateValue), }} onSelect={(val) => { onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); diff --git a/web/components/cycles/gantt-chart/cycles-list-layout.tsx b/web/components/cycles/gantt-chart/cycles-list-layout.tsx index 646333aad..146774319 100644 --- a/web/components/cycles/gantt-chart/cycles-list-layout.tsx +++ b/web/components/cycles/gantt-chart/cycles-list-layout.tsx @@ -8,6 +8,7 @@ import { GanttChartRoot, IBlockUpdateData, CycleGanttSidebar } from "components/ import { CycleGanttBlock } from "components/cycles"; // types import { ICycle } from "@plane/types"; +import { getDate } from "helpers/date-time.helper"; // constants import { EUserProjectRoles } from "constants/project"; @@ -45,8 +46,8 @@ export const CyclesListGanttChartView: FC = observer((props) => { data: block, id: block?.id ?? "", sort_order: block?.sort_order ?? 0, - start_date: new Date(block?.start_date ?? ""), - target_date: new Date(block?.end_date ?? ""), + start_date: getDate(block?.start_date), + target_date: getDate(block?.end_date), })); return structuredBlocks; diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 646736bd2..56f137f8d 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -18,8 +18,8 @@ import { Avatar, CustomMenu, Loader, LayersIcon } from "@plane/ui"; // icons import { ChevronDown, LinkIcon, Trash2, UserCircle2, AlertCircle, ChevronRight, CalendarClock } from "lucide-react"; // helpers +import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "helpers/date-time.helper"; import { copyUrlToClipboard } from "helpers/string.helper"; -import { findHowManyDaysLeft, renderFormattedPayloadDate } from "helpers/date-time.helper"; // types import { ICycle } from "@plane/types"; // constants @@ -186,8 +186,11 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const cycleStatus = cycleDetails?.status.toLocaleLowerCase(); const isCompleted = cycleStatus === "completed"; - const isStartValid = new Date(`${cycleDetails?.start_date}`) <= new Date(); - const isEndValid = new Date(`${cycleDetails?.end_date}`) >= new Date(`${cycleDetails?.start_date}`); + const startDate = getDate(cycleDetails?.start_date); + const endDate = getDate(cycleDetails?.end_date); + + const isStartValid = startDate && startDate <= new Date(); + const isEndValid = endDate && startDate && endDate >= startDate; const progressPercentage = cycleDetails ? isCompleted && cycleDetails?.progress_snapshot @@ -317,8 +320,8 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { buttonVariant="background-with-text" minDate={new Date()} value={{ - from: startDateValue ? new Date(startDateValue) : undefined, - to: endDateValue ? new Date(endDateValue) : undefined, + from: getDate(startDateValue), + to: getDate(endDateValue), }} onSelect={(val) => { onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); diff --git a/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx b/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx index 716a3afc1..b269423c6 100644 --- a/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx +++ b/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx @@ -5,7 +5,7 @@ import { useIssueDetail, useMember, useProject } from "hooks/store"; // ui import { Avatar, AvatarGroup, ControlLink, PriorityIcon } from "@plane/ui"; // helpers -import { findTotalDaysInRange, renderFormattedDate } from "helpers/date-time.helper"; +import { findTotalDaysInRange, getDate, renderFormattedDate } from "helpers/date-time.helper"; // types import { TIssue, TWidgetIssue } from "@plane/types"; @@ -34,6 +34,8 @@ export const AssignedUpcomingIssueListItem: React.FC = obser const blockedByIssueProjectDetails = blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null; + const targetDate = getDate(issueDetails.target_date); + return ( = obser
{issueDetails.name}
- {issueDetails.target_date - ? isToday(new Date(issueDetails.target_date)) - ? "Today" - : renderFormattedDate(issueDetails.target_date) - : "-"} + {targetDate ? (isToday(targetDate) ? "Today" : renderFormattedDate(targetDate)) : "-"}
{blockedByIssues.length > 0 @@ -83,7 +81,7 @@ export const AssignedOverdueIssueListItem: React.FC = observ const blockedByIssueProjectDetails = blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null; - const dueBy = findTotalDaysInRange(new Date(issueDetails.target_date ?? ""), new Date(), false) ?? 0; + const dueBy = findTotalDaysInRange(getDate(issueDetails.target_date), new Date(), false) ?? 0; return ( = observe const projectDetails = getProjectById(issue.project_id); - const dueBy = findTotalDaysInRange(new Date(issue.target_date ?? ""), new Date(), false) ?? 0; + const dueBy = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0; return ( = (props) => { {...attributes.popper} > { dropdownOnChange(date ?? null); }} diff --git a/web/components/exporter/single-export.tsx b/web/components/exporter/single-export.tsx index 34e41fc35..bd73824cf 100644 --- a/web/components/exporter/single-export.tsx +++ b/web/components/exporter/single-export.tsx @@ -2,7 +2,7 @@ import { useState, FC } from "react"; // ui import { Button } from "@plane/ui"; // helpers -import { renderFormattedDate } from "helpers/date-time.helper"; +import { getDate, renderFormattedDate } from "helpers/date-time.helper"; // types import { IExportData } from "@plane/types"; @@ -18,7 +18,8 @@ export const SingleExport: FC = ({ service, refreshing }) => { const checkExpiry = (inputDateString: string) => { const currentDate = new Date(); - const expiryDate = new Date(inputDateString); + const expiryDate = getDate(inputDateString); + if (!expiryDate) return false; expiryDate.setDate(expiryDate.getDate() + 7); return expiryDate > currentDate; }; diff --git a/web/components/gantt-chart/chart/root.tsx b/web/components/gantt-chart/chart/root.tsx index be6229ce3..dada139a8 100644 --- a/web/components/gantt-chart/chart/root.tsx +++ b/web/components/gantt-chart/chart/root.tsx @@ -64,9 +64,9 @@ export const ChartViewRoot: FC = observer((props) => { useGanttChart(); // rendering the block structure - const renderBlockStructure = (view: any, blocks: IGanttBlock[] | null) => + const renderBlockStructure = (view: ChartDataType, blocks: IGanttBlock[] | null) => blocks - ? blocks.map((block: any) => ({ + ? blocks.map((block: IGanttBlock) => ({ ...block, position: getMonthChartItemPositionWidthInMonth(view, block), })) diff --git a/web/components/gantt-chart/types/index.ts b/web/components/gantt-chart/types/index.ts index 6268e4363..cd90758fc 100644 --- a/web/components/gantt-chart/types/index.ts +++ b/web/components/gantt-chart/types/index.ts @@ -6,8 +6,8 @@ export interface IGanttBlock { width: number; }; sort_order: number; - start_date: Date | null; - target_date: Date | null; + start_date: Date | undefined; + target_date: Date | undefined; } export interface IBlockUpdateData { diff --git a/web/components/gantt-chart/views/month-view.ts b/web/components/gantt-chart/views/month-view.ts index 13d054da1..8978dc317 100644 --- a/web/components/gantt-chart/views/month-view.ts +++ b/web/components/gantt-chart/views/month-view.ts @@ -1,4 +1,5 @@ // types +import { findTotalDaysInRange } from "helpers/date-time.helper"; import { ChartDataType, IGanttBlock } from "../types"; // data import { weeks, months } from "../data"; @@ -167,15 +168,16 @@ export const getMonthChartItemPositionWidthInMonth = (chartData: ChartDataType, const { startDate } = chartData.data; const { start_date: itemStartDate, target_date: itemTargetDate } = itemData; - if (!itemStartDate || !itemTargetDate) return null; + if (!itemStartDate || !itemTargetDate) return; startDate.setHours(0, 0, 0, 0); itemStartDate.setHours(0, 0, 0, 0); itemTargetDate.setHours(0, 0, 0, 0); - // position code starts - const positionTimeDifference: number = startDate.getTime() - itemStartDate.getTime(); - const positionDaysDifference: number = Math.abs(Math.floor(positionTimeDifference / (1000 * 60 * 60 * 24))); + const positionDaysDifference = findTotalDaysInRange(startDate, itemStartDate, false); + + if (!positionDaysDifference) return; + scrollPosition = positionDaysDifference * chartData.data.width; var diffMonths = (itemStartDate.getFullYear() - startDate.getFullYear()) * 12; diff --git a/web/components/inbox/inbox-issue-actions.tsx b/web/components/inbox/inbox-issue-actions.tsx index 48d9157c6..aba20ba3c 100644 --- a/web/components/inbox/inbox-issue-actions.tsx +++ b/web/components/inbox/inbox-issue-actions.tsx @@ -21,6 +21,8 @@ import { CheckCircle2, ChevronDown, ChevronUp, Clock, FileStack, Trash2, XCircle import type { TInboxStatus, TInboxDetailedStatus } from "@plane/types"; import { EUserProjectRoles } from "constants/project"; import { ISSUE_DELETED } from "constants/event-tracker"; +//helpers +import { getDate } from "helpers/date-time.helper"; type TInboxIssueActionsHeader = { workspaceSlug: string; @@ -171,11 +173,11 @@ export const InboxIssueActionsHeader: FC = observer((p const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; const today = new Date(); - const tomorrow = new Date(today); - tomorrow.setDate(today.getDate() + 1); + const tomorrow = getDate(today); + tomorrow?.setDate(today.getDate() + 1); useEffect(() => { if (!issueStatus || !issueStatus.snoozed_till) return; - setDate(new Date(issueStatus.snoozed_till)); + setDate(issueStatus.snoozed_till); }, [issueStatus]); if (!issueStatus || !issue || !inboxIssues) return <>; @@ -269,19 +271,23 @@ export const InboxIssueActionsHeader: FC = observer((p {({ close }) => (
{ if (!date) return; setDate(date); }} mode="single" className="border border-custom-border-200 rounded-md p-3" - disabled={[ - { - before: tomorrow, - }, - ]} + disabled={ + tomorrow + ? [ + { + before: tomorrow, + }, + ] + : undefined + } /> + + )} + + + +
+
+ +
+
+
+ ); + } + // while user does not have access to view that workspace if ( membership.hasPermissionToCurrentWorkspace !== undefined && diff --git a/web/layouts/settings-layout/profile/sidebar.tsx b/web/layouts/settings-layout/profile/sidebar.tsx index b40ff93bd..b3efbe2c4 100644 --- a/web/layouts/settings-layout/profile/sidebar.tsx +++ b/web/layouts/settings-layout/profile/sidebar.tsx @@ -43,6 +43,7 @@ export const ProfileLayoutSidebar = observer(() => { const { currentUser, currentUserSettings, signOut } = useUser(); const { workspaces } = useWorkspace(); const { isMobile } = usePlatformOS(); + const workspacesList = Object.values(workspaces ?? {}); // redirect url for normal mode diff --git a/web/public/workspace/workspace-not-available.png b/web/public/workspace/workspace-not-available.png new file mode 100644 index 0000000000000000000000000000000000000000..e95cdf02a7ceaff49fc8967f6edd47497a5ece2d GIT binary patch literal 192990 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

es_FEB*CB3_ER;ar(;HSMOdK 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 M07*qoM6N<$f */ - leaveWorkspace = async (workspaceSlug: string) => + leaveWorkspace = async (workspaceSlug: string) => { + const currentWorksSpace = this.store.workspaceRoot?.currentWorkspace; await this.userService.leaveWorkspace(workspaceSlug).then(() => { runInAction(() => { + if (currentWorksSpace) delete this.store.workspaceRoot?.workspaces?.[currentWorksSpace?.id]; delete this.workspaceMemberInfo[workspaceSlug]; delete this.hasPermissionToWorkspace[workspaceSlug]; }); }); + }; /** * Joins a project From cb4cfa1dd5a66117f029c469c73a75f821c20108 Mon Sep 17 00:00:00 2001 From: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:44:08 +0530 Subject: [PATCH 16/71] [WEB-782] fix: Date timezone changes error (#3992) * fix date related exceptions * replace new Date from develop branch changes as well * changes from self review * wrap getDate logic with try catch to handle in case of error scenarios * fix formatted time --- .../src/ui/components/editor-header.tsx | 3 +- .../src/ui/components/info-popover.tsx | 8 +++-- .../document-editor/src/utils/date-utils.ts | 26 +++++++++++++++ packages/types/src/pages.d.ts | 4 +-- web/components/api-token/modal/form.tsx | 2 +- .../widgets/issue-panels/issue-list-item.tsx | 9 ++---- web/components/gantt-chart/blocks/block.tsx | 10 +++--- .../notifications/notification-card.tsx | 16 ++++++---- .../select-snooze-till-modal.tsx | 10 +++--- web/components/pages/pages-list/list-item.tsx | 17 +++++----- web/helpers/cycle.helper.ts | 9 +++--- web/helpers/date-time.helper.ts | 27 +++++++++------- web/helpers/module.helper.ts | 9 +++--- web/helpers/project.helper.ts | 4 ++- .../projects/[projectId]/pages/[pageId].tsx | 10 +++--- web/store/page.store.ts | 14 ++++---- web/store/project-page.store.ts | 32 +++++++++++-------- 17 files changed, 127 insertions(+), 83 deletions(-) create mode 100644 packages/editor/document-editor/src/utils/date-utils.ts diff --git a/packages/editor/document-editor/src/ui/components/editor-header.tsx b/packages/editor/document-editor/src/ui/components/editor-header.tsx index aaa4c7be3..33ac4a0dc 100644 --- a/packages/editor/document-editor/src/ui/components/editor-header.tsx +++ b/packages/editor/document-editor/src/ui/components/editor-header.tsx @@ -7,6 +7,7 @@ import { AlertLabel } from "src/ui/components/alert-label"; import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu"; import { SummaryPopover } from "src/ui/components/summary-popover"; import { InfoPopover } from "src/ui/components/info-popover"; +import { getDate } from "src/utils/date-utils"; interface IEditorHeader { editor: Editor; @@ -72,7 +73,7 @@ export const EditorHeader = (props: IEditorHeader) => { Icon={Archive} backgroundColor="bg-blue-500/20" textColor="text-blue-500" - label={`Archived at ${archivedAt.toLocaleString()}`} + label={`Archived at ${getDate(archivedAt)?.toLocaleString()}`} /> )} diff --git a/packages/editor/document-editor/src/ui/components/info-popover.tsx b/packages/editor/document-editor/src/ui/components/info-popover.tsx index 9a17a9376..16a3452a6 100644 --- a/packages/editor/document-editor/src/ui/components/info-popover.tsx +++ b/packages/editor/document-editor/src/ui/components/info-popover.tsx @@ -3,13 +3,15 @@ import { usePopper } from "react-popper"; import { Calendar, History, Info } from "lucide-react"; // types import { DocumentDetails } from "src/types/editor-types"; +//utils +import { getDate } from "src/utils/date-utils"; type Props = { documentDetails: DocumentDetails; }; // function to render a Date in the format- 25 May 2023 at 2:53PM -const renderDate = (date: Date): string => { +const renderDate = (date: Date | undefined): string => { const options: Intl.DateTimeFormatOptions = { day: "numeric", month: "long", @@ -52,14 +54,14 @@ export const InfoPopover: React.FC = (props) => {

Last updated on
- {renderDate(documentDetails.last_updated_at)} + {renderDate(getDate(documentDetails?.last_updated_at))}
Created on
- {renderDate(documentDetails.created_on)} + {renderDate(getDate(documentDetails?.created_on))}
diff --git a/packages/editor/document-editor/src/utils/date-utils.ts b/packages/editor/document-editor/src/utils/date-utils.ts new file mode 100644 index 000000000..63c20a974 --- /dev/null +++ b/packages/editor/document-editor/src/utils/date-utils.ts @@ -0,0 +1,26 @@ +function isNumber(value: any) { + return typeof value === "number"; +} + +/** + * This method returns a date from string of type yyyy-mm-dd + * This method is recommended to use instead of new Date() as this does not introduce any timezone offsets + * @param date + * @returns date or undefined + */ +export const getDate = (date: string | Date | undefined | null): Date | undefined => { + try { + if (!date || date === "") return; + + if (typeof date !== "string" && !(date instanceof String)) return date; + const [yearString, monthString, dayString] = date.substring(0, 10).split("-"); + const year = parseInt(yearString); + const month = parseInt(monthString); + const day = parseInt(dayString); + if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return; + + return new Date(year, month - 1, day); + } catch (e) { + return undefined; + } +}; diff --git a/packages/types/src/pages.d.ts b/packages/types/src/pages.d.ts index c9b3fb623..6e1e78be5 100644 --- a/packages/types/src/pages.d.ts +++ b/packages/types/src/pages.d.ts @@ -11,7 +11,7 @@ export interface IPage { archived_at: string | null; blocks: IPageBlock[]; color: string; - created_at: Date; + created_at: string | null; created_by: string; description: string; description_html: string; @@ -25,7 +25,7 @@ export interface IPage { owned_by: string; project: string; project_detail: IProjectLite; - updated_at: Date; + updated_at: string | null; updated_by: string; workspace: string; workspace_detail: IWorkspaceLite; diff --git a/web/components/api-token/modal/form.tsx b/web/components/api-token/modal/form.tsx index 3849fea74..b2b3f64dc 100644 --- a/web/components/api-token/modal/form.tsx +++ b/web/components/api-token/modal/form.tsx @@ -90,7 +90,7 @@ export const CreateApiTokenForm: React.FC = (props) => { // if never expires is toggled on, set expired_at to null if (neverExpires) payload.expired_at = null; // if never expires is toggled off, and the user has selected a custom date, set expired_at to the custom date - else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate ?? new Date()); + else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate); // if never expires is toggled off, and the user has selected a predefined date, set expired_at to the predefined date else { const expiryDate = getExpiryDate(data.expired_at ?? ""); diff --git a/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx b/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx index 72181c9b7..db2c07585 100644 --- a/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx +++ b/web/components/dashboard/widgets/issue-panels/issue-list-item.tsx @@ -155,6 +155,7 @@ export const CreatedUpcomingIssueListItem: React.FC = observ if (!issue) return null; const projectDetails = getProjectById(issue.project_id); + const targetDate = getDate(issue.target_date); return ( = observ
{issue.name}
- {issue.target_date - ? isToday(new Date(issue.target_date)) - ? "Today" - : renderFormattedDate(issue.target_date) - : "-"} + {targetDate ? (isToday(targetDate) ? "Today" : renderFormattedDate(targetDate)) : "-"}
{issue.assignee_ids && issue.assignee_ids?.length > 0 ? ( @@ -210,7 +207,7 @@ export const CreatedOverdueIssueListItem: React.FC = observe const projectDetails = getProjectById(issue.project_id); - const dueBy = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0; + const dueBy: number = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0; return ( = observer((props) => { totalBlockShifts: number, dragDirection: "left" | "right" | "move" ) => { - if (!block.start_date || !block.target_date) return; + const originalStartDate = getDate(block.start_date); + const originalTargetDate = getDate(block.target_date); + + if (!originalStartDate || !originalTargetDate) return; - const originalStartDate = new Date(block.start_date); const updatedStartDate = new Date(originalStartDate); - - const originalTargetDate = new Date(block.target_date); const updatedTargetDate = new Date(originalTargetDate); // update the start date on left resize diff --git a/web/components/notifications/notification-card.tsx b/web/components/notifications/notification-card.tsx index e3716edbb..c7af0824c 100644 --- a/web/components/notifications/notification-card.tsx +++ b/web/components/notifications/notification-card.tsx @@ -4,10 +4,10 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { ArchiveRestore, Clock, MessageSquare, MoreVertical, User2 } from "lucide-react"; import { Menu } from "@headlessui/react"; -// icons +// type import type { IUserNotification, NotificationType } from "@plane/types"; // ui -import { ArchiveIcon, CustomMenu, Tooltip, TOAST_TYPE, setToast } from "@plane/ui"; +import { ArchiveIcon, CustomMenu, Tooltip, TOAST_TYPE, setToast } from "@plane/ui"; // constants import { ISSUE_OPENED, @@ -16,13 +16,13 @@ import { NOTIFICATION_SNOOZED, } from "@/constants/event-tracker"; import { snoozeOptions } from "@/constants/notification"; -// helper -import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "@/helpers/date-time.helper"; -import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "@/helpers/string.helper"; // hooks import { useEventTracker } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; -// type +// helper +import { calculateTimeAgo, renderFormattedTime, renderFormattedDate, getDate } from "helpers/date-time.helper"; +import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper"; + type NotificationCardProps = { selectedTab: NotificationType; @@ -122,7 +122,9 @@ export const NotificationCard: React.FC = (props) => { const notificationField = notification.data.issue_activity.field; const notificationTriggeredBy = notification.triggered_by_details; - if (isSnoozedTabOpen && notification.snoozed_till! < new Date()) return null; + const snoozedTillDate = getDate(notification?.snoozed_till); + + if (snoozedTillDate && isSnoozedTabOpen && snoozedTillDate < new Date()) return null; return ( = (props) => { if (!formDataDate) return timeStamps; - const isToday = today.toDateString() === formDataDate.toDateString(); + const isToday = today.toDateString() === getDate(formDataDate)?.toDateString(); if (!isToday) return timeStamps; @@ -89,9 +91,9 @@ export const SnoozeNotificationModal: FC = (props) => { ); const minutes = parseInt(time[1]); - const dateTime = formData.date; - dateTime.setHours(hours); - dateTime.setMinutes(minutes); + const dateTime: Date | undefined = getDate(formData?.date); + dateTime?.setHours(hours); + dateTime?.setMinutes(minutes); await handleSubmitSnooze(notification.id, dateTime).then(() => { handleClose(); diff --git a/web/components/pages/pages-list/list-item.tsx b/web/components/pages/pages-list/list-item.tsx index 74fa42d71..57b41738f 100644 --- a/web/components/pages/pages-list/list-item.tsx +++ b/web/components/pages/pages-list/list-item.tsx @@ -192,14 +192,15 @@ export const PagesListItem: FC = observer(({ pageId, projectId }

{renderFormattedTime(archived_at)}

) : ( - -

{renderFormattedTime(updated_at)}

-
+ updated_at && ( + +

{renderFormattedTime(updated_at)}

+
+ ) )} {isEditingAllowed && ( 0) fallsInFilters = fallsInFilters && filter.status.includes(cycle.status.toLowerCase()); if (filterKey === "start_date" && filter.start_date && filter.start_date.length > 0) { + const startDate = getDate(cycle.start_date); filter.start_date.forEach((dateFilter) => { - fallsInFilters = - fallsInFilters && !!cycle.start_date && satisfiesDateFilter(new Date(cycle.start_date), dateFilter); + fallsInFilters = fallsInFilters && !!startDate && satisfiesDateFilter(startDate, dateFilter); }); } if (filterKey === "end_date" && filter.end_date && filter.end_date.length > 0) { + const endDate = getDate(cycle.end_date); filter.end_date.forEach((dateFilter) => { - fallsInFilters = - fallsInFilters && !!cycle.end_date && satisfiesDateFilter(new Date(cycle.end_date), dateFilter); + fallsInFilters = fallsInFilters && !!endDate && satisfiesDateFilter(endDate, dateFilter); }); } }); diff --git a/web/helpers/date-time.helper.ts b/web/helpers/date-time.helper.ts index db9bfa3bf..7ca4d49fb 100644 --- a/web/helpers/date-time.helper.ts +++ b/web/helpers/date-time.helper.ts @@ -8,7 +8,7 @@ import isNumber from "lodash/isNumber"; * @param {Date | string} date * @example renderFormattedDate("2024-01-01") // Jan 01, 2024 */ -export const renderFormattedDate = (date: string | Date | undefined): string | null => { +export const renderFormattedDate = (date: string | Date | undefined | null): string | null => { // Parse the date to check if it is valid const parsedDate = getDate(date); // return if undefined @@ -44,7 +44,7 @@ export const renderFormattedDateWithoutYear = (date: string | Date): string => { * @param {Date | string} date * @example renderFormattedPayloadDate("Jan 01, 20224") // "2024-01-01" */ -export const renderFormattedPayloadDate = (date: Date | string): string | null => { +export const renderFormattedPayloadDate = (date: Date | string | undefined | null): string | null => { // Parse the date to check if it is valid const parsedDate = getDate(date); // return if undefined @@ -67,7 +67,7 @@ export const renderFormattedPayloadDate = (date: Date | string): string | null = */ export const renderFormattedTime = (date: string | Date, timeFormat: "12-hour" | "24-hour" = "24-hour"): string => { // Parse the date to check if it is valid - const parsedDate = getDate(date); + const parsedDate = new Date(date); // return if undefined if (!parsedDate) return ""; // Check if the parsed date is valid @@ -208,14 +208,19 @@ export const checkIfDatesAreEqual = ( * @returns date or undefined */ export const getDate = (date: string | Date | undefined | null): Date | undefined => { - if (!date || date === "") return; + try { + if (!date || date === "") return; - if (typeof date !== "string" && !(date instanceof String)) return date; - const [yearString, monthString, dayString] = date.substring(0, 10).split("-"); - const year = parseInt(yearString); - const month = parseInt(monthString); - const day = parseInt(dayString); - if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return; + if (typeof date !== "string" && !(date instanceof String)) return date; - return new Date(year, month - 1, day); + const [yearString, monthString, dayString] = date.substring(0, 10).split("-"); + const year = parseInt(yearString); + const month = parseInt(monthString); + const day = parseInt(dayString); + if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return; + + return new Date(year, month - 1, day); + } catch (e) { + return undefined; + } }; diff --git a/web/helpers/module.helper.ts b/web/helpers/module.helper.ts index e9ce2a6ba..1a01915f2 100644 --- a/web/helpers/module.helper.ts +++ b/web/helpers/module.helper.ts @@ -1,6 +1,7 @@ import sortBy from "lodash/sortBy"; // helpers import { satisfiesDateFilter } from "@/helpers/filter.helper"; +import { getDate } from "@/helpers/date-time.helper"; // types import { IModule, TModuleDisplayFilters, TModuleFilters, TModuleOrderByOptions } from "@plane/types"; @@ -62,15 +63,15 @@ export const shouldFilterModule = ( fallsInFilters = fallsInFilters && filters.members.some((memberId) => memberIds.includes(memberId)); } if (filterKey === "start_date" && filters.start_date && filters.start_date.length > 0) { + const startDate = getDate(module.start_date); filters.start_date.forEach((dateFilter) => { - fallsInFilters = - fallsInFilters && !!module.start_date && satisfiesDateFilter(new Date(module.start_date), dateFilter); + fallsInFilters = fallsInFilters && !!startDate && satisfiesDateFilter(startDate, dateFilter); }); } if (filterKey === "target_date" && filters.target_date && filters.target_date.length > 0) { + const endDate = getDate(module.target_date); filters.target_date.forEach((dateFilter) => { - fallsInFilters = - fallsInFilters && !!module.target_date && satisfiesDateFilter(new Date(module.target_date), dateFilter); + fallsInFilters = fallsInFilters && !!endDate && satisfiesDateFilter(endDate, dateFilter); }); } }); diff --git a/web/helpers/project.helper.ts b/web/helpers/project.helper.ts index 024e77865..b4c461bb4 100644 --- a/web/helpers/project.helper.ts +++ b/web/helpers/project.helper.ts @@ -1,6 +1,7 @@ import sortBy from "lodash/sortBy"; // helpers import { satisfiesDateFilter } from "@/helpers/filter.helper"; +import { getDate } from "@/helpers/date-time.helper"; // types import { IProject, TProjectDisplayFilters, TProjectFilters, TProjectOrderByOptions } from "@plane/types"; // constants @@ -85,8 +86,9 @@ export const shouldFilterProject = ( fallsInFilters = fallsInFilters && filters.members.some((memberId) => memberIds.includes(memberId)); } if (filterKey === "created_at" && filters.created_at && filters.created_at.length > 0) { + const createdDate = getDate(project.created_at); filters.created_at.forEach((dateFilter) => { - fallsInFilters = fallsInFilters && satisfiesDateFilter(new Date(project.created_at), dateFilter); + fallsInFilters = fallsInFilters && !!createdDate && satisfiesDateFilter(createdDate, dateFilter); }); } }); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx index 56b3ac32d..02fb238fe 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx @@ -266,8 +266,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { documentDetails={{ title: pageTitle, created_by: created_by, - created_on: created_at, - last_updated_at: updated_at, + created_on: getDate(created_at) ?? new Date(created_at ?? ""), + last_updated_at: getDate(updated_at) ?? new Date(created_at ?? ""), last_updated_by: updated_by, }} pageLockConfig={userCanLock && !archived_at ? { action: unlockPage, is_locked: is_locked } : undefined} @@ -277,7 +277,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { ? { action: archived_at ? unArchivePage : archivePage, is_archived: archived_at ? true : false, - archived_at: archived_at ? getDate(archived_at) : undefined, + archived_at: getDate(archived_at), } : undefined } @@ -293,8 +293,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { documentDetails={{ title: pageTitle, created_by: created_by, - created_on: created_at, - last_updated_at: updated_at, + created_on: getDate(created_at) ?? new Date(created_at ?? ""), + last_updated_at: getDate(updated_at) ?? new Date(created_at ?? ""), last_updated_by: updated_by, }} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} diff --git a/web/store/page.store.ts b/web/store/page.store.ts index 7428b20e8..60b8e3c69 100644 --- a/web/store/page.store.ts +++ b/web/store/page.store.ts @@ -10,7 +10,7 @@ export interface IPageStore { access: number; archived_at: string | null; color: string; - created_at: Date; + created_at: string | null; created_by: string; description: string; description_html: string; @@ -23,7 +23,7 @@ export interface IPageStore { name: string; owned_by: string; project: string; - updated_at: Date; + updated_at: string | null; updated_by: string; workspace: string; @@ -52,7 +52,7 @@ export class PageStore implements IPageStore { isSubmitting: "submitting" | "submitted" | "saved" = "saved"; archived_at: string | null; color: string; - created_at: Date; + created_at: string | null; created_by: string; description: string; description_html = ""; @@ -64,7 +64,7 @@ export class PageStore implements IPageStore { name = ""; owned_by: string; project: string; - updated_at: Date; + updated_at: string | null; updated_by: string; workspace: string; oldName = ""; @@ -94,9 +94,9 @@ export class PageStore implements IPageStore { cleanup: action, }); this.created_by = page?.created_by || ""; - this.created_at = page?.created_at || new Date(); + this.created_at = page?.created_at ?? ""; this.color = page?.color || ""; - this.archived_at = page?.archived_at || null; + this.archived_at = page?.archived_at ?? null; this.name = page?.name || ""; this.description = page?.description || ""; this.description_stripped = page?.description_stripped || ""; @@ -104,7 +104,7 @@ export class PageStore implements IPageStore { this.access = page?.access || 0; this.workspace = page?.workspace || ""; this.updated_by = page?.updated_by || ""; - this.updated_at = page?.updated_at || new Date(); + this.updated_at = page?.updated_at ?? ""; this.project = page?.project || ""; this.owned_by = page?.owned_by || ""; this.labels = page?.labels || []; diff --git a/web/store/project-page.store.ts b/web/store/project-page.store.ts index 39415daf1..0e451602d 100644 --- a/web/store/project-page.store.ts +++ b/web/store/project-page.store.ts @@ -8,6 +8,8 @@ import { PageStore, IPageStore } from "@/store/page.store"; // types import { IPage, IRecentPages } from "@plane/types"; import { RootStore } from "./root.store"; +//helpers +import { getDate } from "helpers/date-time.helper"; export interface IProjectPageStore { loader: boolean; @@ -73,8 +75,8 @@ export class ProjectPageStore implements IProjectPageStore { const allProjectIds = Object.keys(this.projectPageMap[projectId]); return allProjectIds.sort((a, b) => { - const dateA = this.projectPageMap[projectId][a].created_at.getTime(); - const dateB = this.projectPageMap[projectId][b].created_at.getTime(); + const dateA = getDate(this.projectPageMap[projectId]?.[a]?.created_at)?.getTime() ?? 0; + const dateB = getDate(this.projectPageMap[projectId]?.[b]?.created_at)?.getTime() ?? 0; return dateB - dateA; }); } @@ -84,8 +86,8 @@ export class ProjectPageStore implements IProjectPageStore { if (!projectId || !this.projectArchivedPageMap[projectId]) return []; const archivedPages = Object.keys(this.projectArchivedPageMap[projectId]); return archivedPages.sort((a, b) => { - const dateA = this.projectArchivedPageMap[projectId][a].created_at.getTime(); - const dateB = this.projectArchivedPageMap[projectId][b].created_at.getTime(); + const dateA = getDate(this.projectArchivedPageMap[projectId]?.[a]?.created_at)?.getTime() ?? 0; + const dateB = getDate(this.projectArchivedPageMap[projectId]?.[b]?.created_at)?.getTime() ?? 0; return dateB - dateA; }); } @@ -126,22 +128,24 @@ export class ProjectPageStore implements IProjectPageStore { const projectId = this.rootStore.app.router.projectId; if (!this.projectPageIds || !projectId) return; - const today: string[] = this.projectPageIds.filter((page) => - isToday(this.projectPageMap[projectId][page].updated_at) - ); + const today: string[] = this.projectPageIds.filter((page) => { + const updatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at); + return updatedAt && isToday(updatedAt); + }); - const yesterday: string[] = this.projectPageIds.filter((page) => - isYesterday(this.projectPageMap[projectId][page].updated_at) - ); + const yesterday: string[] = this.projectPageIds.filter((page) => { + const updatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at); + return updatedAt && isYesterday(updatedAt); + }); const this_week: string[] = this.projectPageIds.filter((page) => { - const pageUpdatedAt = this.projectPageMap[projectId][page].updated_at; - return isThisWeek(pageUpdatedAt) && !isToday(pageUpdatedAt) && !isYesterday(pageUpdatedAt); + const pageUpdatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at); + return pageUpdatedAt && isThisWeek(pageUpdatedAt) && !isToday(pageUpdatedAt) && !isYesterday(pageUpdatedAt); }); const older: string[] = this.projectPageIds.filter((page) => { - const pageUpdatedAt = this.projectPageMap[projectId][page].updated_at; - return !isThisWeek(pageUpdatedAt) && !isYesterday(pageUpdatedAt); + const pageUpdatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at); + return pageUpdatedAt && !isThisWeek(pageUpdatedAt) && !isYesterday(pageUpdatedAt); }); return { today, yesterday, this_week, older }; From 2f883e4939f490125d7abd3e2ec35cdecfc10c28 Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:59:31 +0530 Subject: [PATCH 17/71] [WEB-780]feat: project cycle module archive (#3990) * dev: project cycle module archive * fix: update filter logic updated --- apiserver/plane/api/urls/cycle.py | 11 + apiserver/plane/api/urls/module.py | 16 +- apiserver/plane/api/urls/project.py | 10 +- apiserver/plane/api/views/__init__.py | 9 +- apiserver/plane/api/views/cycle.py | 146 +++++++++++- apiserver/plane/api/views/issue.py | 4 + apiserver/plane/api/views/module.py | 126 ++++++++++- apiserver/plane/api/views/project.py | 31 ++- apiserver/plane/api/views/state.py | 1 + apiserver/plane/app/serializers/cycle.py | 1 + apiserver/plane/app/serializers/module.py | 1 + apiserver/plane/app/urls/cycle.py | 11 + apiserver/plane/app/urls/module.py | 11 + apiserver/plane/app/urls/project.py | 6 + apiserver/plane/app/views/__init__.py | 3 + apiserver/plane/app/views/cycle/base.py | 211 +++++++++++++++++- apiserver/plane/app/views/cycle/issue.py | 1 + apiserver/plane/app/views/dashboard/base.py | 5 + apiserver/plane/app/views/exporter/base.py | 5 +- apiserver/plane/app/views/issue/activity.py | 2 + apiserver/plane/app/views/issue/comment.py | 2 + apiserver/plane/app/views/issue/link.py | 1 + apiserver/plane/app/views/issue/reaction.py | 1 + apiserver/plane/app/views/issue/relation.py | 1 + apiserver/plane/app/views/issue/subscriber.py | 1 + apiserver/plane/app/views/module/base.py | 170 +++++++++++++- apiserver/plane/app/views/page/base.py | 1 + apiserver/plane/app/views/project/base.py | 26 +++ apiserver/plane/app/views/search.py | 7 + apiserver/plane/app/views/state/base.py | 1 + apiserver/plane/app/views/view/base.py | 1 + apiserver/plane/app/views/workspace/label.py | 1 + apiserver/plane/app/views/workspace/state.py | 1 + apiserver/plane/app/views/workspace/user.py | 5 +- apiserver/plane/bgtasks/export_task.py | 1 + ...archived_at_module_archived_at_and_more.py | 41 ++++ apiserver/plane/db/models/cycle.py | 1 + apiserver/plane/db/models/issue.py | 1 + apiserver/plane/db/models/module.py | 1 + apiserver/plane/db/models/project.py | 1 + 40 files changed, 854 insertions(+), 22 deletions(-) create mode 100644 apiserver/plane/db/migrations/0062_cycle_archived_at_module_archived_at_and_more.py diff --git a/apiserver/plane/api/urls/cycle.py b/apiserver/plane/api/urls/cycle.py index 593e501bf..0a775454b 100644 --- a/apiserver/plane/api/urls/cycle.py +++ b/apiserver/plane/api/urls/cycle.py @@ -4,6 +4,7 @@ from plane.api.views.cycle import ( CycleAPIEndpoint, CycleIssueAPIEndpoint, TransferCycleIssueAPIEndpoint, + CycleArchiveUnarchiveAPIEndpoint, ) urlpatterns = [ @@ -32,4 +33,14 @@ urlpatterns = [ TransferCycleIssueAPIEndpoint.as_view(), name="transfer-issues", ), + path( + "workspaces//projects//cycles//archive/", + CycleArchiveUnarchiveAPIEndpoint.as_view(), + name="cycle-archive-unarchive", + ), + path( + "workspaces//projects//archived-cycles/", + CycleArchiveUnarchiveAPIEndpoint.as_view(), + name="cycle-archive-unarchive", + ), ] diff --git a/apiserver/plane/api/urls/module.py b/apiserver/plane/api/urls/module.py index 4309f44e9..a131f4d4f 100644 --- a/apiserver/plane/api/urls/module.py +++ b/apiserver/plane/api/urls/module.py @@ -1,6 +1,10 @@ from django.urls import path -from plane.api.views import ModuleAPIEndpoint, ModuleIssueAPIEndpoint +from plane.api.views import ( + ModuleAPIEndpoint, + ModuleIssueAPIEndpoint, + ModuleArchiveUnarchiveAPIEndpoint, +) urlpatterns = [ path( @@ -23,4 +27,14 @@ urlpatterns = [ ModuleIssueAPIEndpoint.as_view(), name="module-issues", ), + path( + "workspaces//projects//modules//archive/", + ModuleArchiveUnarchiveAPIEndpoint.as_view(), + name="module-archive-unarchive", + ), + path( + "workspaces//projects//archived-modules/", + ModuleArchiveUnarchiveAPIEndpoint.as_view(), + name="module-archive-unarchive", + ), ] diff --git a/apiserver/plane/api/urls/project.py b/apiserver/plane/api/urls/project.py index 1ed450c86..490371cca 100644 --- a/apiserver/plane/api/urls/project.py +++ b/apiserver/plane/api/urls/project.py @@ -1,6 +1,9 @@ from django.urls import path -from plane.api.views import ProjectAPIEndpoint +from plane.api.views import ( + ProjectAPIEndpoint, + ProjectArchiveUnarchiveAPIEndpoint, +) urlpatterns = [ path( @@ -13,4 +16,9 @@ urlpatterns = [ ProjectAPIEndpoint.as_view(), name="project", ), + path( + "workspaces//projects//archive/", + ProjectArchiveUnarchiveAPIEndpoint.as_view(), + name="project-archive-unarchive", + ), ] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 0da79566f..574ec69b6 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -1,4 +1,4 @@ -from .project import ProjectAPIEndpoint +from .project import ProjectAPIEndpoint, ProjectArchiveUnarchiveAPIEndpoint from .state import StateAPIEndpoint @@ -14,8 +14,13 @@ from .cycle import ( CycleAPIEndpoint, CycleIssueAPIEndpoint, TransferCycleIssueAPIEndpoint, + CycleArchiveUnarchiveAPIEndpoint, ) -from .module import ModuleAPIEndpoint, ModuleIssueAPIEndpoint +from .module import ( + ModuleAPIEndpoint, + ModuleIssueAPIEndpoint, + ModuleArchiveUnarchiveAPIEndpoint, +) from .inbox import InboxIssueAPIEndpoint diff --git a/apiserver/plane/api/views/cycle.py b/apiserver/plane/api/views/cycle.py index 2ae7faea4..bb2796bf6 100644 --- a/apiserver/plane/api/views/cycle.py +++ b/apiserver/plane/api/views/cycle.py @@ -140,7 +140,9 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): def get(self, request, slug, project_id, pk=None): if pk: - queryset = self.get_queryset().get(pk=pk) + queryset = ( + self.get_queryset().filter(archived_at__isnull=True).get(pk=pk) + ) data = CycleSerializer( queryset, fields=self.fields, @@ -150,7 +152,9 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): data, status=status.HTTP_200_OK, ) - queryset = self.get_queryset() + queryset = ( + self.get_queryset().filter(archived_at__isnull=True) + ) cycle_view = request.GET.get("cycle_view", "all") # Current Cycle @@ -291,6 +295,11 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): cycle = Cycle.objects.get( workspace__slug=slug, project_id=project_id, pk=pk ) + if cycle.archived_at: + return Response( + {"error": "Archived cycle cannot be edited"}, + status=status.HTTP_400_BAD_REQUEST, + ) request_data = request.data @@ -368,6 +377,139 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) +class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView): + + permission_classes = [ + ProjectEntityPermission, + ] + + def get_queryset(self): + return ( + Cycle.objects.filter(workspace__slug=self.kwargs.get("slug")) + .filter(project_id=self.kwargs.get("project_id")) + .filter( + project__project_projectmember__member=self.request.user, + project__project_projectmember__is_active=True, + ) + .filter(archived_at__isnull=False) + .select_related("project") + .select_related("workspace") + .select_related("owned_by") + .annotate( + total_issues=Count( + "issue_cycle", + filter=Q( + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + completed_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="completed", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + cancelled_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="cancelled", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + started_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="started", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + unstarted_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="unstarted", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + backlog_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="backlog", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + total_estimates=Sum("issue_cycle__issue__estimate_point") + ) + .annotate( + completed_estimates=Sum( + "issue_cycle__issue__estimate_point", + filter=Q( + issue_cycle__issue__state__group="completed", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + started_estimates=Sum( + "issue_cycle__issue__estimate_point", + filter=Q( + issue_cycle__issue__state__group="started", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .order_by(self.kwargs.get("order_by", "-created_at")) + .distinct() + ) + + def list(self, request, slug, project_id): + return self.paginate( + request=request, + queryset=(self.get_queryset()), + on_results=lambda cycles: CycleSerializer( + cycles, + many=True, + fields=self.fields, + expand=self.expand, + ).data, + ) + + def post(self, request, slug, project_id, pk): + cycle = Cycle.objects.get( + pk=pk, project_id=project_id, workspace__slug=slug + ) + cycle.archived_at = timezone.now() + cycle.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def delete(self, request, slug, project_id, pk): + cycle = Cycle.objects.get( + pk=pk, project_id=project_id, workspace__slug=slug + ) + cycle.archived_at = None + cycle.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView): """ This viewset automatically provides `list`, `create`, diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index e2ef742b9..4b59dc020 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -357,6 +357,7 @@ class LabelAPIEndpoint(BaseAPIView): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .select_related("project") .select_related("workspace") .select_related("parent") @@ -489,6 +490,7 @@ class IssueLinkAPIEndpoint(BaseAPIView): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .order_by(self.kwargs.get("order_by", "-created_at")) .distinct() ) @@ -618,6 +620,7 @@ class IssueCommentAPIEndpoint(WebhookMixin, BaseAPIView): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .select_related("workspace", "project", "issue", "actor") .annotate( is_member=Exists( @@ -793,6 +796,7 @@ class IssueActivityAPIEndpoint(BaseAPIView): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .select_related("actor", "workspace", "issue", "project") ).order_by(request.GET.get("order_by", "created_at")) diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 677f65ff8..460722f99 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -165,6 +165,11 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): module = Module.objects.get( pk=pk, project_id=project_id, workspace__slug=slug ) + if module.archived_at: + return Response( + {"error": "Archived module cannot be edited"}, + status=status.HTTP_400_BAD_REQUEST, + ) serializer = ModuleSerializer( module, data=request.data, @@ -197,7 +202,9 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): def get(self, request, slug, project_id, pk=None): if pk: - queryset = self.get_queryset().get(pk=pk) + queryset = ( + self.get_queryset().filter(archived_at__isnull=True).get(pk=pk) + ) data = ModuleSerializer( queryset, fields=self.fields, @@ -209,7 +216,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): ) return self.paginate( request=request, - queryset=(self.get_queryset()), + queryset=(self.get_queryset().filter(archived_at__isnull=True)), on_results=lambda modules: ModuleSerializer( modules, many=True, @@ -279,6 +286,7 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .select_related("project") .select_related("workspace") .select_related("module") @@ -446,3 +454,117 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): epoch=int(timezone.now().timestamp()), ) return Response(status=status.HTTP_204_NO_CONTENT) + + +class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): + + permission_classes = [ + ProjectEntityPermission, + ] + + def get_queryset(self): + return ( + Module.objects.filter(project_id=self.kwargs.get("project_id")) + .filter(workspace__slug=self.kwargs.get("slug")) + .filter(archived_at__isnull=False) + .select_related("project") + .select_related("workspace") + .select_related("lead") + .prefetch_related("members") + .prefetch_related( + Prefetch( + "link_module", + queryset=ModuleLink.objects.select_related( + "module", "created_by" + ), + ) + ) + .annotate( + total_issues=Count( + "issue_module", + filter=Q( + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ), + ) + .annotate( + completed_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="completed", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + cancelled_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="cancelled", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + started_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="started", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + unstarted_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="unstarted", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + backlog_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="backlog", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .order_by(self.kwargs.get("order_by", "-created_at")) + ) + + def list(self, request, slug, project_id): + return self.paginate( + request=request, + queryset=(self.get_queryset()), + on_results=lambda modules: ModuleSerializer( + modules, + many=True, + fields=self.fields, + expand=self.expand, + ).data, + ) + + def post(self, request, slug, project_id, pk): + module = Module.objects.get( + pk=pk, project_id=project_id, workspace__slug=slug + ) + module.archived_at = timezone.now() + module.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def delete(self, request, slug, project_id, pk): + module = Module.objects.get( + pk=pk, project_id=project_id, workspace__slug=slug + ) + module.archived_at = None + module.save() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/api/views/project.py b/apiserver/plane/api/views/project.py index e994dfbec..e0bce5514 100644 --- a/apiserver/plane/api/views/project.py +++ b/apiserver/plane/api/views/project.py @@ -1,4 +1,5 @@ # Django imports +from django.utils import timezone from django.db import IntegrityError from django.db.models import Exists, OuterRef, Q, F, Func, Subquery, Prefetch @@ -39,7 +40,10 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): return ( Project.objects.filter(workspace__slug=self.kwargs.get("slug")) .filter( - Q(project_projectmember__member=self.request.user) + Q( + project_projectmember__member=self.request.user, + project_projectmember__is_active=True, + ) | Q(network=2) ) .select_related( @@ -260,6 +264,12 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): workspace = Workspace.objects.get(slug=slug) project = Project.objects.get(pk=project_id) + if project.archived_at: + return Response( + {"error": "Archived project cannot be updated"}, + status=status.HTTP_400_BAD_REQUEST, + ) + serializer = ProjectSerializer( project, data={**request.data}, @@ -316,3 +326,22 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): project = Project.objects.get(pk=project_id, workspace__slug=slug) project.delete() return Response(status=status.HTTP_204_NO_CONTENT) + + +class ProjectArchiveUnarchiveAPIEndpoint(BaseAPIView): + + permission_classes = [ + ProjectBasePermission, + ] + + def post(self, request, slug, project_id): + project = Project.objects.get(pk=project_id, workspace__slug=slug) + project.archived_at = timezone.now() + project.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def delete(self, request, slug, project_id): + project = Project.objects.get(pk=project_id, workspace__slug=slug) + project.archived_at = None + project.save() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index 53ed5d6b7..4ee899831 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -28,6 +28,7 @@ class StateAPIEndpoint(BaseAPIView): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .filter(~Q(name="Triage")) .select_related("project") .select_related("workspace") diff --git a/apiserver/plane/app/serializers/cycle.py b/apiserver/plane/app/serializers/cycle.py index 30e6237f1..13d321780 100644 --- a/apiserver/plane/app/serializers/cycle.py +++ b/apiserver/plane/app/serializers/cycle.py @@ -31,6 +31,7 @@ class CycleWriteSerializer(BaseSerializer): "workspace", "project", "owned_by", + "archived_at", ] diff --git a/apiserver/plane/app/serializers/module.py b/apiserver/plane/app/serializers/module.py index 100b6314a..dfdd265cd 100644 --- a/apiserver/plane/app/serializers/module.py +++ b/apiserver/plane/app/serializers/module.py @@ -39,6 +39,7 @@ class ModuleWriteSerializer(BaseSerializer): "updated_by", "created_at", "updated_at", + "archived_at", ] def to_representation(self, instance): diff --git a/apiserver/plane/app/urls/cycle.py b/apiserver/plane/app/urls/cycle.py index 740b0ab43..2e1779420 100644 --- a/apiserver/plane/app/urls/cycle.py +++ b/apiserver/plane/app/urls/cycle.py @@ -8,6 +8,7 @@ from plane.app.views import ( CycleFavoriteViewSet, TransferCycleIssueEndpoint, CycleUserPropertiesEndpoint, + CycleArchiveUnarchiveEndpoint, ) @@ -90,4 +91,14 @@ urlpatterns = [ CycleUserPropertiesEndpoint.as_view(), name="cycle-user-filters", ), + path( + "workspaces//projects//cycles//archive/", + CycleArchiveUnarchiveEndpoint.as_view(), + name="cycle-archive-unarchive", + ), + path( + "workspaces//projects//archived-cycles/", + CycleArchiveUnarchiveEndpoint.as_view(), + name="cycle-archive-unarchive", + ), ] diff --git a/apiserver/plane/app/urls/module.py b/apiserver/plane/app/urls/module.py index 981b4d1fb..a730fcd50 100644 --- a/apiserver/plane/app/urls/module.py +++ b/apiserver/plane/app/urls/module.py @@ -7,6 +7,7 @@ from plane.app.views import ( ModuleLinkViewSet, ModuleFavoriteViewSet, ModuleUserPropertiesEndpoint, + ModuleArchiveUnarchiveEndpoint, ) @@ -110,4 +111,14 @@ urlpatterns = [ ModuleUserPropertiesEndpoint.as_view(), name="cycle-user-filters", ), + path( + "workspaces//projects//modules//archive/", + ModuleArchiveUnarchiveEndpoint.as_view(), + name="module-archive-unarchive", + ), + path( + "workspaces//projects//archived-modules/", + ModuleArchiveUnarchiveEndpoint.as_view(), + name="module-archive-unarchive", + ), ] diff --git a/apiserver/plane/app/urls/project.py b/apiserver/plane/app/urls/project.py index f8ecac4c0..7ea636df8 100644 --- a/apiserver/plane/app/urls/project.py +++ b/apiserver/plane/app/urls/project.py @@ -14,6 +14,7 @@ from plane.app.views import ( ProjectPublicCoverImagesEndpoint, ProjectDeployBoardViewSet, UserProjectRolesEndpoint, + ProjectArchiveUnarchiveEndpoint, ) @@ -175,4 +176,9 @@ urlpatterns = [ ), name="project-deploy-board", ), + path( + "workspaces//projects//archive/", + ProjectArchiveUnarchiveEndpoint.as_view(), + name="project-archive-unarchive", + ), ] diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index bb5b7dd74..2cdab312c 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -5,6 +5,7 @@ from .project.base import ( ProjectFavoritesViewSet, ProjectPublicCoverImagesEndpoint, ProjectDeployBoardViewSet, + ProjectArchiveUnarchiveEndpoint, ) from .project.invite import ( @@ -90,6 +91,7 @@ from .cycle.base import ( CycleDateCheckEndpoint, CycleFavoriteViewSet, TransferCycleIssueEndpoint, + CycleArchiveUnarchiveEndpoint, CycleUserPropertiesEndpoint, ) from .cycle.issue import ( @@ -168,6 +170,7 @@ from .module.base import ( ModuleViewSet, ModuleLinkViewSet, ModuleFavoriteViewSet, + ModuleArchiveUnarchiveEndpoint, ModuleUserPropertiesEndpoint, ) diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 5a57ebef2..b70db4c11 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -80,6 +80,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .select_related("project", "workspace", "owned_by") .prefetch_related( Prefetch( @@ -184,13 +185,17 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) def list(self, request, slug, project_id): - queryset = self.get_queryset().annotate( - total_issues=Count( - "issue_cycle", - filter=Q( - issue_cycle__issue__archived_at__isnull=True, - issue_cycle__issue__is_draft=False, - ), + queryset = ( + self.get_queryset() + .filter(archived_at__isnull=True) + .annotate( + total_issues=Count( + "issue_cycle", + filter=Q( + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) ) ) cycle_view = request.GET.get("cycle_view", "all") @@ -420,6 +425,11 @@ class CycleViewSet(WebhookMixin, BaseViewSet): workspace__slug=slug, project_id=project_id, pk=pk ) cycle = queryset.first() + if cycle.archived_at: + return Response( + {"error": "Archived cycle cannot be updated"}, + status=status.HTTP_400_BAD_REQUEST, + ) request_data = request.data if ( @@ -478,6 +488,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): def retrieve(self, request, slug, project_id, pk): queryset = ( self.get_queryset() + .filter(archived_at__isnull=True) .filter(pk=pk) .annotate( total_issues=Count( @@ -682,6 +693,192 @@ class CycleViewSet(WebhookMixin, BaseViewSet): return Response(status=status.HTTP_204_NO_CONTENT) +class CycleArchiveUnarchiveEndpoint(BaseAPIView): + + permission_classes = [ + ProjectEntityPermission, + ] + + def get_queryset(self): + favorite_subquery = CycleFavorite.objects.filter( + user=self.request.user, + cycle_id=OuterRef("pk"), + project_id=self.kwargs.get("project_id"), + workspace__slug=self.kwargs.get("slug"), + ) + return self.filter_queryset( + super() + .get_queryset() + .filter(workspace__slug=self.kwargs.get("slug")) + .filter(project_id=self.kwargs.get("project_id")) + .filter(archived_at__isnull=False) + .filter( + project__project_projectmember__member=self.request.user, + project__project_projectmember__is_active=True, + ) + .filter(project__archived_at__isnull=True) + .select_related("project", "workspace", "owned_by") + .prefetch_related( + Prefetch( + "issue_cycle__issue__assignees", + queryset=User.objects.only( + "avatar", "first_name", "id" + ).distinct(), + ) + ) + .prefetch_related( + Prefetch( + "issue_cycle__issue__labels", + queryset=Label.objects.only( + "name", "color", "id" + ).distinct(), + ) + ) + .annotate(is_favorite=Exists(favorite_subquery)) + .annotate( + completed_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="completed", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + cancelled_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="cancelled", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + started_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="started", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + unstarted_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="unstarted", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + backlog_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="backlog", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .annotate( + status=Case( + When( + Q(start_date__lte=timezone.now()) + & Q(end_date__gte=timezone.now()), + then=Value("CURRENT"), + ), + When( + start_date__gt=timezone.now(), then=Value("UPCOMING") + ), + When(end_date__lt=timezone.now(), then=Value("COMPLETED")), + When( + Q(start_date__isnull=True) & Q(end_date__isnull=True), + then=Value("DRAFT"), + ), + default=Value("DRAFT"), + output_field=CharField(), + ) + ) + .annotate( + assignee_ids=Coalesce( + ArrayAgg( + "issue_cycle__issue__assignees__id", + distinct=True, + filter=~Q( + issue_cycle__issue__assignees__id__isnull=True + ), + ), + Value([], output_field=ArrayField(UUIDField())), + ) + ) + .order_by("-is_favorite", "name") + .distinct() + ) + + def list(self, request, slug, project_id): + queryset = ( + self.get_queryset() + .annotate( + total_issues=Count( + "issue_cycle", + filter=Q( + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .values( + # necessary fields + "id", + "workspace_id", + "project_id", + # model fields + "name", + "description", + "start_date", + "end_date", + "owned_by_id", + "view_props", + "sort_order", + "external_source", + "external_id", + "progress_snapshot", + # meta fields + "total_issues", + "is_favorite", + "cancelled_issues", + "completed_issues", + "started_issues", + "unstarted_issues", + "backlog_issues", + "assignee_ids", + "status", + ) + ).order_by("-is_favorite", "-created_at") + return Response(queryset, status=status.HTTP_200_OK) + + def post(self, request, slug, project_id, cycle_id): + cycle = Cycle.objects.get( + pk=cycle_id, project_id=project_id, workspace__slug=slug + ) + cycle.archived_at = timezone.now() + cycle.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def delete(self, request, slug, project_id, cycle_id): + cycle = Cycle.objects.get( + pk=cycle_id, project_id=project_id, workspace__slug=slug + ) + cycle.archived_at = None + cycle.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + class CycleDateCheckEndpoint(BaseAPIView): permission_classes = [ ProjectEntityPermission, diff --git a/apiserver/plane/app/views/cycle/issue.py b/apiserver/plane/app/views/cycle/issue.py index 84af4ff32..d2a7795da 100644 --- a/apiserver/plane/app/views/cycle/issue.py +++ b/apiserver/plane/app/views/cycle/issue.py @@ -74,6 +74,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, ) + .filter(project__archived_at__isnull=True) .filter(cycle_id=self.kwargs.get("cycle_id")) .select_related("project") .select_related("workspace") diff --git a/apiserver/plane/app/views/dashboard/base.py b/apiserver/plane/app/views/dashboard/base.py index e6757faf9..508f81f21 100644 --- a/apiserver/plane/app/views/dashboard/base.py +++ b/apiserver/plane/app/views/dashboard/base.py @@ -471,6 +471,7 @@ def dashboard_recent_activity(self, request, slug): workspace__slug=slug, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, actor=request.user, ).select_related("actor", "workspace", "issue", "project")[:8] @@ -486,6 +487,7 @@ def dashboard_recent_projects(self, request, slug): workspace__slug=slug, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, actor=request.user, ) .values_list("project_id", flat=True) @@ -500,6 +502,7 @@ def dashboard_recent_projects(self, request, slug): additional_projects = Project.objects.filter( project_projectmember__member=request.user, project_projectmember__is_active=True, + archived_at__isnull=True, workspace__slug=slug, ).exclude(id__in=unique_project_ids) @@ -522,6 +525,7 @@ def dashboard_recent_collaborators(self, request, slug): actor=OuterRef("member"), project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .values("actor") .annotate(num_activities=Count("pk")) @@ -534,6 +538,7 @@ def dashboard_recent_collaborators(self, request, slug): workspace__slug=slug, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .annotate( num_activities=Coalesce( diff --git a/apiserver/plane/app/views/exporter/base.py b/apiserver/plane/app/views/exporter/base.py index 846508515..698d9eb99 100644 --- a/apiserver/plane/app/views/exporter/base.py +++ b/apiserver/plane/app/views/exporter/base.py @@ -29,7 +29,10 @@ class ExportIssuesEndpoint(BaseAPIView): if provider in ["csv", "xlsx", "json"]: if not project_ids: project_ids = Project.objects.filter( - workspace__slug=slug + workspace__slug=slug, + project_projectmember__member=request.user, + project_projectmember__is_active=True, + archived_at__isnull=True, ).values_list("id", flat=True) project_ids = [str(project_id) for project_id in project_ids] diff --git a/apiserver/plane/app/views/issue/activity.py b/apiserver/plane/app/views/issue/activity.py index ea6e9b389..6815b254e 100644 --- a/apiserver/plane/app/views/issue/activity.py +++ b/apiserver/plane/app/views/issue/activity.py @@ -44,6 +44,7 @@ class IssueActivityEndpoint(BaseAPIView): ~Q(field__in=["comment", "vote", "reaction", "draft"]), project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) .filter(**filters) @@ -54,6 +55,7 @@ class IssueActivityEndpoint(BaseAPIView): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) .filter(**filters) diff --git a/apiserver/plane/app/views/issue/comment.py b/apiserver/plane/app/views/issue/comment.py index eb2d5834c..0d61f1325 100644 --- a/apiserver/plane/app/views/issue/comment.py +++ b/apiserver/plane/app/views/issue/comment.py @@ -48,6 +48,7 @@ class IssueCommentViewSet(WebhookMixin, BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .select_related("project") .select_related("workspace") @@ -163,6 +164,7 @@ class CommentReactionViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .order_by("-created_at") .distinct() diff --git a/apiserver/plane/app/views/issue/link.py b/apiserver/plane/app/views/issue/link.py index ca3290759..c965a7d4d 100644 --- a/apiserver/plane/app/views/issue/link.py +++ b/apiserver/plane/app/views/issue/link.py @@ -35,6 +35,7 @@ class IssueLinkViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .order_by("-created_at") .distinct() diff --git a/apiserver/plane/app/views/issue/reaction.py b/apiserver/plane/app/views/issue/reaction.py index c6f6823be..da8f6ebb5 100644 --- a/apiserver/plane/app/views/issue/reaction.py +++ b/apiserver/plane/app/views/issue/reaction.py @@ -34,6 +34,7 @@ class IssueReactionViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .order_by("-created_at") .distinct() diff --git a/apiserver/plane/app/views/issue/relation.py b/apiserver/plane/app/views/issue/relation.py index 45a5dc9a7..eb5aff9af 100644 --- a/apiserver/plane/app/views/issue/relation.py +++ b/apiserver/plane/app/views/issue/relation.py @@ -41,6 +41,7 @@ class IssueRelationViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .select_related("project") .select_related("workspace") diff --git a/apiserver/plane/app/views/issue/subscriber.py b/apiserver/plane/app/views/issue/subscriber.py index 61e09e4a2..dc727de28 100644 --- a/apiserver/plane/app/views/issue/subscriber.py +++ b/apiserver/plane/app/views/issue/subscriber.py @@ -54,6 +54,7 @@ class IssueSubscriberViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .order_by("-created_at") .distinct() diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 881730d65..f6329c223 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -196,7 +196,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def list(self, request, slug, project_id): - queryset = self.get_queryset() + queryset = self.get_queryset().filter(archived_at__isnull=True) if self.fields: modules = ModuleSerializer( queryset, @@ -238,6 +238,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): def retrieve(self, request, slug, project_id, pk): queryset = ( self.get_queryset() + .filter(archived_at__isnull=True) .filter(pk=pk) .annotate( total_issues=Issue.issue_objects.filter( @@ -374,14 +375,20 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) def partial_update(self, request, slug, project_id, pk): - queryset = self.get_queryset().filter(pk=pk) + module = self.get_queryset().filter(pk=pk) + + if module.first().archived_at: + return Response( + {"error": "Archived module cannot be updated"}, + status=status.HTTP_400_BAD_REQUEST, + ) serializer = ModuleWriteSerializer( - queryset.first(), data=request.data, partial=True + module.first(), data=request.data, partial=True ) if serializer.is_valid(): serializer.save() - module = queryset.values( + module = module.values( # Required fields "id", "workspace_id", @@ -464,12 +471,167 @@ class ModuleLinkViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .order_by("-created_at") .distinct() ) +class ModuleArchiveUnarchiveEndpoint(BaseAPIView): + + permission_classes = [ + ProjectEntityPermission, + ] + + def get_queryset(self): + favorite_subquery = ModuleFavorite.objects.filter( + user=self.request.user, + module_id=OuterRef("pk"), + project_id=self.kwargs.get("project_id"), + workspace__slug=self.kwargs.get("slug"), + ) + return ( + super() + .get_queryset() + .filter(project_id=self.kwargs.get("project_id")) + .filter(workspace__slug=self.kwargs.get("slug")) + .filter(archived_at__isnull=False) + .annotate(is_favorite=Exists(favorite_subquery)) + .select_related("project") + .select_related("workspace") + .select_related("lead") + .prefetch_related("members") + .prefetch_related( + Prefetch( + "link_module", + queryset=ModuleLink.objects.select_related( + "module", "created_by" + ), + ) + ) + .annotate( + total_issues=Count( + "issue_module", + filter=Q( + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ), + ) + .annotate( + completed_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="completed", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + cancelled_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="cancelled", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + started_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="started", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + unstarted_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="unstarted", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + backlog_issues=Count( + "issue_module__issue__state__group", + filter=Q( + issue_module__issue__state__group="backlog", + issue_module__issue__archived_at__isnull=True, + issue_module__issue__is_draft=False, + ), + ) + ) + .annotate( + member_ids=Coalesce( + ArrayAgg( + "members__id", + distinct=True, + filter=~Q(members__id__isnull=True), + ), + Value([], output_field=ArrayField(UUIDField())), + ) + ) + .order_by("-is_favorite", "-created_at") + ) + + def list(self, request, slug, project_id): + queryset = self.get_queryset() + modules = queryset.values( # Required fields + "id", + "workspace_id", + "project_id", + # Model fields + "name", + "description", + "description_text", + "description_html", + "start_date", + "target_date", + "status", + "lead_id", + "member_ids", + "view_props", + "sort_order", + "external_source", + "external_id", + # computed fields + "total_issues", + "is_favorite", + "cancelled_issues", + "completed_issues", + "started_issues", + "unstarted_issues", + "backlog_issues", + "created_at", + "updated_at", + ) + return Response(modules, status=status.HTTP_200_OK) + + def post(self, request, slug, project_id, module_id): + module = Module.objects.get( + pk=module_id, project_id=project_id, workspace__slug=slug + ) + module.archived_at = timezone.now() + module.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def delete(self, request, slug, project_id, module_id): + module = Module.objects.get( + pk=module_id, project_id=project_id, workspace__slug=slug + ) + module.archived_at = None + module.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + class ModuleFavoriteViewSet(BaseViewSet): serializer_class = ModuleFavoriteSerializer model = ModuleFavorite diff --git a/apiserver/plane/app/views/page/base.py b/apiserver/plane/app/views/page/base.py index 34a9ee638..d60d78500 100644 --- a/apiserver/plane/app/views/page/base.py +++ b/apiserver/plane/app/views/page/base.py @@ -70,6 +70,7 @@ class PageViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .filter(parent__isnull=True) .filter(Q(owned_by=self.request.user) | Q(access=0)) diff --git a/apiserver/plane/app/views/project/base.py b/apiserver/plane/app/views/project/base.py index 22bf3b423..b2f9f56e9 100644 --- a/apiserver/plane/app/views/project/base.py +++ b/apiserver/plane/app/views/project/base.py @@ -13,6 +13,7 @@ from django.db.models import ( Subquery, ) from django.conf import settings +from django.utils import timezone # Third Party imports from rest_framework.response import Response @@ -179,6 +180,7 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): def retrieve(self, request, slug, pk): project = ( self.get_queryset() + .filter(archived_at__isnull=True) .filter(pk=pk) .annotate( total_issues=Issue.issue_objects.filter( @@ -366,6 +368,12 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): project = Project.objects.get(pk=pk) + if project.archived_at: + return Response( + {"error": "Archived projects cannot be updated"}, + status=status.HTTP_400_BAD_REQUEST, + ) + serializer = ProjectSerializer( project, data={**request.data}, @@ -420,6 +428,24 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): ) +class ProjectArchiveUnarchiveEndpoint(BaseAPIView): + + permission_classes = [ + ProjectBasePermission, + ] + def post(self, request, slug, project_id): + project = Project.objects.get(pk=project_id, workspace__slug=slug) + project.archived_at = timezone.now() + project.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def delete(self, request, slug, project_id): + project = Project.objects.get(pk=project_id, workspace__slug=slug) + project.archived_at = None + project.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + class ProjectIdentifierEndpoint(BaseAPIView): permission_classes = [ ProjectBasePermission, diff --git a/apiserver/plane/app/views/search.py b/apiserver/plane/app/views/search.py index 42aa05e4f..4a4ffd826 100644 --- a/apiserver/plane/app/views/search.py +++ b/apiserver/plane/app/views/search.py @@ -50,6 +50,7 @@ class GlobalSearchEndpoint(BaseAPIView): q, project_projectmember__member=self.request.user, project_projectmember__is_active=True, + archived_at__isnull=True, workspace__slug=slug, ) .distinct() @@ -72,6 +73,7 @@ class GlobalSearchEndpoint(BaseAPIView): q, project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) @@ -97,6 +99,7 @@ class GlobalSearchEndpoint(BaseAPIView): q, project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) @@ -121,6 +124,7 @@ class GlobalSearchEndpoint(BaseAPIView): q, project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) @@ -145,6 +149,7 @@ class GlobalSearchEndpoint(BaseAPIView): q, project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) @@ -169,6 +174,7 @@ class GlobalSearchEndpoint(BaseAPIView): q, project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, workspace__slug=slug, ) @@ -243,6 +249,7 @@ class IssueSearchEndpoint(BaseAPIView): workspace__slug=slug, project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True ) if workspace_search == "false": diff --git a/apiserver/plane/app/views/state/base.py b/apiserver/plane/app/views/state/base.py index 137a89d99..7b0904490 100644 --- a/apiserver/plane/app/views/state/base.py +++ b/apiserver/plane/app/views/state/base.py @@ -33,6 +33,7 @@ class StateViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .filter(~Q(name="Triage")) .select_related("project") diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 16c50e880..e2fc29aac 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -282,6 +282,7 @@ class IssueViewViewSet(BaseViewSet): .filter( project__project_projectmember__member=self.request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .select_related("project") .select_related("workspace") diff --git a/apiserver/plane/app/views/workspace/label.py b/apiserver/plane/app/views/workspace/label.py index ba396a842..328f3f8c1 100644 --- a/apiserver/plane/app/views/workspace/label.py +++ b/apiserver/plane/app/views/workspace/label.py @@ -20,6 +20,7 @@ class WorkspaceLabelsEndpoint(BaseAPIView): workspace__slug=slug, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) serializer = LabelSerializer(labels, many=True).data return Response(serializer, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/workspace/state.py b/apiserver/plane/app/views/workspace/state.py index d44f83e73..7e3b158e5 100644 --- a/apiserver/plane/app/views/workspace/state.py +++ b/apiserver/plane/app/views/workspace/state.py @@ -20,6 +20,7 @@ class WorkspaceStatesEndpoint(BaseAPIView): workspace__slug=slug, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) serializer = StateSerializer(states, many=True).data return Response(serializer, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/workspace/user.py b/apiserver/plane/app/views/workspace/user.py index 36b00b738..fe495de6c 100644 --- a/apiserver/plane/app/views/workspace/user.py +++ b/apiserver/plane/app/views/workspace/user.py @@ -124,7 +124,7 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): | Q(issue_subscribers__subscriber_id=user_id), workspace__slug=slug, project__project_projectmember__member=request.user, - project__project_projectmember__is_active=True + project__project_projectmember__is_active=True, ) .filter(**filters) .select_related("workspace", "project", "state", "parent") @@ -299,6 +299,7 @@ class WorkspaceUserProfileEndpoint(BaseAPIView): workspace__slug=slug, project_projectmember__member=request.user, project_projectmember__is_active=True, + archived_at__isnull=True, ) .annotate( created_issues=Count( @@ -387,6 +388,7 @@ class WorkspaceUserActivityEndpoint(BaseAPIView): workspace__slug=slug, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, actor=user_id, ).select_related("actor", "workspace", "issue", "project") @@ -498,6 +500,7 @@ class WorkspaceUserProfileStatsEndpoint(BaseAPIView): subscriber_id=user_id, project__project_projectmember__member=request.user, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .filter(**filters) .count() diff --git a/apiserver/plane/bgtasks/export_task.py b/apiserver/plane/bgtasks/export_task.py index 2e0d88994..c99836c83 100644 --- a/apiserver/plane/bgtasks/export_task.py +++ b/apiserver/plane/bgtasks/export_task.py @@ -304,6 +304,7 @@ def issue_export_task( project_id__in=project_ids, project__project_projectmember__member=exporter_instance.initiated_by_id, project__project_projectmember__is_active=True, + project__archived_at__isnull=True, ) .select_related( "project", "workspace", "state", "parent", "created_by" diff --git a/apiserver/plane/db/migrations/0062_cycle_archived_at_module_archived_at_and_more.py b/apiserver/plane/db/migrations/0062_cycle_archived_at_module_archived_at_and_more.py new file mode 100644 index 000000000..be3f9fc2a --- /dev/null +++ b/apiserver/plane/db/migrations/0062_cycle_archived_at_module_archived_at_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.7 on 2024-03-19 08:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0061_project_logo_props'), + ] + + operations = [ + migrations.AddField( + model_name="cycle", + name="archived_at", + field=models.DateTimeField(null=True), + ), + migrations.AddField( + model_name="module", + name="archived_at", + field=models.DateTimeField(null=True), + ), + migrations.AddField( + model_name="project", + name="archived_at", + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name="socialloginconnection", + name="medium", + field=models.CharField( + choices=[ + ("Google", "google"), + ("Github", "github"), + ("Jira", "jira"), + ], + default=None, + max_length=20, + ), + ), + ] diff --git a/apiserver/plane/db/models/cycle.py b/apiserver/plane/db/models/cycle.py index d802dbc1e..15a8251d7 100644 --- a/apiserver/plane/db/models/cycle.py +++ b/apiserver/plane/db/models/cycle.py @@ -69,6 +69,7 @@ class Cycle(ProjectBaseModel): external_source = models.CharField(max_length=255, null=True, blank=True) external_id = models.CharField(max_length=255, blank=True, null=True) progress_snapshot = models.JSONField(default=dict) + archived_at = models.DateTimeField(null=True) class Meta: verbose_name = "Cycle" diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 5bd0b3397..0a59acb93 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -91,6 +91,7 @@ class IssueManager(models.Manager): | models.Q(issue_inbox__isnull=True) ) .exclude(archived_at__isnull=False) + .exclude(project__archived_at__isnull=False) .exclude(is_draft=True) ) diff --git a/apiserver/plane/db/models/module.py b/apiserver/plane/db/models/module.py index 9af4e120e..b201e4d7f 100644 --- a/apiserver/plane/db/models/module.py +++ b/apiserver/plane/db/models/module.py @@ -92,6 +92,7 @@ class Module(ProjectBaseModel): sort_order = models.FloatField(default=65535) external_source = models.CharField(max_length=255, null=True, blank=True) external_id = models.CharField(max_length=255, blank=True, null=True) + archived_at = models.DateTimeField(null=True) class Meta: unique_together = ["name", "project"] diff --git a/apiserver/plane/db/models/project.py b/apiserver/plane/db/models/project.py index bb4885d14..db5ebf33b 100644 --- a/apiserver/plane/db/models/project.py +++ b/apiserver/plane/db/models/project.py @@ -114,6 +114,7 @@ class Project(BaseModel): null=True, related_name="default_state", ) + archived_at = models.DateTimeField(null=True) def __str__(self): """Return name of the project""" From 901a7703ff2008d4ed9803cb024f7f4e2835d9d5 Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Wed, 20 Mar 2024 18:06:45 +0530 Subject: [PATCH 18/71] fix: project, module, cycle filter store infinite loop (#3994) --- web/store/cycle_filter.store.ts | 16 +++++++++------- web/store/module_filter.store.ts | 16 +++++++++------- web/store/project/project_filter.store.ts | 16 +++++++++------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/web/store/cycle_filter.store.ts b/web/store/cycle_filter.store.ts index b610741b9..2c57b5e78 100644 --- a/web/store/cycle_filter.store.ts +++ b/web/store/cycle_filter.store.ts @@ -1,4 +1,4 @@ -import { action, computed, observable, makeObservable, runInAction, autorun } from "mobx"; +import { action, computed, observable, makeObservable, runInAction, reaction } from "mobx"; import { computedFn } from "mobx-utils"; import set from "lodash/set"; // types @@ -49,11 +49,13 @@ export class CycleFilterStore implements ICycleFilterStore { // root store this.rootStore = _rootStore; // initialize display filters of the current project - autorun(() => { - const projectId = this.rootStore.app.router.projectId; - if (!projectId) return; - this.initProjectCycleFilters(projectId); - }); + reaction( + () => this.rootStore.app.router.projectId, + (projectId) => { + if (!projectId) return; + this.initProjectCycleFilters(projectId); + } + ); } /** @@ -97,7 +99,7 @@ export class CycleFilterStore implements ICycleFilterStore { active_tab: displayFilters?.active_tab || "active", layout: displayFilters?.layout || "list", }; - this.filters[projectId] = {}; + this.filters[projectId] = this.filters[projectId] ?? {}; }); }; diff --git a/web/store/module_filter.store.ts b/web/store/module_filter.store.ts index 7ead79e7e..ae5d07135 100644 --- a/web/store/module_filter.store.ts +++ b/web/store/module_filter.store.ts @@ -1,4 +1,4 @@ -import { action, computed, observable, makeObservable, runInAction, autorun } from "mobx"; +import { action, computed, observable, makeObservable, runInAction, reaction } from "mobx"; import { computedFn } from "mobx-utils"; import set from "lodash/set"; // types @@ -49,11 +49,13 @@ export class ModuleFilterStore implements IModuleFilterStore { // root store this.rootStore = _rootStore; // initialize display filters of the current project - autorun(() => { - const projectId = this.rootStore.app.router.projectId; - if (!projectId) return; - this.initProjectModuleFilters(projectId); - }); + reaction( + () => this.rootStore.app.router.projectId, + (projectId) => { + if (!projectId) return; + this.initProjectModuleFilters(projectId); + } + ); } /** @@ -98,7 +100,7 @@ export class ModuleFilterStore implements IModuleFilterStore { layout: displayFilters?.layout || "list", order_by: displayFilters?.order_by || "name", }; - this.filters[projectId] = {}; + this.filters[projectId] = this.filters[projectId] ?? {}; }); }; diff --git a/web/store/project/project_filter.store.ts b/web/store/project/project_filter.store.ts index 013f2dff5..7d6aff96f 100644 --- a/web/store/project/project_filter.store.ts +++ b/web/store/project/project_filter.store.ts @@ -1,4 +1,4 @@ -import { action, computed, observable, makeObservable, runInAction, autorun } from "mobx"; +import { action, computed, observable, makeObservable, runInAction, reaction } from "mobx"; import { computedFn } from "mobx-utils"; import set from "lodash/set"; // types @@ -49,11 +49,13 @@ export class ProjectFilterStore implements IProjectFilterStore { // root store this.rootStore = _rootStore; // initialize display filters of the current workspace - autorun(() => { - const workspaceSlug = this.rootStore.app.router.workspaceSlug; - if (!workspaceSlug) return; - this.initWorkspaceFilters(workspaceSlug); - }); + reaction( + () => this.rootStore.app.router.workspaceSlug, + (workspaceSlug) => { + if (!workspaceSlug) return; + this.initWorkspaceFilters(workspaceSlug); + } + ); } /** @@ -96,7 +98,7 @@ export class ProjectFilterStore implements IProjectFilterStore { this.displayFilters[workspaceSlug] = { order_by: displayFilters?.order_by || "created_at", }; - this.filters[workspaceSlug] = {}; + this.filters[workspaceSlug] = this.filters[workspaceSlug] ?? {}; }); }; From 4ea616f1cd0a2a52cbfa50c19c0aaced203a7302 Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Wed, 20 Mar 2024 18:07:11 +0530 Subject: [PATCH 19/71] fix: Labels overflow in peek, detail view (#3995) --- .../issues/issue-detail/label/create-label.tsx | 17 +++++++++++++---- .../issue-detail/label/select/label-select.tsx | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/web/components/issues/issue-detail/label/create-label.tsx b/web/components/issues/issue-detail/label/create-label.tsx index f20fa4721..160ff0071 100644 --- a/web/components/issues/issue-detail/label/create-label.tsx +++ b/web/components/issues/issue-detail/label/create-label.tsx @@ -7,6 +7,8 @@ import { IIssueLabel } from "@plane/types"; // hooks import { Input, TOAST_TYPE, setToast } from "@plane/ui"; import { useIssueDetail } from "@/hooks/store"; +// helpers +import { cn } from "helpers/common.helper"; // ui // types import { TLabelOperations } from "./root"; @@ -29,6 +31,7 @@ export const LabelCreate: FC = (props) => { // hooks const { issue: { getIssueById }, + peekIssue, } = useIssueDetail(); // state const [isCreateToggle, setIsCreateToggle] = useState(false); @@ -82,13 +85,13 @@ export const LabelCreate: FC = (props) => {
{isCreateToggle && ( -
+
( - + <> {value && value?.trim() !== "" && ( @@ -110,8 +113,14 @@ export const LabelCreate: FC = (props) => { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - - onChange(value.hex)} /> + + onChange(value.hex)} + /> diff --git a/web/components/issues/issue-detail/label/select/label-select.tsx b/web/components/issues/issue-detail/label/select/label-select.tsx index 1bacb34be..2882a1e0e 100644 --- a/web/components/issues/issue-detail/label/select/label-select.tsx +++ b/web/components/issues/issue-detail/label/select/label-select.tsx @@ -56,7 +56,7 @@ export const IssueLabelSelect: React.FC = observer((props) => query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement: "bottom-start", + placement: "bottom-end", modifiers: [ { name: "preventOverflow", From 621624e29fad978f9c9516a0ad1c90f710546301 Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Wed, 20 Mar 2024 18:07:35 +0530 Subject: [PATCH 20/71] [WEB-383] fix: rendering state and members from different projects in sub-issues (#3996) * fix: rendering the state and memebers from different projects in sub issues and exception handling while creating an issue from different project * chore: handled different project issue properties in a new function handler --- .../issues/sub-issues/properties.tsx | 4 +- .../issue/issue-details/sub_issues.store.ts | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/web/components/issues/sub-issues/properties.tsx b/web/components/issues/sub-issues/properties.tsx index 33f346b6d..6c611468a 100644 --- a/web/components/issues/sub-issues/properties.tsx +++ b/web/components/issues/sub-issues/properties.tsx @@ -72,8 +72,8 @@ export const IssueProperty: React.FC = (props) => { } disabled={!disabled} multiple - buttonVariant={issue.assignee_ids.length > 0 ? "transparent-without-text" : "border-without-text"} - buttonClassName={issue.assignee_ids.length > 0 ? "hover:bg-transparent px-0" : ""} + buttonVariant={(issue?.assignee_ids || []).length > 0 ? "transparent-without-text" : "border-without-text"} + buttonClassName={(issue?.assignee_ids || []).length > 0 ? "hover:bg-transparent px-0" : ""} />
diff --git a/web/store/issue/issue-details/sub_issues.store.ts b/web/store/issue/issue-details/sub_issues.store.ts index ca1528928..f4bd20772 100644 --- a/web/store/issue/issue-details/sub_issues.store.ts +++ b/web/store/issue/issue-details/sub_issues.store.ts @@ -1,10 +1,9 @@ import concat from "lodash/concat"; import pull from "lodash/pull"; import set from "lodash/set"; +import uniq from "lodash/uniq"; import update from "lodash/update"; import { action, makeObservable, observable, runInAction } from "mobx"; -// services -import { IssueService } from "@/services/issue"; // types import { TIssue, @@ -13,6 +12,9 @@ import { TIssueSubIssuesIdMap, TSubIssuesStateDistribution, } from "@plane/types"; +// services +import { IssueService } from "@/services/issue"; +// store import { IIssueDetail } from "./root.store"; export interface IIssueSubIssuesStoreActions { @@ -48,6 +50,7 @@ export interface IIssueSubIssuesStore extends IIssueSubIssuesStoreActions { subIssuesByIssueId: (issueId: string) => string[] | undefined; subIssueHelpersByIssueId: (issueId: string) => TSubIssueHelpers; // actions + fetchOtherProjectProperties: (workspaceSlug: string, projectIds: string[]) => Promise; setSubIssueHelpers: (parentIssueId: string, key: TSubIssueHelpersKeys, value: string) => void; } @@ -74,6 +77,7 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { updateSubIssue: action, removeSubIssue: action, deleteSubIssue: action, + fetchOtherProjectProperties: action, }); // root store this.rootIssueDetailStore = rootStore; @@ -116,6 +120,12 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { this.rootIssueDetailStore.rootIssueStore.issues.addIssue(subIssues); + // fetch other issues states and members when sub-issues are from different project + if (subIssues && subIssues.length > 0) { + const otherProjectIds = uniq(subIssues.map((issue) => issue.project_id).filter((id) => id !== projectId)); + this.fetchOtherProjectProperties(workspaceSlug, otherProjectIds); + } + runInAction(() => { set(this.subIssuesStateDistribution, parentIssueId, subIssuesStateDistribution); set( @@ -140,6 +150,12 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { const subIssuesStateDistribution = response?.state_distribution; const subIssues = response.sub_issues as TIssue[]; + // fetch other issues states and members when sub-issues are from different project + if (subIssues && subIssues.length > 0) { + const otherProjectIds = uniq(subIssues.map((issue) => issue.project_id).filter((id) => id !== projectId)); + this.fetchOtherProjectProperties(workspaceSlug, otherProjectIds); + } + runInAction(() => { Object.keys(subIssuesStateDistribution).forEach((key) => { const stateGroup = key as keyof TSubIssuesStateDistribution; @@ -292,4 +308,30 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { throw error; } }; + + fetchOtherProjectProperties = async (workspaceSlug: string, projectIds: string[]) => { + try { + if (projectIds.length > 0) { + for (const projectId of projectIds) { + // fetching other project states + this.rootIssueDetailStore.rootIssueStore.rootStore.state.fetchProjectStates(workspaceSlug, projectId); + // fetching other project members + this.rootIssueDetailStore.rootIssueStore.rootStore.memberRoot.project.fetchProjectMembers( + workspaceSlug, + projectId + ); + // fetching other project labels + this.rootIssueDetailStore.rootIssueStore.rootStore.label.fetchProjectLabels(workspaceSlug, projectId); + // fetching other project cycles + this.rootIssueDetailStore.rootIssueStore.rootStore.cycle.fetchAllCycles(workspaceSlug, projectId); + // fetching other project modules + this.rootIssueDetailStore.rootIssueStore.rootStore.module.fetchModules(workspaceSlug, projectId); + // fetching other project estimates + this.rootIssueDetailStore.rootIssueStore.rootStore.estimate.fetchProjectEstimates(workspaceSlug, projectId); + } + } + } catch (error) { + throw error; + } + }; } From e02fa4d9e7b1e537f1449f88c1df97db67e1c028 Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Wed, 20 Mar 2024 18:08:21 +0530 Subject: [PATCH 21/71] fix: autofocus on the title element when we are creating the bulk issues in the issue create modal (#3998) --- web/components/issues/issue-modal/draft-issue-layout.tsx | 3 +++ web/components/issues/issue-modal/form.tsx | 5 ++++- web/components/issues/issue-modal/modal.tsx | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/web/components/issues/issue-modal/draft-issue-layout.tsx b/web/components/issues/issue-modal/draft-issue-layout.tsx index c7bf17682..55f8d711f 100644 --- a/web/components/issues/issue-modal/draft-issue-layout.tsx +++ b/web/components/issues/issue-modal/draft-issue-layout.tsx @@ -16,6 +16,7 @@ import { IssueDraftService } from "@/services/issue"; export interface DraftIssueProps { changesMade: Partial | null; data?: Partial; + issueTitleRef: React.MutableRefObject; isCreateMoreToggleEnabled: boolean; onCreateMoreToggleChange: (value: boolean) => void; onChange: (formData: Partial | null) => void; @@ -31,6 +32,7 @@ export const DraftIssueLayout: React.FC = observer((props) => { const { changesMade, data, + issueTitleRef, onChange, onClose, onSubmit, @@ -107,6 +109,7 @@ export const DraftIssueLayout: React.FC = observer((props) => { isCreateMoreToggleEnabled={isCreateMoreToggleEnabled} onCreateMoreToggleChange={onCreateMoreToggleChange} data={data} + issueTitleRef={issueTitleRef} onChange={onChange} onClose={handleClose} onSubmit={onSubmit} diff --git a/web/components/issues/issue-modal/form.tsx b/web/components/issues/issue-modal/form.tsx index 9d11f4e68..b6c773335 100644 --- a/web/components/issues/issue-modal/form.tsx +++ b/web/components/issues/issue-modal/form.tsx @@ -52,6 +52,7 @@ const defaultValues: Partial = { export interface IssueFormProps { data?: Partial; + issueTitleRef: React.MutableRefObject; isCreateMoreToggleEnabled: boolean; onCreateMoreToggleChange: (value: boolean) => void; onChange?: (formData: Partial | null) => void; @@ -93,6 +94,7 @@ const getTabIndex = (key: string) => TAB_INDICES.findIndex((tabIndex) => tabInde export const IssueFormRoot: FC = observer((props) => { const { data, + issueTitleRef, onChange, onClose, onSubmit, @@ -366,11 +368,12 @@ export const IssueFormRoot: FC = observer((props) => { onChange(e.target.value); handleFormChange(); }} - ref={ref} + ref={issueTitleRef || ref} hasError={Boolean(errors.name)} placeholder="Issue Title" className="w-full resize-none text-xl" tabIndex={getTabIndex("name")} + autoFocus /> )} /> diff --git a/web/components/issues/issue-modal/modal.tsx b/web/components/issues/issue-modal/modal.tsx index b0e08dfd1..609392d4f 100644 --- a/web/components/issues/issue-modal/modal.tsx +++ b/web/components/issues/issue-modal/modal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import { Dialog, Transition } from "@headlessui/react"; @@ -46,6 +46,8 @@ export const CreateUpdateIssueModal: React.FC = observer((prop storeType = EIssuesStoreType.PROJECT, isDraft = false, } = props; + // ref + const issueTitleRef = useRef(null); // states const [changesMade, setChangesMade] = useState | null>(null); const [createMore, setCreateMore] = useState(false); @@ -169,6 +171,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop path: router.asPath, }); !createMore && handleClose(); + if (createMore) issueTitleRef && issueTitleRef?.current?.focus(); return response; } catch (error) { setToast({ @@ -268,6 +271,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop cycle_id: data?.cycle_id ? data?.cycle_id : cycleId ? cycleId : null, module_ids: data?.module_ids ? data?.module_ids : moduleId ? [moduleId] : null, }} + issueTitleRef={issueTitleRef} onChange={handleFormChange} onClose={handleClose} onSubmit={handleFormSubmit} @@ -278,6 +282,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop /> ) : ( Date: Wed, 20 Mar 2024 18:08:42 +0530 Subject: [PATCH 22/71] fix: project inifinite loader when we go the workspace projects for the first time (#3999) --- web/components/project/card-list.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 19830c7c0..4e01dabd4 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -19,8 +19,6 @@ export const ProjectCardList = observer(() => { const { workspaceProjectIds, filteredProjectIds, getProjectById } = useProject(); const { searchQuery } = useProjectFilter(); - if (!filteredProjectIds) return ; - if (workspaceProjectIds?.length === 0) return ( { }} /> ); + + if (!filteredProjectIds) return ; + if (filteredProjectIds.length === 0) return (
From 7142889c23bb151e1a8036714868bc6e21265e19 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:19:14 +0530 Subject: [PATCH 23/71] [WEB-413] chore: project active cycle UI revamp (#3997) * chore: project active cycle ui revamp * chore: resolved liniting issues --------- Co-authored-by: gurusainath --- .../core/sidebar/progress-chart.tsx | 5 +- .../cycles/active-cycle/cycle-stats.tsx | 260 ++++++++++++ web/components/cycles/active-cycle/header.tsx | 77 ++++ web/components/cycles/active-cycle/index.ts | 4 + .../cycles/active-cycle/productivity.tsx | 46 +++ .../cycles/active-cycle/progress.tsx | 79 ++++ web/components/cycles/active-cycle/root.tsx | 389 ++---------------- .../active-cycle/upcoming-cycles-list.tsx | 44 +- 8 files changed, 533 insertions(+), 371 deletions(-) create mode 100644 web/components/cycles/active-cycle/cycle-stats.tsx create mode 100644 web/components/cycles/active-cycle/header.tsx create mode 100644 web/components/cycles/active-cycle/productivity.tsx create mode 100644 web/components/cycles/active-cycle/progress.tsx diff --git a/web/components/core/sidebar/progress-chart.tsx b/web/components/core/sidebar/progress-chart.tsx index a1c5f3a13..68b1708fe 100644 --- a/web/components/core/sidebar/progress-chart.tsx +++ b/web/components/core/sidebar/progress-chart.tsx @@ -12,6 +12,7 @@ type Props = { startDate: string | Date; endDate: string | Date; totalIssues: number; + className?: string; }; const styleById = { @@ -40,7 +41,7 @@ const DashedLine = ({ series, lineGenerator, xScale, yScale }: any) => /> )); -const ProgressChart: React.FC = ({ distribution, startDate, endDate, totalIssues }) => { +const ProgressChart: React.FC = ({ distribution, startDate, endDate, totalIssues, className = "" }) => { const chartData = Object.keys(distribution ?? []).map((key) => ({ currentDate: renderFormattedDateWithoutYear(key), pending: distribution[key], @@ -73,7 +74,7 @@ const ProgressChart: React.FC = ({ distribution, startDate, endDate, tota }; return ( -
+
= observer((props) => { + const { workspaceSlug, projectId, cycle } = props; + + const { storedValue: tab, setValue: setTab } = useLocalStorage("activeCycleTab", "Assignees"); + + const currentValue = (tab: string | null) => { + switch (tab) { + case "Priority-Issues": + return 0; + case "Assignees": + return 1; + case "Labels": + return 2; + default: + return 0; + } + }; + const { + issues: { fetchActiveCycleIssues }, + } = useIssues(EIssuesStoreType.CYCLE); + + const { currentProjectDetails } = useProject(); + + const { data: activeCycleIssues } = useSWR( + workspaceSlug && projectId && cycle.id ? CYCLE_ISSUES_WITH_PARAMS(cycle.id, { priority: "urgent,high" }) : null, + workspaceSlug && projectId && cycle.id ? () => fetchActiveCycleIssues(workspaceSlug, projectId, cycle.id) : null + ); + + const cycleIssues = activeCycleIssues ?? []; + + return ( +
+ { + switch (i) { + case 0: + return setTab("Priority-Issues"); + case 1: + return setTab("Assignees"); + case 2: + return setTab("Labels"); + + default: + return setTab("Priority-Issues"); + } + }} + > + + + cn( + "relative z-[1] font-semibold text-xs rounded-[3px] py-1.5 text-custom-text-400 focus:outline-none transition duration-500", + { + "text-custom-text-300 bg-custom-background-100": selected, + "hover:text-custom-text-300": !selected, + } + ) + } + > + Priority Issues + + + cn( + "relative z-[1] font-semibold text-xs rounded-[3px] py-1.5 text-custom-text-400 focus:outline-none transition duration-500", + { + "text-custom-text-300 bg-custom-background-100": selected, + "hover:text-custom-text-300": !selected, + } + ) + } + > + Assignees + + + cn( + "relative z-[1] font-semibold text-xs rounded-[3px] py-1.5 text-custom-text-400 focus:outline-none transition duration-500", + { + "text-custom-text-300 bg-custom-background-100": selected, + "hover:text-custom-text-300": !selected, + } + ) + } + > + Labels + + + + + +
+ {cycleIssues ? ( + cycleIssues.length > 0 ? ( + cycleIssues.map((issue: TIssue) => ( + +
+ + + + + {currentProjectDetails?.identifier}-{issue.sequence_id} + + + + {issue.name} + +
+
+ {}} + projectId={projectId?.toString() ?? ""} + disabled + buttonVariant="background-with-text" + buttonContainerClassName="cursor-pointer max-w-24" + showTooltip + /> + {issue.target_date && ( + +
+ + + {renderFormattedDateWithoutYear(issue.target_date)} + +
+
+ )} +
+ + )) + ) : ( +
+ There are no high priority issues present in this cycle. +
+ ) + ) : ( + + + + + + )} +
+
+ + + {cycle.distribution?.assignees?.map((assignee, index) => { + if (assignee.assignee_id) + return ( + + + + {assignee.display_name} +
+ } + completed={assignee.completed_issues} + total={assignee.total_issues} + /> + ); + else + return ( + +
+ User +
+ No assignee +
+ } + completed={assignee.completed_issues} + total={assignee.total_issues} + /> + ); + })} + + + + {cycle.distribution?.labels?.map((label, index) => ( + + + {label.label_name ?? "No labels"} +
+ } + completed={label.completed_issues} + total={label.total_issues} + /> + ))} + + + +
+ ); +}); diff --git a/web/components/cycles/active-cycle/header.tsx b/web/components/cycles/active-cycle/header.tsx new file mode 100644 index 000000000..98ed91c1d --- /dev/null +++ b/web/components/cycles/active-cycle/header.tsx @@ -0,0 +1,77 @@ +import { FC } from "react"; +import Link from "next/link"; +// types +import { ICycle, TCycleGroups } from "@plane/types"; +// ui +import { Tooltip, CycleGroupIcon, getButtonStyling, Avatar, AvatarGroup } from "@plane/ui"; +// helpers +import { renderFormattedDate, findHowManyDaysLeft } from "@/helpers/date-time.helper"; +import { truncateText } from "@/helpers/string.helper"; +// hooks +import { useMember } from "@/hooks/store"; + +export type ActiveCycleHeaderProps = { + cycle: ICycle; + workspaceSlug: string; + projectId: string; +}; + +export const ActiveCycleHeader: FC = (props) => { + const { cycle, workspaceSlug, projectId } = props; + // store + const { getUserDetails } = useMember(); + const cycleOwnerDetails = cycle && cycle.owned_by_id ? getUserDetails(cycle.owned_by_id) : undefined; + + const daysLeft = findHowManyDaysLeft(cycle.end_date) ?? 0; + const currentCycleStatus = cycle.status.toLocaleLowerCase() as TCycleGroups; + + const cycleAssignee = (cycle.distribution?.assignees ?? []).filter((assignee) => assignee.display_name); + + return ( +
+
+ + +

{truncateText(cycle.name, 70)}

+
+ + + {`${daysLeft} ${daysLeft > 1 ? "days" : "day"} left`} + + +
+
+
+
+ + {cycleAssignee.length > 0 && ( + + + {cycleAssignee.map((member) => ( + + ))} + + + )} +
+
+ + View Cycle + +
+
+ ); +}; diff --git a/web/components/cycles/active-cycle/index.ts b/web/components/cycles/active-cycle/index.ts index 73d5d1e98..d88ccc3e8 100644 --- a/web/components/cycles/active-cycle/index.ts +++ b/web/components/cycles/active-cycle/index.ts @@ -1,4 +1,8 @@ export * from "./root"; +export * from "./header"; export * from "./stats"; export * from "./upcoming-cycles-list-item"; export * from "./upcoming-cycles-list"; +export * from "./cycle-stats"; +export * from "./progress"; +export * from "./productivity"; diff --git a/web/components/cycles/active-cycle/productivity.tsx b/web/components/cycles/active-cycle/productivity.tsx new file mode 100644 index 000000000..59c2ac3c9 --- /dev/null +++ b/web/components/cycles/active-cycle/productivity.tsx @@ -0,0 +1,46 @@ +import { FC } from "react"; +// types +import { ICycle } from "@plane/types"; +// components +import ProgressChart from "@/components/core/sidebar/progress-chart"; + +export type ActiveCycleProductivityProps = { + cycle: ICycle; +}; + +export const ActiveCycleProductivity: FC = (props) => { + const { cycle } = props; + + return ( +
+
+

Issue burndown

+
+ +
+
+
+
+ + Ideal +
+
+ + Current +
+
+ {`Pending issues - ${cycle.backlog_issues + cycle.unstarted_issues + cycle.started_issues}`} +
+
+ +
+
+
+ ); +}; diff --git a/web/components/cycles/active-cycle/progress.tsx b/web/components/cycles/active-cycle/progress.tsx new file mode 100644 index 000000000..dea3b496a --- /dev/null +++ b/web/components/cycles/active-cycle/progress.tsx @@ -0,0 +1,79 @@ +import { FC } from "react"; +// types +import { ICycle } from "@plane/types"; +// ui +import { LinearProgressIndicator } from "@plane/ui"; +// constants +import { CYCLE_STATE_GROUPS_DETAILS } from "@/constants/cycle"; + +export type ActiveCycleProgressProps = { + cycle: ICycle; +}; + +export const ActiveCycleProgress: FC = (props) => { + const { cycle } = props; + + const progressIndicatorData = CYCLE_STATE_GROUPS_DETAILS.map((group, index) => ({ + id: index, + name: group.title, + value: cycle.total_issues > 0 ? (cycle[group.key as keyof ICycle] as number) : 0, + color: group.color, + })); + + const groupedIssues: any = { + completed: cycle.completed_issues, + started: cycle.started_issues, + unstarted: cycle.unstarted_issues, + backlog: cycle.backlog_issues, + }; + + return ( +
+
+
+

Progress

+ + {`${cycle.completed_issues + cycle.cancelled_issues}/${cycle.total_issues - cycle.cancelled_issues} ${ + cycle.completed_issues + cycle.cancelled_issues > 1 ? "Issues" : "Issue" + } closed`} + +
+ +
+ +
+ {Object.keys(groupedIssues).map((group, index) => ( + <> + {groupedIssues[group] > 0 && ( +
+
+
+ + {group} +
+ {`${groupedIssues[group]} ${ + groupedIssues[group] > 1 ? "Issues" : "Issue" + }`} +
+
+ )} + + ))} + {cycle.cancelled_issues > 0 && ( + + + {`${cycle.cancelled_issues} cancelled ${ + cycle.cancelled_issues > 1 ? "issues are" : "issue is" + } excluded from this report.`}{" "} + + + )} +
+
+ ); +}; diff --git a/web/components/cycles/active-cycle/root.tsx b/web/components/cycles/active-cycle/root.tsx index 83acd1521..bd2c3b613 100644 --- a/web/components/cycles/active-cycle/root.tsx +++ b/web/components/cycles/active-cycle/root.tsx @@ -1,48 +1,20 @@ -import { MouseEvent } from "react"; import { observer } from "mobx-react-lite"; -import Link from "next/link"; import useSWR from "swr"; -// hooks -import { ArrowRight, CalendarCheck, CalendarDays, Star, Target } from "lucide-react"; -import { ICycle, TCycleGroups } from "@plane/types"; -import { - AvatarGroup, - Loader, - Tooltip, - LinearProgressIndicator, - LayersIcon, - StateGroupIcon, - PriorityIcon, - Avatar, - CycleGroupIcon, - setPromiseToast, - getButtonStyling, -} from "@plane/ui"; -import { SingleProgressStats } from "@/components/core"; // ui +import { Loader } from "@plane/ui"; // components -import ProgressChart from "@/components/core/sidebar/progress-chart"; -import { ActiveCycleProgressStats, UpcomingCyclesList } from "@/components/cycles"; -import { StateDropdown } from "@/components/dropdowns"; -import { EmptyState } from "@/components/empty-state"; -// icons -// helpers -// types -// constants -import { CYCLE_STATE_GROUPS_DETAILS } from "@/constants/cycle"; -import { EmptyStateType } from "@/constants/empty-state"; -import { CYCLE_ISSUES_WITH_PARAMS } from "@/constants/fetch-keys"; -import { EIssuesStoreType } from "@/constants/issue"; -import { cn } from "@/helpers/common.helper"; import { - renderFormattedDate, - findHowManyDaysLeft, - renderFormattedDateWithoutYear, - getDate, -} from "@/helpers/date-time.helper"; -import { truncateText } from "@/helpers/string.helper"; -import { useCycle, useCycleFilter, useIssues, useMember, useProject } from "@/hooks/store"; -import { usePlatformOS } from "@/hooks/use-platform-os"; + ActiveCycleHeader, + ActiveCycleProductivity, + ActiveCycleProgress, + ActiveCycleStats, + UpcomingCyclesList, +} from "@/components/cycles"; +import { EmptyState } from "@/components/empty-state"; +// constants +import { EmptyStateType } from "@/constants/empty-state"; +// hooks +import { useCycle, useCycleFilter } from "@/hooks/store"; interface IActiveCycleDetails { workspaceSlug: string; @@ -52,41 +24,24 @@ interface IActiveCycleDetails { export const ActiveCycleRoot: React.FC = observer((props) => { // props const { workspaceSlug, projectId } = props; - // hooks - const { isMobile } = usePlatformOS(); // store hooks - const { - issues: { fetchActiveCycleIssues }, - } = useIssues(EIssuesStoreType.CYCLE); - const { - currentProjectActiveCycleId, - currentProjectUpcomingCycleIds, - fetchActiveCycle, - getActiveCycleById, - addCycleToFavorites, - removeCycleFromFavorites, - } = useCycle(); - const { currentProjectDetails } = useProject(); - const { getUserDetails } = useMember(); + const { fetchActiveCycle, currentProjectActiveCycleId, currentProjectUpcomingCycleIds, getActiveCycleById } = + useCycle(); // cycle filters hook const { updateDisplayFilters } = useCycleFilter(); // derived values const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null; - const cycleOwnerDetails = activeCycle ? getUserDetails(activeCycle.owned_by_id) : undefined; // fetch active cycle details const { isLoading } = useSWR( workspaceSlug && projectId ? `PROJECT_ACTIVE_CYCLE_${projectId}` : null, workspaceSlug && projectId ? () => fetchActiveCycle(workspaceSlug, projectId) : null ); - // fetch active cycle issues - const { data: activeCycleIssues } = useSWR( - workspaceSlug && projectId && currentProjectActiveCycleId - ? CYCLE_ISSUES_WITH_PARAMS(currentProjectActiveCycleId, { priority: "urgent,high" }) - : null, - workspaceSlug && projectId && currentProjectActiveCycleId - ? () => fetchActiveCycleIssues(workspaceSlug, projectId, currentProjectActiveCycleId) - : null - ); + + const handleEmptyStateAction = () => + updateDisplayFilters(projectId, { + active_tab: "all", + }); + // show loader if active cycle is loading if (!activeCycle && isLoading) return ( @@ -110,310 +65,28 @@ export const ActiveCycleRoot: React.FC = observer((props) = Create new cycles to find them here or check
{"'"}All{"'"} cycles tab to see all cycles or{" "} -

- + ); } - const endDate = getDate(activeCycle.end_date); - const startDate = getDate(activeCycle.start_date); - const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0; - const cycleStatus = activeCycle.status.toLowerCase() as TCycleGroups; - - const groupedIssues: any = { - backlog: activeCycle.backlog_issues, - unstarted: activeCycle.unstarted_issues, - started: activeCycle.started_issues, - completed: activeCycle.completed_issues, - cancelled: activeCycle.cancelled_issues, - }; - - const handleAddToFavorites = (e: MouseEvent) => { - e.preventDefault(); - if (!workspaceSlug || !projectId) return; - - const addToFavoritePromise = addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), activeCycle.id); - - setPromiseToast(addToFavoritePromise, { - loading: "Adding cycle to favorites...", - success: { - title: "Success!", - message: () => "Cycle added to favorites.", - }, - error: { - title: "Error!", - message: () => "Couldn't add the cycle to favorites. Please try again.", - }, - }); - }; - - const handleRemoveFromFavorites = (e: MouseEvent) => { - e.preventDefault(); - if (!workspaceSlug || !projectId) return; - - const removeFromFavoritePromise = removeCycleFromFavorites( - workspaceSlug?.toString(), - projectId.toString(), - activeCycle.id - ); - - setPromiseToast(removeFromFavoritePromise, { - loading: "Removing cycle from favorites...", - success: { - title: "Success!", - message: () => "Cycle removed from favorites.", - }, - error: { - title: "Error!", - message: () => "Couldn't remove the cycle from favorites. Please try again.", - }, - }); - }; - - const progressIndicatorData = CYCLE_STATE_GROUPS_DETAILS.map((group, index) => ({ - id: index, - name: group.title, - value: - activeCycle.total_issues > 0 - ? ((activeCycle[group.key as keyof ICycle] as number) / activeCycle.total_issues) * 100 - : 0, - color: group.color, - })); - return ( -
-
-
-
-
-
- - - - - -

{truncateText(activeCycle.name, 70)}

-
-
- - - {`${daysLeft} ${daysLeft > 1 ? "days" : "day"} left`} - - {activeCycle.is_favorite ? ( - - ) : ( - - )} - -
- -
-
- - {renderFormattedDate(startDate)} -
- -
- - {renderFormattedDate(endDate)} -
-
- -
-
- - {cycleOwnerDetails?.display_name} -
- - {activeCycle.assignee_ids.length > 0 && ( -
- - {activeCycle.assignee_ids.map((assignee_id) => { - const member = getUserDetails(assignee_id); - return ; - })} - -
- )} -
- -
-
- - {activeCycle.total_issues} issues -
-
- - {activeCycle.completed_issues} issues -
-
- - - View cycle - -
-
-
-
-
-
-
- Progress - -
-
- {Object.keys(groupedIssues).map((group, index) => ( - - - {group} -
- } - completed={groupedIssues[group]} - total={activeCycle.total_issues} - /> - ))} -
-
-
-
- -
+ <> +
+ +
+ + +
-
-
-
High priority issues
-
- {activeCycleIssues ? ( - activeCycleIssues.length > 0 ? ( - activeCycleIssues.map((issue) => ( - -
- - - - - {currentProjectDetails?.identifier}-{issue.sequence_id} - - - - {truncateText(issue.name, 30)} - -
-
- {}} - projectId={projectId} - disabled - buttonVariant="background-with-text" - /> - {issue.target_date && ( - -
- - {renderFormattedDateWithoutYear(issue.target_date)} -
-
- )} -
- - )) - ) : ( -
- There are no high priority issues present in this cycle. -
- ) - ) : ( - - - - - - )} -
-
-
-
-
-
- - Ideal -
-
- - Current -
-
-
- - - - - Pending issues-{" "} - {activeCycle.total_issues - (activeCycle.completed_issues + activeCycle.cancelled_issues)} - -
-
-
- -
-
-
-
+ {currentProjectUpcomingCycleIds && } + ); }); diff --git a/web/components/cycles/active-cycle/upcoming-cycles-list.tsx b/web/components/cycles/active-cycle/upcoming-cycles-list.tsx index c2d8b2388..f4156f341 100644 --- a/web/components/cycles/active-cycle/upcoming-cycles-list.tsx +++ b/web/components/cycles/active-cycle/upcoming-cycles-list.tsx @@ -1,10 +1,16 @@ +import { FC } from "react"; import { observer } from "mobx-react"; -// hooks -import { UpcomingCycleListItem } from "@/components/cycles"; -import { useCycle } from "@/hooks/store"; // components +import { UpcomingCycleListItem } from "@/components/cycles"; +// hooks +import { useCycle } from "@/hooks/store"; -export const UpcomingCyclesList = observer(() => { +type Props = { + handleEmptyStateAction: () => void; +}; + +export const UpcomingCyclesList: FC = observer((props) => { + const { handleEmptyStateAction } = props; // store hooks const { currentProjectUpcomingCycleIds } = useCycle(); @@ -12,14 +18,30 @@ export const UpcomingCyclesList = observer(() => { return (
-
- Upcoming cycles -
-
- {currentProjectUpcomingCycleIds.map((cycleId) => ( - - ))} +
+ Next cycles
+ {currentProjectUpcomingCycleIds.length > 0 ? ( +
+ {currentProjectUpcomingCycleIds.map((cycleId) => ( + + ))} +
+ ) : ( +
+
+
No upcoming cycles
+

+ Create new cycles to find them here or check +
+ {"'"}All{"'"} cycles tab to see all cycles or{" "} + +

+
+
+ )}
); }); From 4eefc7f692e38a03630d7a72a93f80a5d23fa82c Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:22:02 +0530 Subject: [PATCH 24/71] [WEB-784] chore: create workspace fields as required indication (#4003) * chore: create workspace fields as required indication * style: gap between asterisk & text --- web/components/workspace/create-workspace-form.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/web/components/workspace/create-workspace-form.tsx b/web/components/workspace/create-workspace-form.tsx index ad0c05096..d8a443d4d 100644 --- a/web/components/workspace/create-workspace-form.tsx +++ b/web/components/workspace/create-workspace-form.tsx @@ -121,7 +121,10 @@ export const CreateWorkspaceForm: FC = observer((props) => {
- + = observer((props) => { />
- +
{window && window.location.host}/ = observer((props) => { )}
- What size is your organization? + + What size is your organization?* +
Date: Wed, 20 Mar 2024 19:28:19 +0530 Subject: [PATCH 25/71] dev: add -p flag for the logs folder (#4007) --- apiserver/Dockerfile.api | 2 +- apiserver/Dockerfile.dev | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apiserver/Dockerfile.api b/apiserver/Dockerfile.api index c5113e059..34a50334a 100644 --- a/apiserver/Dockerfile.api +++ b/apiserver/Dockerfile.api @@ -48,7 +48,7 @@ USER root RUN apk --no-cache add "bash~=5.2" COPY ./bin ./bin/ -RUN mkdir /code/plane/logs +RUN mkdir -p /code/plane/logs RUN chmod +x ./bin/takeoff ./bin/worker ./bin/beat RUN chmod -R 777 /code RUN chown -R captain:plane /code diff --git a/apiserver/Dockerfile.dev b/apiserver/Dockerfile.dev index bd6684fd5..06f15231c 100644 --- a/apiserver/Dockerfile.dev +++ b/apiserver/Dockerfile.dev @@ -35,6 +35,7 @@ RUN addgroup -S plane && \ COPY . . +RUN mkdir -p /code/plane/logs RUN chown -R captain.plane /code RUN chmod -R +x /code/bin RUN chmod -R 777 /code From 6ab8588afb7cad15307f4a912e83f14c6b1d3db0 Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:32:42 +0530 Subject: [PATCH 26/71] fix: create label inline overflow (#4006) --- web/components/labels/project-setting-label-list.tsx | 2 +- .../[workspaceSlug]/projects/[projectId]/settings/labels.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/components/labels/project-setting-label-list.tsx b/web/components/labels/project-setting-label-list.tsx index 9193fdd0b..bbc4023d1 100644 --- a/web/components/labels/project-setting-label-list.tsx +++ b/web/components/labels/project-setting-label-list.tsx @@ -96,7 +96,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => { Add label
-
+
{showLabelForm && (
{ return ( <> -
+
From 180f1d74e877260f3f87d17515e5bafd33ce2dd3 Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:59:31 +0530 Subject: [PATCH 27/71] dev: removed double issue count (#4008) --- apiserver/plane/api/views/module.py | 12 ++++++++++++ apiserver/plane/app/views/cycle/base.py | 5 +++++ apiserver/plane/app/views/module/base.py | 12 ++++++++++++ apiserver/plane/app/views/workspace/module.py | 6 ++++++ 4 files changed, 35 insertions(+) diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 460722f99..88bb1b05e 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -67,6 +67,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ), ) .annotate( @@ -77,6 +78,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -87,6 +89,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -97,6 +100,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -107,6 +111,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -117,6 +122,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .order_by(self.kwargs.get("order_by", "-created_at")) @@ -486,6 +492,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ), ) .annotate( @@ -496,6 +503,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -506,6 +514,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -516,6 +525,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -526,6 +536,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -536,6 +547,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .order_by(self.kwargs.get("order_by", "-created_at")) diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index b70db4c11..f2b493502 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -107,6 +107,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): issue_cycle__issue__archived_at__isnull=True, issue_cycle__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -117,6 +118,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): issue_cycle__issue__archived_at__isnull=True, issue_cycle__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -127,6 +129,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): issue_cycle__issue__archived_at__isnull=True, issue_cycle__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -137,6 +140,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): issue_cycle__issue__archived_at__isnull=True, issue_cycle__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -147,6 +151,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): issue_cycle__issue__archived_at__isnull=True, issue_cycle__issue__is_draft=False, ), + distinct=True, ) ) .annotate( diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index f6329c223..2e4b1024d 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -86,6 +86,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ), ) .annotate( @@ -96,6 +97,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -106,6 +108,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -116,6 +119,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -126,6 +130,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -136,6 +141,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -517,6 +523,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ), ) .annotate( @@ -527,6 +534,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -537,6 +545,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -547,6 +556,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -557,6 +567,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -567,6 +578,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( diff --git a/apiserver/plane/app/views/workspace/module.py b/apiserver/plane/app/views/workspace/module.py index fbd760271..8dd5e97f4 100644 --- a/apiserver/plane/app/views/workspace/module.py +++ b/apiserver/plane/app/views/workspace/module.py @@ -45,6 +45,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ), ) .annotate( @@ -55,6 +56,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -65,6 +67,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -75,6 +78,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -85,6 +89,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .annotate( @@ -95,6 +100,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__issue__archived_at__isnull=True, issue_module__issue__is_draft=False, ), + distinct=True, ) ) .order_by(self.kwargs.get("order_by", "-created_at")) From 054dd2bb7df38715f19b7b10a416ba609242060f Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:00:13 +0530 Subject: [PATCH 28/71] fix: module empty state (#4004) --- web/components/modules/modules-list-view.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/components/modules/modules-list-view.tsx b/web/components/modules/modules-list-view.tsx index a4cb7a652..ce8ce4e65 100644 --- a/web/components/modules/modules-list-view.tsx +++ b/web/components/modules/modules-list-view.tsx @@ -10,6 +10,7 @@ import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from // assets // constants import { EmptyStateType } from "@/constants/empty-state"; +import { calculateTotalFilters } from "@/helpers/filter.helper"; import { useApplication, useEventTracker, useModule, useModuleFilter } from "@/hooks/store"; import AllFiltersImage from "public/empty-state/module/all-filters.svg"; import NameFilterImage from "public/empty-state/module/name-filter.svg"; @@ -22,10 +23,12 @@ export const ModulesListView: React.FC = observer(() => { const { commandPalette: commandPaletteStore } = useApplication(); const { setTrackElement } = useEventTracker(); const { getFilteredModuleIds, loader } = useModule(); - const { currentProjectDisplayFilters: displayFilters, searchQuery } = useModuleFilter(); + const { currentProjectDisplayFilters: displayFilters, searchQuery, currentProjectFilters } = useModuleFilter(); // derived values const filteredModuleIds = projectId ? getFilteredModuleIds(projectId.toString()) : undefined; + const totalFilters = calculateTotalFilters(currentProjectFilters ?? {}); + if (loader || !filteredModuleIds) return ( <> @@ -35,7 +38,7 @@ export const ModulesListView: React.FC = observer(() => { ); - if (filteredModuleIds.length === 0) + if (totalFilters > 0 || searchQuery.trim() !== "") return (
From 37c5ce54d558d79835ebf49b5c91ae829556623a Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:00:34 +0530 Subject: [PATCH 29/71] fix: peek overview sub issue operation (#4002) --- .../issues/sub-issues/issue-list-item.tsx | 16 ++++++++++++++-- web/components/issues/sub-issues/root.tsx | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/web/components/issues/sub-issues/issue-list-item.tsx b/web/components/issues/sub-issues/issue-list-item.tsx index c3842e6b2..825f19796 100644 --- a/web/components/issues/sub-issues/issue-list-item.tsx +++ b/web/components/issues/sub-issues/issue-list-item.tsx @@ -45,6 +45,8 @@ export const IssueListItem: React.FC = observer((props) => { setPeekIssue, issue: { getIssueById }, subIssues: { subIssueHelpersByIssueId, setSubIssueHelpers }, + toggleCreateIssueModal, + toggleDeleteIssueModal, } = useIssueDetail(); const project = useProject(); const { getProjectStates } = useProjectState(); @@ -139,7 +141,12 @@ export const IssueListItem: React.FC = observer((props) => {
{disabled && ( - handleIssueCrudState("update", parentIssueId, { ...issue })}> + { + handleIssueCrudState("update", parentIssueId, { ...issue }); + toggleCreateIssueModal(true); + }} + >
Edit issue @@ -148,7 +155,12 @@ export const IssueListItem: React.FC = observer((props) => { )} {disabled && ( - handleIssueCrudState("delete", parentIssueId, issue)}> + { + handleIssueCrudState("delete", parentIssueId, issue); + toggleDeleteIssueModal(true); + }} + >
Delete issue diff --git a/web/components/issues/sub-issues/root.tsx b/web/components/issues/sub-issues/root.tsx index 9c02fbcc4..b9c40f6e2 100644 --- a/web/components/issues/sub-issues/root.tsx +++ b/web/components/issues/sub-issues/root.tsx @@ -57,6 +57,7 @@ export const SubIssuesRoot: FC = observer((props) => { toggleCreateIssueModal, isSubIssuesModalOpen, toggleSubIssuesModal, + toggleDeleteIssueModal, } = useIssueDetail(); const { setTrackElement, captureIssueEvent } = useEventTracker(); // state @@ -496,6 +497,7 @@ export const SubIssuesRoot: FC = observer((props) => { isOpen={issueCrudState?.update?.toggle} onClose={() => { handleIssueCrudState("update", null, null); + toggleCreateIssueModal(false); }} data={issueCrudState?.update?.issue ?? undefined} onSubmit={async (_issue: TIssue) => { @@ -521,6 +523,7 @@ export const SubIssuesRoot: FC = observer((props) => { isOpen={issueCrudState?.delete?.toggle} handleClose={() => { handleIssueCrudState("delete", null, null); + toggleDeleteIssueModal(false); }} data={issueCrudState?.delete?.issue as TIssue} onSubmit={async () => From 3ff0f6187a92542844b8fbb570049788b756dc5a Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:41:49 +0530 Subject: [PATCH 30/71] [WEB-762] chore: cycle, module modal improvement and cycle, module (#4000) * chore: cycle modal description improvement * chore: updated the description ui in cycles and modules --------- Co-authored-by: gurusainath --- web/components/cycles/form.tsx | 2 +- web/components/cycles/sidebar.tsx | 14 ++++++++------ web/components/modules/form.tsx | 2 +- web/components/modules/sidebar.tsx | 9 ++++++--- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/web/components/cycles/form.tsx b/web/components/cycles/form.tsx index 27b972044..f8092f8d0 100644 --- a/web/components/cycles/form.tsx +++ b/web/components/cycles/form.tsx @@ -113,7 +113,7 @@ export const CycleForm: React.FC = (props) => { id="cycle_description" name="description" placeholder="Description..." - className="!h-24 w-full resize-none text-sm" + className="w-full text-sm resize-none min-h-24" hasError={Boolean(errors?.description)} value={value} onChange={onChange} diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index e142b95ec..ed6fe4b75 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -8,7 +8,7 @@ import { Disclosure, Transition } from "@headlessui/react"; // icons import { ICycle } from "@plane/types"; // ui -import { Avatar, CustomMenu, Loader, LayersIcon, TOAST_TYPE, setToast } from "@plane/ui"; +import { Avatar, CustomMenu, Loader, LayersIcon, TOAST_TYPE, setToast, TextArea } from "@plane/ui"; // components import { SidebarProgressStats } from "@/components/core"; import ProgressChart from "@/components/core/sidebar/progress-chart"; @@ -219,8 +219,8 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { ? "0 Issue" : `${cycleDetails.progress_snapshot.completed_issues}/${cycleDetails.progress_snapshot.total_issues}` : cycleDetails.total_issues === 0 - ? "0 Issue" - : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; + ? "0 Issue" + : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; const daysLeft = findHowManyDaysLeft(cycleDetails.end_date); @@ -290,9 +290,11 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
{cycleDetails.description && ( - - {cycleDetails.description} - +