From a361dae185039d110a503e69aa9dc5c814fc3d20 Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Mon, 16 Oct 2023 13:26:18 +0530 Subject: [PATCH] chore: cycles revamp using MobX (#2443) * chore: kanban refactoring * chore: Implemented new kanaban board UX and implemented draggable using react beautiful dnd * chore: updated yarn lock * chore: updated the store for issues and issue filters * chore: resolved build error * chore: created filters and updated the issue filters, display_filter and display_properties in mobx and components * chore: implemented filters for issues * chore: UI theming updates * chore: handled single and multi select in filter cards * chore: implemented filters and views in kanaban * chore: updating filters, display_filter and display properties * chore: filter, layout, display filters, extra filters and display properties render validation * chore: clean up and resolved import warnings * chore: type check * chore: renamed gantt key to gantt_chart * chore: filter render UI and Functionality implementation * chore: filter empty state handling in issue filter selection * Implementing list view * chore: kanban drag drop logic * filtering * chore: store setup * chore: handled build issues * chore: store setup * user filter * chore: store setup * chore: store fixes and static data setup * chore: store setup for build fixes * fix: merge conflicts (#2231) * chore: dynamic position dropdown (#2138) * chore: dynamic position state dropdown for issue view * style: state select dropdown styling * fix: state icon attribute names * chore: state select dynamic dropdown * chore: member select dynamic dropdown * chore: label select dynamic dropdown * chore: priority select dynamic dropdown * chore: label select dropdown improvement * refactor: state dropdown location * chore: dropdown improvement and code refactor * chore: dynamic dropdown hook type added --------- Co-authored-by: Aaryan Khandelwal * fix: fields not getting selected in the create issue form (#2212) * fix: hydration error and draft issue workflow * fix: build error * fix: properties getting de-selected after create, module & cycle not getting auto-select on the form * fix: display layout, props being updated directly * chore: sub issues count in individual issue (#2221) * fix: service imports * chore: rename csv service file --------- Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> * chore: store fixes * chore: update issue detail store to handle peek overview (#2237) * chore: dynamic position dropdown (#2138) * chore: dynamic position state dropdown for issue view * style: state select dropdown styling * fix: state icon attribute names * chore: state select dynamic dropdown * chore: member select dynamic dropdown * chore: label select dynamic dropdown * chore: priority select dynamic dropdown * chore: label select dropdown improvement * refactor: state dropdown location * chore: dropdown improvement and code refactor * chore: dynamic dropdown hook type added --------- Co-authored-by: Aaryan Khandelwal * fix: fields not getting selected in the create issue form (#2212) * fix: hydration error and draft issue workflow * fix: build error * fix: properties getting de-selected after create, module & cycle not getting auto-select on the form * fix: display layout, props being updated directly * chore: sub issues count in individual issue (#2221) * Implemented nested issues in the sub issues section in issue detail page (#2233) * feat: subissues infinte level * feat: updated UI for sub issues * feat: subissues new ui and nested sub issues in issue detail * chore: removed repeated code * refactor: product updates modal layout (#2225) * fix: handle no issues in custom analytics (#2226) * fix: activity label color (#2227) * fix: profile issues layout switch (#2228) * chore: update service imports * chore: update issue detail store to handle peek overview --------- Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Co-authored-by: guru_sainath * chore: minor fixes * workspace project fixes * feat: project issues topbar (#2256) * chore: project issues topbar * style: theming and minor UI fixes * refactor: file structure * chore: layout wise authorization added * style: filter dropdowns * chore: add fetch keys * chore: minor fixes * chore: filters dropdown (#2260) * chore: project issues topbar * style: theming and minor UI fixes * refactor: file structure * chore: layout wise authorization added * style: filter dropdowns * chore: add fetch keys * feat: search option for filters * fix: sticky headers * chore: sub_group_by section added * fix: leave project fixes * refactor: project card component refactor * Implemented swimlanes and kanban view (#2262) * chore: issue store for kanban and calendar * chore: updated ui for kanba and swimlanes * chore: yarn.lock updated * fix: computed filters logic * chore: added sub_group_by in params and handled sub-group-by render error in display filter's * fix: ui package setup and project update form refactor * fix: ui package setup * fix: minor ui fixes * dev: calendar view layout revamp (#2293) * dev: calendar view init * chore: new render logic * chore: implement calendar view * chore: calendar view * refactor: calendar payload * chore: remove active month logic from backend * chore: setup new store for calendar * refactor: issues fetching structure * chore: months dropdown * chore: modify request query params for calendar layout * refactor: remove console logs and add comments * chore: removed demo m-store routes * cycles changes * chore: issues grouped kanban and swimlanes UI and functionality (#2294) * chore: updated the all the group_by and sub_group_by UI and functionality render in kanban * chore: kanban sorting in mobx and ui updates * chore: ui changes and drag and drop functionality changes in kanban * chore: issues count render in kanban default and swimlanes * chore: Added icons to the group_by and sub_group_by in kanban and swimlanes * refactor: filter components, constants and helper functions (#2297) * refactor: filters and display filters to accept handlers as props * refactor: filters and display filters folder structure * refactor: change issue layout options constant structure * chore: display filters validations * chore: view less filters functionality * fix: display filters validation * refactor: wrap functions around useCallback * chore: start and target date filter options added * refactor: query params generator function * fix: query params generator function * dev: gantt chart implementation using MobX (#2302) * dev: fetch project gantt issues using mobx * chore: handle group by options in the kanban layout * dev: spreadsheet layout implementation using MobX (#2306) * dev: implement spreadsheet view using mobx * refactor: remove console logs and props * chore: refactoring cycles list * feat: adding additional ui components * dev: applied filters list implementation using MobX (#2325) * dev: applied filters list UI * fix: filter item height * chore: remove unnecessary classes * fix: params generator * fix: cycles views list and board * fix: cycles list rendering fixes * fix: layout fixes * refactor: filter components (#2359) * fix: calendar layout dividers * refactor: filter selection components * fix: dropdown closing after selection * refactor: filters components * chore: issue properties for list and kanban layouts and implemented estimates in project store (#2363) * chore: issue properties for state, priorit, labels and members * feat: implemented assignee, labels properties * fix: implemented estimates in project store and issue properties * chore: staer_date and due_date and validation properties in kanban * chore: filters import conflict * dev: setup module and module filter store (#2364) * dev: implement module issues using mobx store * dev: module filter store setup * chore: module store crud operations * chore: issue list layout (#2367) * chore: merge develop (#2388) * fix: build erros * chore: cycles, modules store integration, list and kanban layouts and updated kanban logic (#2399) * chore: cycle, cycle-issue, cycle-filters, cycle-kanban, cycle layout setup * chore: cycles kanban and list view store * chore: cycles, modules kanban and list, kanban view store * refactor: change naming convention (#2383) * fix:auth layer revamp * chore: Implemented list and kanban views in project modules (#2402) * chore: updated kanban logic in project cycles and modules * chore: updated list and kanban in module * dev: implement global views using MobX (#2404) * fix: selfhosted fixes (#2154) * fix: selfhosted fixes * fix: updated env example * chore: dynamic position dropdown (#2138) * chore: dynamic position state dropdown for issue view * style: state select dropdown styling * fix: state icon attribute names * chore: state select dynamic dropdown * chore: member select dynamic dropdown * chore: label select dynamic dropdown * chore: priority select dynamic dropdown * chore: label select dropdown improvement * refactor: state dropdown location * chore: dropdown improvement and code refactor * chore: dynamic dropdown hook type added --------- Co-authored-by: Aaryan Khandelwal * fix: fields not getting selected in the create issue form (#2212) * fix: hydration error and draft issue workflow * fix: build error * fix: properties getting de-selected after create, module & cycle not getting auto-select on the form * fix: display layout, props being updated directly * chore: sub issues count in individual issue (#2221) * Implemented nested issues in the sub issues section in issue detail page (#2233) * feat: subissues infinte level * feat: updated UI for sub issues * feat: subissues new ui and nested sub issues in issue detail * chore: removed repeated code * refactor: product updates modal layout (#2225) * fix: handle no issues in custom analytics (#2226) * fix: activity label color (#2227) * fix: profile issues layout switch (#2228) * fix: issues resolved in sub issues (#2238) * fix: aws region name (#2234) * chore: updated docker naming conventions (#2239) * naming convention changes * dev: update docker-compose-hub in consistent with docker-compose * dev: updated docker container name --------- Co-authored-by: sriram veeraghanta * chore: added state and priority order in workspace user profile (#2241) * fix: changed priority from None to none (#2229) * fix: cycle and module stats when issues are archived (#2185) * fix: cycle and module stats when issues are archived * fix: added draft filter --------- Co-authored-by: NarayanBavisetti * feat: quick add (#2240) * feat: quick add * style: made text color muted * chore: added epoch in draft (#2244) * chore: added epoch in draft * chore: removed extra spaces * fix: resolved pending issue graph in analytics, user wishes in dashboard, and typo in projects list (#2247) * style: settings page improvement (#2211) * style: settings page improvement * style: toggle switch styling --------- Co-authored-by: Anmol Singh Bhatia * chore: changed priority props in workspace and project (#2253) * fix: bug fix related to fetching dropdown options for the profile issue (#2246) * fix: sub issue state and member select build error (#2254) * rename view to layout (#2255) Co-authored-by: Your Name * fix: bug fixes and ui improvement (#2250) * dev: remove auto filter endpoint * feat: quick-add placement in spreadsheet and gantt (#2259) * feat: sticking quick-add at the bottom of the screen fix: opening create issue modal instead of quick-add in draft-issues, my-issue and profile page * fix: build error due to dynamic import * fix: draft issue delete not working (#2249) * fix: draft issue not deleting, project can't be changed in draft issue modal * fix: removed mutation for view where draft issues are not shown * fix: inline create issue for draft issue * fix: clearing data from localstorage on discard click * feat: Add peek overview in sub issues and updated UI for empty states. (#2263) * chore: add tooltip to show full time on activity logs (#2235) * fix: issue automation iterable error (#2208) * fix: n+1 queries for cycle list and project member endpoints (#2257) * [fix] nginx continuously rewriting and reloading on index page of spaces app (#2236) * chore: shifted index page to /home route * chore: added rewrite logic, to rewrite index to /home * chore: routed home to login route as login page * chore: updated nginx config to route to login * chore: updated path for home * dev: migration for 0.13 (#2266) * dev: updated migrations * dev: migration for 0.13 * dev: re-split migrations into two different files (#2268) * dev: split issue activity migration separate files * dev: resplit migrations into two different files * dev: changed the batch size * chore: udpate date filters to support dynamic options * fix: bugs in quick-add and draft issues (#2269) * fix: 'Last Drafted Issue' making sidebar look weird on collapsed * feat: scroll to the bottom when issue is created * fix: 'Add Issue' button overlapping issue card in spreadsheet view * fix: wrong placement of quick-add in calender layout * fix: spacing for issue card in spreadsheet view * chore: add instructions to contributing guide (#2270) * chore: add instructions to contributing guide * dev: update contributing.md to use the new configuration --------- Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com> * fix: user dashboard greeting timezone (#2267) * chore: user greeting timezone * fix: group by labels not working on workspace level * feat: workspace global view, style: spreadsheet view revamp (#2273) * chore: workspace view types, services and hooks added * style: spreadsheet view revamp and code refactor * feat: workspace view * fix: build fix * chore: sidebar workspace issues redirection updated * style: gantt layout quick-add padding (#2272) * fix: 'Last Drafted Issue' making sidebar look weird on collapsed * feat: scroll to the bottom when issue is created * fix: 'Add Issue' button overlapping issue card in spreadsheet view * fix: wrong placement of quick-add in calender layout * fix: spacing for issue card in spreadsheet view * style: gantt layout quick-add padding style: removed 'State group' from draft issue * style: decrese shadow, quick-add position on calender layout, and 'add issue' sticky * style: button color * fix: block click happening while moving (#2275) * dev: refactor date filters to a single function * chore: handle calendar date range in frontend (#2277) * chore: gantt chart empty state (#2279) * chore: gantt empty state * chore: Add heading to the gantt sidebar * style: calender quick-add same width as single date (#2280) * style: calender quick-add same width as single date * style: margin bottom in quick-add in spreadsheet view * fix: quick add opening in list-layout * style: reduced margin left * chore: updated created at in draft issue (#2278) * chore: make target dates inclusive when filtering (#2276) * chore: sort order and issue props for global views (#2283) * chore: removed project filter (#2284) * fix: inbox issue deletes (#2290) * chore: views (#2288) * chore: global views order by * chore: update permissions for global views --------- Co-authored-by: NarayanBavisetti * chore: fetch issues from previous and next month in the calendar view (#2282) * fix: issue activity estimate value bug fix (#2281) * fix: issue activity estimate value bug fix * fix: activity typo fix * fix: ui and bugs (#2289) * fix: 24 character limit on first & last name in onboarding page * fix: no option: 'Add Issue' in archive issue page * fix: in archive issue directly sending to issue detail page * fix: issue type showing in archive issue * fix: custom menu overflowing * fix: changing subscriber in filters has no effect * style: border in quick-add * fix: on onboarding member role overflowing * fix: inconsistent icons in issue detail * style: spacing, borders and shadows in quick-add * fix: custom menu truncate * fix: notifications for created by me and assigned to me (#2292) * chore: workspace view display filters and properties , code refactor (#2295) * chore: spreadsheet view context * chore: spreadsheet context provider * chore: spreadsheet view context * chore: display filters and properties added in workspace view and code refactor * fix: build error fix * chore: set sub-issue display option to false for global views --------- Co-authored-by: gurusainath * chore: label create error (#2299) * chore: global issues ui improvement and bug fixes (#2300) * chore: workspace view mutation fix ,bug fixes and code refactor (#2301) * chore: workspace view mutation fix ,bug fixes and code refactor * chore: update workspace view toast alert added * chore: workspace view order by removed (#2303) * dev: updated migrations for 0.13-dev (#2305) * chore: epoch migration batch size changed * chore: reoredered the migration files * dev: updated migrations for 0.13-dev * chore: added epoch field * dev: merged the migration files * fix: workspace view filters count fix (#2307) * fix: unsplash api fix (#2310) * fix: workspace view redirection fix, style: spreadsheet view shadow scroll fix (#2314) * fix: workspace view redirection fix * style: spreadsheet view scroll shadow fix * fix: update build workflow for the deploy app (#2315) * fix: workspace view add issue mutation fix (#2317) * dev: create action to sync PR changes to the repo (#2333) * fix: ui package readme added (#2334) * fix: variable name for token (#2336) * dev: update add permissions to the action (#2337) * dev: rename token variables (#2338) * fix: updated readme fixes (#2339) * dev: update sync workflow to run only when the source repo is configured (#2346) * dev: update sync workflow to run only when the source repo is configured * fix: naming convention changes --------- Co-authored-by: sriramveeraghanta * fix: issue relation mutation and draft issue (#2340) * fix: issue relation mutation and draft issue * fix: 'New Issue' in gantt view fix: emoji select going under * fix: profile page typo * fix: sync workflow fixes (#2365) * fix: sync job pr description escaped values fix (#2366) * Update index.tsx (#2343) Fixes #2342 * dev: update apiserver configuration files (#2348) * dev: update apiserver configuration files * dev: add email and minio redirection urls * fix: themening validation in store init. (#2350) * chore: member can change role (#2371) * chore: removed the issue draft log from my profile (#2368) * adding sync info in pr title (#2373) * chore: layout access validation and switch in plane deploy issues route (#2351) * chore: handled route validation and layout access validation in plane deploy issues * chore: impoved validation condition * show current version in the help section dropdown (#2353) * fix: table menu positioning (#2354) * fix: handle cross project issues in the sub-issues. (#2357) * fix: login process validation based on api config (#2361) * dev: configuration endpoint for frontend client (#2355) * dev: configuration endpoint for frontend clients * dev: configuration enable magic and email/password signup * dev: update unsplash keys * dev: add unsplash API and add env for magic login * fix: 404 when redirecting user clicks on Sign In button (#2349) * fix: 404 when redirecting user to login page * fix: next_path redirection not working * fix: authentication workflow update in plane deploy --------- Co-authored-by: gurusainath * fix: project setting member role validation (#2369) * fix: project setting member role validation * chore: opacity removed from member setting page * chore: member setting page validation * chore: project covers endpoint (#2370) * chore: project covers endpoint * dev: remove print logs * dev: formatting --------- Co-authored-by: sriramveeraghanta * feat: default project cover images tab on the change cover popover (#2375) * feat: default project cover images tab * chore: remove unnecessary env vars from turbo.json * chore: remove unnecessary OAuth envs (#2378) * chore: remove unnecessary oauth envs * merge conflicts resolved * fix: adding new service --------- Co-authored-by: sriramveeraghanta * fix: added user store variables in mobx store observable (#2380) * fix: state group icons (#2381) * fix: removed default theme setting in the index page (#2382) * fix: removed default theme setting in the index page * fix: empty space * dev: global views and workspace filters store implemented * sync CE Master to EE Develop * refactor: create update view modal * chore: static issue global views * refactor: remove old code * refactor: filters select dropdown * chore: fix calendar layout * chore: mobx store for new applied filters * chore: dded search functionality --------- Co-authored-by: Vamsi Kurama Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Co-authored-by: sriram veeraghanta Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Co-authored-by: guru_sainath Co-authored-by: NarayanBavisetti Co-authored-by: Anmol Singh Bhatia Co-authored-by: Rhea Jain <65884341+rhea0110@users.noreply.github.com> Co-authored-by: Your Name Co-authored-by: pablohashescobar Co-authored-by: Henit Chobisa Co-authored-by: Thomas Co-authored-by: Luis Cruz <55716036+luis-cruzt@users.noreply.github.com> Co-authored-by: Manish Gupta * fix: Auth fixes and Layout fixes (#2408) * fix: auth fixes and layout improvements * fix: layout fixes * fix: analytics page fixes * dev: implemented project views using MobX (#2410) * dev: implemented project views list using mobx * style: views list UI * dev: implemented view issues page using mobx * refactor: project view issues fetching * chore: plane ui library component and code refactor (#2406) * chore: swap input component with plane/ui package * chore: swap textarea component with plane/ui package * chore: swap button component with plane/ui package * chore: button component revamp * fix: button type fix * chore: secondary button revamp * chore: button props updated * chore: swap loader component with plane/ui package * fix: build error fix * chore: button component refactor * chore: code refactor * chore: swap toggle switch component with plane/ui package * chore: swap spinner component with plane/ui package * chore: swap progress bar componenet with plan/ui package * chore: code refactor * chore: cycles revamp * fix: gitignore fixes * chore: updated cycles view adn layout mutation * fix: project card fixes * chore: ui component revamp (#2415) * chore: swap tooltip component with plane ui package * chore: swap linear progress component with plane ui package * fix: login button fix * chore: implement new worksapace wrapper for global views (#2412) * chore: implement new worksapace wrapper for global views pages * fix: merge conflicts * fix: merge conflicts * dev: add remaining layouts to cycle (#2413) * fix: workspace auth wrapper changes * chore: project card revamp and refactor (#2416) * removing dist from ui * chore: handled edit and delete operation in cycle board view, and gantt view * refactor: analytics (#2419) * refactor: helper functions * chore: updated all the page headers * refactor: custom analytics * refactor: project analytics modal * refactor: folder structure, remove junk code (#2423) * refactor: folder structure * chore: ad order by target date option * refactor: remove old layout components * refactor: inbox folder structure * fix: services fixes * fix: store imports changes * fix: services export fixes * fix: services implementation fixes * fix: build issue fixes * fix: react library fixes * refactor: MobX store folder structure (#2435) * refactor: store folder structure * chore: update import statements * fix: service import errors (#2436) * fix: service imports * chore: update service imports in store * chore: fix remianing service imports * build fixes * editor ts config fixes * fix: turbo and build fixes * fix: Auth screen loading implementation * fix: build issues * fix: turbo settings for ui package * chore: project active cycles requestes changed from swr to mobx * chore: imports and structuring codebase in cycles * chore: removed legacy code from cycles --------- Co-authored-by: sriram veeraghanta Co-authored-by: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Co-authored-by: Aaryan Khandelwal Co-authored-by: Vamsi Kurama Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Co-authored-by: NarayanBavisetti Co-authored-by: Anmol Singh Bhatia Co-authored-by: Rhea Jain <65884341+rhea0110@users.noreply.github.com> Co-authored-by: Your Name Co-authored-by: pablohashescobar Co-authored-by: Henit Chobisa Co-authored-by: Thomas Co-authored-by: Luis Cruz <55716036+luis-cruzt@users.noreply.github.com> Co-authored-by: Manish Gupta --- web/components/common/empty-state.tsx | 12 +- .../cycles/active-cycle-details.tsx | 150 +++---- .../cycles/cycle-create-edit-modal.tsx | 134 ++++++ web/components/cycles/cycle-delete-modal.tsx | 121 ++++++ web/components/cycles/cycles-board-card.tsx | 85 +++- web/components/cycles/cycles-board.tsx | 6 +- web/components/cycles/cycles-list-item.tsx | 390 ++++++++++-------- web/components/cycles/cycles-list.tsx | 9 +- web/components/cycles/cycles-view-legacy.tsx | 255 ------------ web/components/cycles/cycles-view.tsx | 42 +- web/components/cycles/delete-cycle-modal.tsx | 176 -------- .../cycles/gantt-chart/cycles-list-layout.tsx | 1 + web/components/cycles/index.ts | 1 - web/components/cycles/sidebar.tsx | 19 +- web/lib/local-storage.ts | 21 + .../projects/[projectId]/cycles/index.tsx | 218 +++++----- web/store/cycle/cycles.store.ts | 130 +++++- web/types/cycles.d.ts | 8 +- 18 files changed, 913 insertions(+), 865 deletions(-) create mode 100644 web/components/cycles/cycle-create-edit-modal.tsx create mode 100644 web/components/cycles/cycle-delete-modal.tsx delete mode 100644 web/components/cycles/cycles-view-legacy.tsx delete mode 100644 web/components/cycles/delete-cycle-modal.tsx create mode 100644 web/lib/local-storage.ts diff --git a/web/components/common/empty-state.tsx b/web/components/common/empty-state.tsx index e39b10801..6cccf614e 100644 --- a/web/components/common/empty-state.tsx +++ b/web/components/common/empty-state.tsx @@ -28,22 +28,14 @@ export const EmptyState: React.FC = ({ isFullScreen = true, disabled = false, }) => ( -
+
{primaryButton?.text}
{title}
{description &&

{description}

}
{primaryButton && ( - + {primaryButton.icon} {primaryButton.text} diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index 09ee9853d..eb1fb8a09 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -1,14 +1,12 @@ -import React from "react"; - +import { MouseEvent } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; - import useSWR, { mutate } from "swr"; - // services import { CycleService } from "services/cycle.service"; // hooks import useToast from "hooks/use-toast"; +import { useMobxStore } from "lib/mobx/store-provider"; // ui import { AssigneesList } from "components/ui/avatar"; import { SingleProgressStats } from "components/core"; @@ -16,7 +14,7 @@ import { Loader, Tooltip, LinearProgressIndicator } from "@plane/ui"; // components import ProgressChart from "components/core/sidebar/progress-chart"; import { ActiveCycleProgressStats } from "components/cycles"; - +import { ViewIssueLabel } from "components/issues"; // icons import { CalendarDaysIcon } from "@heroicons/react/20/solid"; import { PriorityIcon } from "components/icons/priority-icon"; @@ -31,8 +29,6 @@ import { StateGroupIcon, } from "components/icons"; import { StarIcon } from "@heroicons/react/24/outline"; -// components -import { ViewIssueLabel } from "components/issues"; // helpers import { getDateRangeStatus, renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper"; import { truncateText } from "helpers/string.helper"; @@ -69,34 +65,43 @@ const stateGroups = [ }, ]; -// services -const cycleService = new CycleService(); +interface IActiveCycleDetails { + workspaceSlug: string; + projectId: string; +} + +export const ActiveCycleDetails: React.FC = (props) => { + // services + const cycleService = new CycleService(); -export const ActiveCycleDetails: React.FC = () => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + + const { workspaceSlug, projectId } = props; + + const { cycle: cycleStore } = useMobxStore(); const { setToastAlert } = useToast(); - const { data: currentCycle } = useSWR( - workspaceSlug && projectId ? CURRENT_CYCLE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, "current") - : null + const { isLoading } = useSWR( + workspaceSlug && projectId ? `ACTIVE_CYCLE_ISSUE_${projectId}_CURRENT` : null, + workspaceSlug && projectId ? () => cycleStore.fetchCycles(workspaceSlug, projectId, "current") : null ); - const cycle = currentCycle ? currentCycle[0] : null; - const { data: issues } = useSWR( - workspaceSlug && projectId && cycle?.id ? CYCLE_ISSUES_WITH_PARAMS(cycle?.id, { priority: "urgent,high" }) : null, - workspaceSlug && projectId && cycle?.id - ? () => - cycleService.getCycleIssuesWithParams(workspaceSlug as string, projectId as string, cycle.id, { - priority: "urgent,high", - }) - : null - ) as { data: IIssue[] | undefined }; + const activeCycle = cycleStore.cycles?.[projectId] || null; + const cycle = activeCycle ? activeCycle[0] : null; + const issues = (cycleStore?.active_cycle_issues as any) || null; - if (!currentCycle) + // const { data: issues } = useSWR( + // workspaceSlug && projectId && cycle?.id ? CYCLE_ISSUES_WITH_PARAMS(cycle?.id, { priority: "urgent,high" }) : null, + // workspaceSlug && projectId && cycle?.id + // ? () => + // cycleService.getCycleIssuesWithParams(workspaceSlug as string, projectId as string, cycle.id, { + // priority: "urgent,high", + // }) + // : null + // ) as { data: IIssue[] | undefined }; + + if (isLoading) return ( @@ -146,70 +151,28 @@ export const ActiveCycleDetails: React.FC = () => { const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); - const handleAddToFavorites = () => { - if (!workspaceSlug || !projectId || !cycle) return; + const handleAddToFavorites = (e: MouseEvent) => { + e.preventDefault(); + if (!workspaceSlug || !projectId) return; - mutate( - CURRENT_CYCLE_LIST(projectId as string), - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - mutate( - CYCLES_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - cycleService - .addCycleToFavorites(workspaceSlug as string, projectId as string, { - cycle: cycle.id, - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't add the cycle to favorites. Please try again.", - }); - }); - }; - - const handleRemoveFromFavorites = () => { - if (!workspaceSlug || !projectId || !cycle) return; - - mutate( - CURRENT_CYCLE_LIST(projectId as string), - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - mutate( - CYCLES_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - cycleService.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id).catch(() => { + cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { setToastAlert({ type: "error", title: "Error!", - message: "Couldn't remove the cycle from favorites. Please try again.", + message: "Couldn't add the cycle to favorites. Please try again.", + }); + }); + }; + + const handleRemoveFromFavorites = (e: MouseEvent) => { + e.preventDefault(); + if (!workspaceSlug || !projectId) return; + + cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Couldn't add the cycle to favorites. Please try again.", }); }); }; @@ -296,8 +259,7 @@ export const ActiveCycleDetails: React.FC = () => { {cycle.is_favorite ? ( - ) : ( - - )} -
- - {!isCompleted && ( - - - - Edit Cycle - - - )} - {!isCompleted && ( - - - - Delete cycle - - - )} - - - - Copy cycle link - - - -
-
+ > + {cycleStatus === "current" ? ( + + {cycle.total_issues > 0 ? ( + <> + + {Math.floor((cycle.completed_issues / cycle.total_issues) * 100)} % + + ) : ( + No issues present + )} + + ) : cycleStatus === "upcoming" ? ( + + Yet to start + + ) : cycleStatus === "completed" ? ( + + + {100} % + + ) : ( + + + {cycleStatus} + + )} + + + + {/* cycle favorite */} + {cycle.is_favorite ? ( + + ) : ( + + )}
-
- - + + +
+ +
+ + {!isCompleted && ( + setUpdateModal(true)}> + + + Edit Cycle + + + )} + + {!isCompleted && ( + setDeleteModal(true)}> + + + Delete cycle + + + )} + + + + + Copy cycle link + + + +
- + + setUpdateModal(false)} + onSubmit={updateModalCallback} + workspaceSlug={workspaceSlug} + projectId={projectId} + /> + + setDeleteModal(false)} + onSubmit={deleteModalCallback} + workspaceSlug={workspaceSlug} + projectId={projectId} + /> + ); }; diff --git a/web/components/cycles/cycles-list.tsx b/web/components/cycles/cycles-list.tsx index ec8a40837..947bd1fea 100644 --- a/web/components/cycles/cycles-list.tsx +++ b/web/components/cycles/cycles-list.tsx @@ -1,17 +1,20 @@ import { FC } from "react"; +// components +import { CyclesListItem } from "./cycles-list-item"; // ui import { Loader } from "@plane/ui"; // types import { ICycle } from "types"; -import { CyclesListItem } from "./cycles-list-item"; export interface ICyclesList { cycles: ICycle[]; filter: string; + workspaceSlug: string; + projectId: string; } export const CyclesList: FC = (props) => { - const { cycles, filter } = props; + const { cycles, filter, workspaceSlug, projectId } = props; return (
@@ -22,7 +25,7 @@ export const CyclesList: FC = (props) => { {cycles.map((cycle) => (
- +
))} diff --git a/web/components/cycles/cycles-view-legacy.tsx b/web/components/cycles/cycles-view-legacy.tsx deleted file mode 100644 index 55c67524b..000000000 --- a/web/components/cycles/cycles-view-legacy.tsx +++ /dev/null @@ -1,255 +0,0 @@ -import React, { useState } from "react"; -import { useRouter } from "next/router"; -import { KeyedMutator, mutate } from "swr"; -// services -import { CycleService } from "services/cycle.service"; -// hooks -import useToast from "hooks/use-toast"; -import useUserAuth from "hooks/use-user-auth"; -import useLocalStorage from "hooks/use-local-storage"; -// components -import { - CreateUpdateCycleModal, - CyclesListGanttChartView, - DeleteCycleModal, - SingleCycleCard, - SingleCycleList, -} from "components/cycles"; -// ui -import { Loader } from "@plane/ui"; -// helpers -import { getDateRangeStatus } from "helpers/date-time.helper"; -// types -import { ICycle } from "types"; -// fetch-keys -import { - COMPLETED_CYCLES_LIST, - CURRENT_CYCLE_LIST, - CYCLES_LIST, - DRAFT_CYCLES_LIST, - UPCOMING_CYCLES_LIST, -} from "constants/fetch-keys"; - -type Props = { - cycles: ICycle[] | undefined; - mutateCycles?: KeyedMutator; - viewType: string | null; -}; - -const cycleService = new CycleService(); - -export const CyclesView: React.FC = ({ cycles, mutateCycles, viewType }) => { - const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false); - const [selectedCycleToUpdate, setSelectedCycleToUpdate] = useState(null); - - const [deleteCycleModal, setDeleteCycleModal] = useState(false); - const [selectedCycleToDelete, setSelectedCycleToDelete] = useState(null); - - const { storedValue: cycleTab } = useLocalStorage("cycle_tab", "all"); - console.log("cycleTab", cycleTab); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { user } = useUserAuth(); - const { setToastAlert } = useToast(); - - const handleEditCycle = (cycle: ICycle) => { - setSelectedCycleToUpdate(cycle); - setCreateUpdateCycleModal(true); - }; - - const handleDeleteCycle = (cycle: ICycle) => { - setSelectedCycleToDelete(cycle); - setDeleteCycleModal(true); - }; - - const handleAddToFavorites = (cycle: ICycle) => { - if (!workspaceSlug || !projectId) return; - - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); - - const fetchKey = - cycleStatus === "current" - ? CURRENT_CYCLE_LIST(projectId as string) - : cycleStatus === "upcoming" - ? UPCOMING_CYCLES_LIST(projectId as string) - : cycleStatus === "completed" - ? COMPLETED_CYCLES_LIST(projectId as string) - : DRAFT_CYCLES_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - mutate( - CYCLES_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - cycleService - .addCycleToFavorites(workspaceSlug as string, projectId as string, { - cycle: cycle.id, - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't add the cycle to favorites. Please try again.", - }); - }); - }; - - const handleRemoveFromFavorites = (cycle: ICycle) => { - if (!workspaceSlug || !projectId) return; - - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); - - const fetchKey = - cycleStatus === "current" - ? CURRENT_CYCLE_LIST(projectId as string) - : cycleStatus === "upcoming" - ? UPCOMING_CYCLES_LIST(projectId as string) - : cycleStatus === "completed" - ? COMPLETED_CYCLES_LIST(projectId as string) - : DRAFT_CYCLES_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - mutate( - CYCLES_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - cycleService.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id).catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't remove the cycle from favorites. Please try again.", - }); - }); - }; - - return ( - <> - setCreateUpdateCycleModal(false)} - data={selectedCycleToUpdate} - user={user} - /> - - {cycles ? ( - cycles.length > 0 ? ( - viewType === "list" ? ( -
- {cycles.map((cycle) => ( -
-
- handleDeleteCycle(cycle)} - handleEditCycle={() => handleEditCycle(cycle)} - handleAddToFavorites={() => handleAddToFavorites(cycle)} - handleRemoveFromFavorites={() => handleRemoveFromFavorites(cycle)} - /> -
-
- ))} -
- ) : viewType === "board" ? ( -
- {cycles.map((cycle) => ( - handleDeleteCycle(cycle)} - handleEditCycle={() => handleEditCycle(cycle)} - handleAddToFavorites={() => handleAddToFavorites(cycle)} - handleRemoveFromFavorites={() => handleRemoveFromFavorites(cycle)} - /> - ))} -
- ) : ( - - ) - ) : ( -
-
-
- - - - -
-

- {cycleTab === "all" ? "No cycles" : `No ${cycleTab} cycles`} -

- -
-
- ) - ) : viewType === "list" ? ( - - - - - - ) : viewType === "board" ? ( - - - - - - ) : ( - - - - )} - - ); -}; diff --git a/web/components/cycles/cycles-view.tsx b/web/components/cycles/cycles-view.tsx index 2404c27da..36955398e 100644 --- a/web/components/cycles/cycles-view.tsx +++ b/web/components/cycles/cycles-view.tsx @@ -4,35 +4,39 @@ import { observer } from "mobx-react-lite"; // store import { useMobxStore } from "lib/mobx/store-provider"; // components -import { CyclesBoard, CyclesList } from "components/cycles"; -import { Loader } from "@plane/ui"; +import { CyclesBoard, CyclesList, CyclesListGanttChartView } from "components/cycles"; +// ui components +import { Loader } from "components/ui"; +// types +import { TCycleLayout } from "types"; export interface ICyclesView { filter: "all" | "current" | "upcoming" | "draft" | "completed" | "incomplete"; - view: "list" | "board" | "gantt"; + layout: TCycleLayout; workspaceSlug: string; projectId: string; } export const CyclesView: FC = observer((props) => { - const { filter, view, workspaceSlug, projectId } = props; + const { filter, layout, workspaceSlug, projectId } = props; + // store const { cycle: cycleStore } = useMobxStore(); + // api call to fetch cycles list const { isLoading } = useSWR( - workspaceSlug && projectId ? `CYCLES_LIST_${projectId}_${filter}` : null, - workspaceSlug && projectId ? () => cycleStore.fetchCycles(workspaceSlug, projectId, filter) : null + workspaceSlug && projectId && filter ? `CYCLES_LIST_${projectId}_${filter}` : null, + workspaceSlug && projectId && filter ? () => cycleStore.fetchCycles(workspaceSlug, projectId, filter) : null ); const cyclesList = cycleStore.cycles?.[projectId]; - console.log("cyclesList", cyclesList); return ( <> - {view === "list" && ( + {layout === "list" && ( <> {!isLoading ? ( - + ) : ( @@ -42,10 +46,11 @@ export const CyclesView: FC = observer((props) => { )} )} - {view === "board" && ( + + {layout === "board" && ( <> {!isLoading ? ( - + ) : ( @@ -55,7 +60,20 @@ export const CyclesView: FC = observer((props) => { )} )} - {view === "gantt" && } + + {layout === "gantt" && ( + <> + {!isLoading ? ( + + ) : ( + + + + + + )} + + )} ); }); diff --git a/web/components/cycles/delete-cycle-modal.tsx b/web/components/cycles/delete-cycle-modal.tsx deleted file mode 100644 index 7c219fec6..000000000 --- a/web/components/cycles/delete-cycle-modal.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import React, { useState } from "react"; -// next -import { useRouter } from "next/router"; -// swr -import { mutate } from "swr"; -// headless ui -import { Dialog, Transition } from "@headlessui/react"; -// services -import { CycleService } from "services/cycle.service"; -// hooks -import useToast from "hooks/use-toast"; -// ui -import { Button } from "@plane/ui"; -// icons -import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; -// types -import type { IUser, ICycle, IProject } from "types"; -type TConfirmCycleDeletionProps = { - isOpen: boolean; - setIsOpen: React.Dispatch>; - data?: ICycle | null; - user: IUser | undefined; -}; -// fetch-keys -import { - COMPLETED_CYCLES_LIST, - CURRENT_CYCLE_LIST, - CYCLES_LIST, - DRAFT_CYCLES_LIST, - PROJECT_DETAILS, - UPCOMING_CYCLES_LIST, -} from "constants/fetch-keys"; -import { getDateRangeStatus } from "helpers/date-time.helper"; - -// services -const cycleService = new CycleService(); - -export const DeleteCycleModal: React.FC = ({ isOpen, setIsOpen, data, user }) => { - const [isDeleteLoading, setIsDeleteLoading] = useState(false); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { setToastAlert } = useToast(); - - const handleClose = () => { - setIsOpen(false); - setIsDeleteLoading(false); - }; - - const handleDeletion = async () => { - if (!data || !workspaceSlug || !projectId) return; - - setIsDeleteLoading(true); - - await cycleService - .deleteCycle(workspaceSlug as string, data.project, data.id, user) - .then(() => { - const cycleType = getDateRangeStatus(data.start_date, data.end_date); - const fetchKey = - cycleType === "current" - ? CURRENT_CYCLE_LIST(projectId as string) - : cycleType === "upcoming" - ? UPCOMING_CYCLES_LIST(projectId as string) - : cycleType === "completed" - ? COMPLETED_CYCLES_LIST(projectId as string) - : DRAFT_CYCLES_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => { - if (!prevData) return; - - return prevData.filter((cycle) => cycle.id !== data?.id); - }, - false - ); - - mutate( - CYCLES_LIST(projectId as string), - (prevData: any) => { - if (!prevData) return; - return prevData.filter((cycle: any) => cycle.id !== data?.id); - }, - false - ); - - // update total cycles count in the project details - mutate( - PROJECT_DETAILS(projectId.toString()), - (prevData) => { - if (!prevData) return prevData; - - return { - ...prevData, - total_cycles: prevData.total_cycles - 1, - }; - }, - false - ); - - handleClose(); - - setToastAlert({ - title: "Success", - type: "success", - message: "Cycle deleted successfully", - }); - }) - .catch(() => { - setIsDeleteLoading(false); - }); - }; - - return ( - - - -
- - -
-
- - -
-
-
-
-
- - Delete Cycle - -
-

- Are you sure you want to delete cycle-{" "} - {data?.name}? All of the - data related to the cycle will be permanently removed. This action cannot be undone. -

-
-
-
-
-
- - -
-
-
-
-
-
-
- ); -}; diff --git a/web/components/cycles/gantt-chart/cycles-list-layout.tsx b/web/components/cycles/gantt-chart/cycles-list-layout.tsx index b994c8f39..49d461d1a 100644 --- a/web/components/cycles/gantt-chart/cycles-list-layout.tsx +++ b/web/components/cycles/gantt-chart/cycles-list-layout.tsx @@ -16,6 +16,7 @@ import { CycleGanttBlock, CycleGanttSidebarBlock } from "components/cycles"; import { ICycle } from "types"; type Props = { + workspaceSlug: string; cycles: ICycle[]; mutateCycles?: KeyedMutator; }; diff --git a/web/components/cycles/index.ts b/web/components/cycles/index.ts index 3f74be500..76f0d9773 100644 --- a/web/components/cycles/index.ts +++ b/web/components/cycles/index.ts @@ -3,7 +3,6 @@ export * from "./active-cycle-details"; export * from "./active-cycle-stats"; export * from "./gantt-chart"; export * from "./cycles-view"; -export * from "./delete-cycle-modal"; export * from "./form"; export * from "./modal"; export * from "./select"; diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 676f3e06e..2872fd122 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -11,7 +11,7 @@ import useToast from "hooks/use-toast"; // components import { SidebarProgressStats } from "components/core"; import ProgressChart from "components/core/sidebar/progress-chart"; -import { DeleteCycleModal } from "components/cycles"; +import { CycleDeleteModal } from "components/cycles/cycle-delete-modal"; // ui import { CustomMenu, CustomRangeDatePicker } from "components/ui"; import { Loader, ProgressBar } from "@plane/ui"; @@ -49,7 +49,11 @@ export const CycleDetailsSidebar: React.FC = ({ cycle, isOpen, cycleStatu const [cycleDeleteModal, setCycleDeleteModal] = useState(false); const router = useRouter(); - const { workspaceSlug, projectId, cycleId } = router.query; + const { workspaceSlug, projectId, cycleId } = router.query as { + workspaceSlug: string; + projectId: string; + cycleId: string; + }; const { setToastAlert } = useToast(); @@ -261,7 +265,16 @@ export const CycleDetailsSidebar: React.FC = ({ cycle, isOpen, cycleStatu return ( <> - + {cycle && ( + setCycleDeleteModal(false)} + onSubmit={() => {}} + workspaceSlug={workspaceSlug} + projectId={projectId} + /> + )}
{ + if (typeof window === undefined || typeof window === "undefined") return null; + try { + const item = window.localStorage.getItem(key); + return item ? item : null; + } catch (error) { + window.localStorage.removeItem(key); + return null; + } +}; + +export const setLocalStorage = (key: string, value: any) => { + if (key && value) { + const _value = value ? (["string", "boolean"].includes(typeof value) ? value : JSON.stringify(value)) : null; + if (_value) window.localStorage.setItem(key, _value); + } +}; + +export const removeLocalStorage = (key: string) => { + if (key) window.localStorage.removeItem(key); +}; diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index f1b7f5ea2..0e4d3d5db 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -1,25 +1,22 @@ -import React, { useEffect, useState } from "react"; +import { Fragment, useCallback, useEffect, useState } from "react"; import { useRouter } from "next/router"; import { Tab } from "@headlessui/react"; import useSWR from "swr"; -// hooks -import useLocalStorage from "hooks/use-local-storage"; -import useUserAuth from "hooks/use-user-auth"; +import { Plus } from "lucide-react"; // layouts import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy"; // components -import { CyclesView, ActiveCycleDetails, CreateUpdateCycleModal } from "components/cycles"; +import { CyclesView, ActiveCycleDetails } from "components/cycles"; +import { CycleCreateEditModal } from "components/cycles/cycle-create-edit-modal"; // ui import { Button } from "@plane/ui"; import { EmptyState } from "components/common"; import { Icon } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; -// icons -import { PlusIcon } from "@heroicons/react/24/outline"; // images import emptyCycle from "public/empty-state/cycle.svg"; // types -import { SelectCycleType } from "types"; +import { TCycleView, TCycleLayout } from "types"; import type { NextPage } from "next"; // helper import { truncateText } from "helpers/string.helper"; @@ -27,52 +24,66 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { observer } from "mobx-react-lite"; // constants import { CYCLE_TAB_LIST, CYCLE_VIEWS } from "constants/cycle"; - -type ICycleAPIFilter = "all" | "current" | "upcoming" | "draft" | "completed" | "incomplete"; -type ICycleView = "list" | "board" | "gantt"; +// lib cookie +import { setLocalStorage, getLocalStorage } from "lib/local-storage"; const ProjectCyclesPage: NextPage = observer(() => { + const [createModal, setCreateModal] = useState(false); + const createOnSubmit = () => {}; + + // store + const { project: projectStore, cycle: cycleStore } = useMobxStore(); + // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - // store - const { project: projectStore } = useMobxStore(); - const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null; - // states - const [selectedCycle, setSelectedCycle] = useState(); - const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false); - // local storage - const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage("cycle_tab", "all"); - const { storedValue: cyclesView, setValue: setCyclesView } = useLocalStorage("cycle_view", "list"); - // hooks - const { user } = useUserAuth(); - // api call fetch project details + const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; + useSWR( workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null, - workspaceSlug && projectId - ? () => { - projectStore.fetchProjectDetails(workspaceSlug.toString(), projectId.toString()); - } - : null + workspaceSlug && projectId ? () => projectStore.fetchProjectDetails(workspaceSlug, projectId) : null ); - /** - * Clearing form data after closing the modal - */ - useEffect(() => { - if (createUpdateCycleModal) return; + const handleCurrentLayout = useCallback( + (_layout: TCycleLayout) => { + if (projectId) { + setLocalStorage(`cycle_layout:${projectId}`, _layout); + cycleStore.setCycleLayout(_layout); + } + }, + [cycleStore, projectId] + ); - const timer = setTimeout(() => { - setSelectedCycle(undefined); - clearTimeout(timer); - }, 500); - }, [createUpdateCycleModal]); + const handleCurrentView = useCallback( + (_view: TCycleView) => { + if (projectId) { + setLocalStorage(`cycle_view:${projectId}`, _view); + cycleStore.setCycleView(_view); + if (_view === "draft" && cycleStore.cycleLayout === "gantt") { + handleCurrentLayout("list"); + } + } + }, + [cycleStore, projectId, handleCurrentLayout] + ); useEffect(() => { - if (cycleTab === "draft" && cyclesView === "gantt") { - setCyclesView("list"); + if (projectId) { + const _viewKey = `cycle_view:${projectId}`; + const _viewValue = getLocalStorage(_viewKey); + if (_viewValue && _viewValue !== cycleStore?.cycleView) cycleStore.setCycleView(_viewValue as TCycleView); + else handleCurrentView("all"); + + const _layoutKey = `cycle_layout:${projectId}`; + const _layoutValue = getLocalStorage(_layoutKey); + if (_layoutValue && _layoutValue !== cycleStore?.cycleView) + cycleStore.setCycleLayout(_layoutValue as TCycleLayout); + else handleCurrentLayout("list"); } - }, [cycleTab, cyclesView, setCyclesView]); + }, [projectId, cycleStore, handleCurrentView, handleCurrentLayout]); + + const projectDetails = projectId ? projectStore.project_details[projectId] : null; + const cycleView = cycleStore?.cycleView; + const cycleLayout = cycleStore?.cycleLayout; return ( { right={ } > - setCreateUpdateCycleModal(false)} - data={selectedCycle} - user={user} + setCreateModal(false)} + onSubmit={createOnSubmit} /> + {projectDetails?.total_cycles === 0 ? (
{ description="Cycle is a custom time period in which a team works to complete items on their backlog." image={emptyCycle} primaryButton={{ - icon: , + icon: , text: "New Cycle", onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "q", - }); - document.dispatchEvent(e); + setCreateModal(true); }, }} /> @@ -123,14 +132,10 @@ const ProjectCyclesPage: NextPage = observer(() => { i.key === cycleTab)} - selectedIndex={CYCLE_TAB_LIST.findIndex((i) => i.key === cycleTab)} + defaultIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleStore?.cycleView)} + selectedIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleStore?.cycleView)} onChange={(i) => { - try { - setCycleTab(CYCLE_TAB_LIST[i].key); - } catch (e) { - setCycleTab(CYCLE_TAB_LIST[0].key); - } + handleCurrentView(CYCLE_TAB_LIST[i].key as TCycleView); }} >
@@ -148,67 +153,74 @@ const ProjectCyclesPage: NextPage = observer(() => { ))} -
- {CYCLE_VIEWS.map((view) => { - if (cycleTab === "active") return null; - if (view.key === "gantt" && cycleTab === "draft") return null; - - return ( - - ); - })} -
+ {CYCLE_VIEWS && CYCLE_VIEWS.length > 0 && cycleStore?.cycleView != "active" && ( +
+ {CYCLE_VIEWS.map((view) => { + if (view.key === "gantt" && cycleStore?.cycleView === "draft") return null; + return ( + + ); + })} +
+ )}
- + + - {cycleTab && cyclesView && workspaceSlug && projectId && ( + {cycleView && cycleLayout && workspaceSlug && projectId && ( )} + - + + - {cycleTab && cyclesView && workspaceSlug && projectId && ( + {cycleView && cycleLayout && workspaceSlug && projectId && ( )} + - {cycleTab && cyclesView && workspaceSlug && projectId && ( + {cycleView && cycleLayout && workspaceSlug && projectId && ( )} + - {cycleTab && cyclesView && workspaceSlug && projectId && ( + {cycleView && cycleLayout && workspaceSlug && projectId && ( )} diff --git a/web/store/cycle/cycles.store.ts b/web/store/cycle/cycles.store.ts index d3767c88a..d68609a4a 100644 --- a/web/store/cycle/cycles.store.ts +++ b/web/store/cycle/cycles.store.ts @@ -1,7 +1,8 @@ import { action, computed, observable, makeObservable, runInAction } from "mobx"; // types +import { ICycle, TCycleView, TCycleLayout, CycleDateCheckData, IIssue } from "types"; +// mobx import { RootStore } from "../root"; -import { ICycle } from "types"; // services import { ProjectService } from "services/project"; import { IssueService } from "services/issue"; @@ -11,6 +12,9 @@ export interface ICycleStore { loader: boolean; error: any | null; + cycleView: TCycleView; + cycleLayout: TCycleLayout; + cycleId: string | null; cycles: { [project_id: string]: ICycle[]; @@ -18,21 +22,31 @@ export interface ICycleStore { cycle_details: { [cycle_id: string]: ICycle; }; + active_cycle_issues: { + [cycle_id: string]: IIssue[]; + }; // computed getCycleById: (cycleId: string) => ICycle | null; // actions + setCycleView: (_cycleView: TCycleView) => void; + setCycleLayout: (_cycleLayout: TCycleLayout) => void; setCycleId: (cycleId: string) => void; + validateDate: (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => Promise; + fetchCycles: ( workspaceSlug: string, projectId: string, params: "all" | "current" | "upcoming" | "draft" | "completed" | "incomplete" ) => Promise; fetchCycleWithId: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; + fetchActiveCycleIssues: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; + createCycle: (workspaceSlug: string, projectId: string, data: any) => Promise; updateCycle: (workspaceSlug: string, projectId: string, cycleId: string, data: any) => Promise; + removeCycle: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; addCycleToFavorites: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; removeCycleFromFavorites: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; @@ -42,6 +56,9 @@ export class CycleStore implements ICycleStore { loader: boolean = false; error: any | null = null; + cycleView: TCycleView = "all"; + cycleLayout: TCycleLayout = "list"; + cycleId: string | null = null; cycles: { [project_id: string]: ICycle[]; @@ -51,6 +68,10 @@ export class CycleStore implements ICycleStore { [cycle_id: string]: ICycle; } = {}; + active_cycle_issues: { + [cycle_id: string]: IIssue[]; + } = {}; + // root store rootStore; // services @@ -63,20 +84,31 @@ export class CycleStore implements ICycleStore { loader: observable, error: observable.ref, + cycleView: observable, + cycleLayout: observable, + cycleId: observable, cycles: observable.ref, cycle_details: observable.ref, + active_cycle_issues: observable.ref, // computed projectCycles: computed, // actions + setCycleView: action, + setCycleLayout: action, setCycleId: action, getCycleById: action, fetchCycles: action, fetchCycleWithId: action, + + fetchActiveCycleIssues: action, + createCycle: action, + updateCycle: action, + removeCycle: action, addCycleToFavorites: action, removeCycleFromFavorites: action, @@ -97,8 +129,18 @@ export class CycleStore implements ICycleStore { getCycleById = (cycleId: string) => this.cycle_details[cycleId] || null; // actions - setCycleId = (cycleId: string) => { - this.cycleId = cycleId; + setCycleView = (_cycleView: TCycleView) => (this.cycleView = _cycleView); + setCycleLayout = (_cycleLayout: TCycleLayout) => (this.cycleLayout = _cycleLayout); + setCycleId = (cycleId: string) => (this.cycleId = cycleId); + + validateDate = async (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => { + try { + const response = await this.cycleService.cycleDateCheck(workspaceSlug, projectId, payload); + return response; + } catch (error) { + console.log("Failed to validate cycle dates", error); + throw error; + } }; fetchCycles = async ( @@ -112,6 +154,8 @@ export class CycleStore implements ICycleStore { const cyclesResponse = await this.cycleService.getCyclesWithParams(workspaceSlug, projectId, params); + if (this.cycleView === "active") this.fetchActiveCycleIssues(workspaceSlug, projectId, cyclesResponse[0].id); + runInAction(() => { this.cycles = { ...this.cycles, @@ -144,6 +188,27 @@ export class CycleStore implements ICycleStore { } }; + fetchActiveCycleIssues = async (workspaceSlug: string, projectId: string, cycleId: string) => { + try { + const _cycleIssues = await this.cycleService.getCycleIssuesWithParams(workspaceSlug, projectId, cycleId, { + priority: `urgent,high`, + }); + + const _activeCycleIssues = { + ...this.active_cycle_issues, + [cycleId]: _cycleIssues as IIssue[], + }; + + runInAction(() => { + this.active_cycle_issues = _activeCycleIssues; + }); + + return _activeCycleIssues; + } catch (error) { + console.log("error"); + } + }; + createCycle = async (workspaceSlug: string, projectId: string, data: any) => { try { const response = await this.cycleService.createCycle( @@ -154,12 +219,15 @@ export class CycleStore implements ICycleStore { ); runInAction(() => { - this.cycles = { - ...this.cycles, - [projectId]: [...this.cycles[projectId], response], + this.cycle_details = { + ...this.cycle_details, + [response?.id]: response, }; }); + const _currentView = this.cycleView === "active" ? "current" : this.cycleView; + this.fetchCycles(workspaceSlug, projectId, _currentView); + return response; } catch (error) { console.log("Failed to create cycle from cycle store"); @@ -171,23 +239,18 @@ export class CycleStore implements ICycleStore { try { const response = await this.cycleService.updateCycle(workspaceSlug, projectId, cycleId, data, undefined); - const _cycles = { - ...this.cycles, - [projectId]: this.cycles[projectId].map((cycle) => { - if (cycle.id === cycleId) return { ...cycle, ...response }; - return cycle; - }), - }; const _cycleDetails = { ...this.cycle_details, [cycleId]: { ...this.cycle_details[cycleId], ...response }, }; runInAction(() => { - this.cycles = _cycles; this.cycle_details = _cycleDetails; }); + const _currentView = this.cycleView === "active" ? "current" : this.cycleView; + this.fetchCycles(workspaceSlug, projectId, _currentView); + return response; } catch (error) { console.log("Failed to update cycle from cycle store"); @@ -195,6 +258,43 @@ export class CycleStore implements ICycleStore { } }; + patchCycle = async (workspaceSlug: string, projectId: string, cycleId: string, data: any) => { + try { + const _response = await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, data, undefined); + + const _cycleDetails = { + ...this.cycle_details, + [cycleId]: { ...this.cycle_details[cycleId], ..._response }, + }; + + runInAction(() => { + this.cycle_details = _cycleDetails; + }); + + const _currentView = this.cycleView === "active" ? "current" : this.cycleView; + this.fetchCycles(workspaceSlug, projectId, _currentView); + + return _response; + } catch (error) { + console.log("Failed to patch cycle from cycle store"); + throw error; + } + }; + + removeCycle = async (workspaceSlug: string, projectId: string, cycleId: string) => { + try { + const _response = await this.cycleService.deleteCycle(workspaceSlug, projectId, cycleId, undefined); + + const _currentView = this.cycleView === "active" ? "current" : this.cycleView; + this.fetchCycles(workspaceSlug, projectId, _currentView); + + return _response; + } catch (error) { + console.log("Failed to delete cycle from cycle store"); + throw error; + } + }; + addCycleToFavorites = async (workspaceSlug: string, projectId: string, cycleId: string) => { try { runInAction(() => { @@ -237,12 +337,10 @@ export class CycleStore implements ICycleStore { }), }; }); - // updating through api const response = await this.cycleService.removeCycleFromFavorites(workspaceSlug, projectId, cycleId); return response; } catch (error) { console.log("Failed to remove cycle from favorites - Cycle Store", error); - // resetting the local state runInAction(() => { this.cycles = { ...this.cycles, diff --git a/web/types/cycles.d.ts b/web/types/cycles.d.ts index 955e82222..e97aa2133 100644 --- a/web/types/cycles.d.ts +++ b/web/types/cycles.d.ts @@ -9,6 +9,10 @@ import type { IUserLite, } from "types"; +export type TCycleView = "all" | "active" | "upcoming" | "completed" | "draft"; + +export type TCycleLayout = "list" | "board" | "gantt"; + export interface ICycle { backlog_issues: number; cancelled_issues: number; @@ -82,9 +86,7 @@ export interface CycleIssueResponse { sub_issues_count: number; } -export type SelectCycleType = - | (ICycle & { actionType: "edit" | "delete" | "create-issue" }) - | undefined; +export type SelectCycleType = (ICycle & { actionType: "edit" | "delete" | "create-issue" }) | undefined; export type SelectIssue = (IIssue & { actionType: "edit" | "delete" | "create" }) | null;