diff --git a/components/pages/Goals/ArchivePage/index.tsx b/components/pages/Goals/ArchivePage/index.tsx new file mode 100644 index 00000000..8ade8897 --- /dev/null +++ b/components/pages/Goals/ArchivePage/index.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { observer } from 'mobx-react-lite'; +import { GoalsArchiveStoreProvider } from './store'; +import { GoalsArchiveView } from './view'; + +const GoalsArchivePage = observer(function GoalsArchivePage() { + return ( + + + + ); +}); + +export default GoalsArchivePage; diff --git a/components/pages/Goals/ArchivePage/store.ts b/components/pages/Goals/ArchivePage/store.ts new file mode 100644 index 00000000..dc848e51 --- /dev/null +++ b/components/pages/Goals/ArchivePage/store.ts @@ -0,0 +1,40 @@ +import { computed, makeObservable } from 'mobx'; +import { RootStore } from '../../../../stores/RootStore'; +import { getProvider } from '../../../../helpers/StoreProvider'; +import { BaseGoalsStore } from "../stores/BaseGoalsStore"; +import { GoalDataExtended } from "../types"; + +export class GoalsArchiveStore extends BaseGoalsStore { + constructor(public root: RootStore) { + super(root); + + makeObservable(this, { + list: computed, + hasGoals: computed, + }) + } + + get list() { + return Object.entries(this.extendedGoals).reduce((acc, [id, goals]) => { + const archivedGoals = goals.filter((goal) => goal.isArchived); + + if (archivedGoals.length) { + return { + ...acc, + [id]: archivedGoals, + }; + } + + return acc; + }, {} as Record) + } + + get hasGoals() { + return Boolean(Object.keys(this.list).length); + } +} + +export const { + StoreProvider: GoalsArchiveStoreProvider, + useStore: useGoalsArchiveStore +} = getProvider(GoalsArchiveStore); diff --git a/components/pages/Goals/ArchivePage/view.tsx b/components/pages/Goals/ArchivePage/view.tsx new file mode 100644 index 00000000..166a9496 --- /dev/null +++ b/components/pages/Goals/ArchivePage/view.tsx @@ -0,0 +1,77 @@ +import React, { useEffect } from 'react'; +import { observer } from 'mobx-react-lite'; +import Head from 'next/head'; +import { GoalList } from '../components/GoalList'; +import { ModalsSwitcher } from '../../../../helpers/ModalsController'; +import { useGoalsArchiveStore } from './store'; +import { Box, Button, Heading, Text } from "@chakra-ui/react"; +import NextLink from "next/link"; +import { faAngleLeft } from "@fortawesome/pro-light-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useRouter } from "next/navigation"; + +export const GoalsArchiveView = observer(function GoalsArchiveView() { + const { push } = useRouter(); + const store = useGoalsArchiveStore(); + + useEffect(() => { + if (!store.hasGoals) { + push('/goals'); + } + }, [store.hasGoals, push]); + + return ( + <> + + Goals Archive + + {store.hasGoals && ( + <> + + + Archive + + + + + + + + + )} + + ); +}); diff --git a/components/pages/Goals/index.tsx b/components/pages/Goals/MainPage/index.tsx similarity index 91% rename from components/pages/Goals/index.tsx rename to components/pages/Goals/MainPage/index.tsx index 6384f432..6fc58c22 100644 --- a/components/pages/Goals/index.tsx +++ b/components/pages/Goals/MainPage/index.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { observer } from 'mobx-react-lite'; import { GoalsStoreProvider } from './store'; import { GoalsView } from './view'; diff --git a/components/pages/Goals/MainPage/store.ts b/components/pages/Goals/MainPage/store.ts new file mode 100644 index 00000000..ca4acd4f --- /dev/null +++ b/components/pages/Goals/MainPage/store.ts @@ -0,0 +1,80 @@ +import { action, computed, makeObservable, observable } from 'mobx'; +import { RootStore } from '../../../../stores/RootStore'; +import { getProvider } from '../../../../helpers/StoreProvider'; +import { BaseGoalsStore, GoalsModalsTypes } from "../stores/BaseGoalsStore"; +import { GoalDataExtended } from "../types"; +import { CreateGoalParams } from "../../../../stores/RootStore/Resources/GoalsStore"; + +export class GoalsStore extends BaseGoalsStore { + keymap = { + CREATE_GOAL: ['n'], + }; + + hotkeysHandlers = { + CREATE_GOAL: () => { + this.startGoalCreation(); + }, + }; + + constructor(public root: RootStore) { + super(root); + + makeObservable(this, { + keymap: observable, + hotkeysHandlers: observable, + list: computed, + hasGoals: computed, + cloneGoal: action.bound, + startGoalCreation: action.bound, + }); + } + + get list() { + return Object.entries(this.extendedGoals).reduce((acc, [id, goals]) => { + const notArchivedGoals = goals.filter((goal) => !goal.isArchived); + + if (notArchivedGoals.length) { + return { + ...acc, + [id]: notArchivedGoals, + }; + } + + return acc; + }, {} as Record) + } + + get hasGoals() { + return Boolean(Object.keys(this.list).length); + } + + get hasArchivedGoals() { + return this.root.resources.goals.list.some((goal) => goal.isArchived); + } + + cloneGoal = ({ customFields, ...goal }: GoalDataExtended) => { + return this.root.resources.goals.cloneGoal(goal); + } + + startGoalCreation = () => { + this.modals.open({ + type: GoalsModalsTypes.CREATE_OR_UPDATE_GOAL, + props: { + onSave: async ({ + goal: { customFields, ...goal }, + ...otherParams + }: CreateGoalParams) => { + await this.root.resources.goals.add({ goal, ...otherParams }); + await this.loadTaskList(); + this.modals.close(); + }, + onClose: this.modals.close, + }, + }); + }; +} + +export const { + StoreProvider: GoalsStoreProvider, + useStore: useGoalsStore +} = getProvider(GoalsStore); diff --git a/components/pages/Goals/MainPage/view.tsx b/components/pages/Goals/MainPage/view.tsx new file mode 100644 index 00000000..aa23318b --- /dev/null +++ b/components/pages/Goals/MainPage/view.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { observer } from 'mobx-react-lite'; +import Head from 'next/head'; +import { GoalList } from '../components/GoalList'; +import { ModalsSwitcher } from '../../../../helpers/ModalsController'; +import { useGoalsStore } from './store'; +import { useHotkeysHandler } from "../../../../helpers/useHotkeysHandler"; +import { Box, Button, Heading, Text } from "@chakra-ui/react"; +import { GoalCreateNewButton } from "../components/GoalCreateNewButton"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faPlus } from "@fortawesome/pro-regular-svg-icons"; +import { EmptyGoalListMessage } from "../components/EmptyGoalListMessage/EmptyGoalListMessage"; +import NextLink from 'next/link'; +import { faBoxArchive } from "@fortawesome/pro-light-svg-icons"; +import { Tooltip } from "../../../shared/Tooltip"; + +export const GoalsView = observer(function GoalsView() { + const store = useGoalsStore(); + + useHotkeysHandler(store.keymap, store.hotkeysHandlers, { + enabled: !store.modals.isOpen, + keyup: true, + }); + + return ( + <> + + Goals + + + + Goals + + + {store.hasArchivedGoals && ( + + + + + + )} + + {store.hasGoals ? ( + + ) : ( + + )} + + {store.hasGoals && ( + + + + )} + + + + ); +}); diff --git a/components/pages/Goals/components/EmptyGoalListMessage/EmptyGoalListMessage.tsx b/components/pages/Goals/components/EmptyGoalListMessage/EmptyGoalListMessage.tsx index 16aab73b..3703386d 100644 --- a/components/pages/Goals/components/EmptyGoalListMessage/EmptyGoalListMessage.tsx +++ b/components/pages/Goals/components/EmptyGoalListMessage/EmptyGoalListMessage.tsx @@ -3,7 +3,11 @@ import { MascotGoals } from '../../../../images/illustrations/MascotGoals'; import { GoalCreateNewButton } from "../GoalCreateNewButton"; import React from "react"; -export function EmptyGoalListMessage() { +type Props = { + onCreate(): void; +} + +export function EmptyGoalListMessage({ onCreate }: Props) { return ( - + Create new goal 🎯 diff --git a/components/pages/Goals/components/GoalCreateNewButton/index.tsx b/components/pages/Goals/components/GoalCreateNewButton/index.tsx index 62189791..e050b377 100644 --- a/components/pages/Goals/components/GoalCreateNewButton/index.tsx +++ b/components/pages/Goals/components/GoalCreateNewButton/index.tsx @@ -1,6 +1,4 @@ -import { observer } from 'mobx-react-lite'; import { Button, Text, ButtonProps } from '@chakra-ui/react'; -import { useGoalsStore } from '../../store'; import React from 'react'; import { HotkeyBlock } from '../../../../shared/HotkeyBlock'; import { Tooltip } from "../../../../shared/Tooltip"; @@ -10,51 +8,46 @@ type Props = ButtonProps & { withTooltip?: boolean; } -export const GoalCreateNewButton = observer( - function GoalCreateNewButton({ children, withHotkey, withTooltip, ...buttonProps }: Props) { - const store = useGoalsStore(); - - return ( - <> - + + + + {withHotkey && !withTooltip && ( + - - - {withHotkey && !withTooltip && ( - - Press - - - )} - - ); - } -); + Press + + + )} + + ); +} diff --git a/components/pages/Goals/components/GoalEmojiSelect/GoalEmojiSelect.tsx b/components/pages/Goals/components/GoalEmojiSelect/index.tsx similarity index 100% rename from components/pages/Goals/components/GoalEmojiSelect/GoalEmojiSelect.tsx rename to components/pages/Goals/components/GoalEmojiSelect/index.tsx diff --git a/components/pages/Goals/components/GoalItem/index.tsx b/components/pages/Goals/components/GoalList/GoalItem/index.tsx similarity index 85% rename from components/pages/Goals/components/GoalItem/index.tsx rename to components/pages/Goals/components/GoalList/GoalItem/index.tsx index deccf77c..1c6827d1 100644 --- a/components/pages/Goals/components/GoalItem/index.tsx +++ b/components/pages/Goals/components/GoalList/GoalItem/index.tsx @@ -1,7 +1,6 @@ import { observer } from 'mobx-react-lite'; import { Box, chakra, Flex, Text, Tooltip } from '@chakra-ui/react'; -import { useGoalsStore } from '../../store'; -import { DatePicker } from "../../../../shared/DatePicker"; +import { DatePicker } from "../../../../../shared/DatePicker"; import { faAlarmClock, faBoxArchive, @@ -19,11 +18,12 @@ import { } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React from "react"; -import { GoalDataExtended, GoalState, GoalStatus } from "../../types"; -import { ActionMenu } from "../../../../shared/ActionMenu"; -import { EditableTitle } from "../../../../shared/EditableTitle"; -import { DatePickerHelpers } from "../../../../shared/DatePicker/helpers"; -import { GoalEmojiSelect } from "../GoalEmojiSelect/GoalEmojiSelect"; +import { GoalDataExtended, GoalState, GoalStatus } from "../../../types"; +import { ActionMenu } from "../../../../../shared/ActionMenu"; +import { EditableTitle } from "../../../../../shared/EditableTitle"; +import { DatePickerHelpers } from "../../../../../shared/DatePicker/helpers"; +import { GoalEmojiSelect } from "../../GoalEmojiSelect"; +import { useGoalListStore } from "../store"; type Props = { goal: GoalDataExtended @@ -48,9 +48,7 @@ const GOAL_STATE_PARAMS = { } export const GoalItem = observer(function GoalItem({ goal }: Props) { - const store = useGoalsStore(); - - const handleOpenGoal = () => store.editGoal(goal.id); + const store = useGoalListStore(); const isDone = goal.status === GoalStatus.DONE; const isWontDo = goal.status === GoalStatus.WONT_DO; @@ -60,13 +58,13 @@ export const GoalItem = observer(function GoalItem({ goal }: Props) { icon: faSquareArrowUpRight, title: 'Open', command: '↵/⌥O', - onClick: handleOpenGoal + onClick: () => store.callbacks?.onOpenGoal(goal.id), }, { icon: faCircleCheck, title: isDone ? 'Unmark as done' : 'Done', command: '⌥D', - onClick: () => store.updateGoal({ + onClick: () => store.callbacks?.onUpdateGoal({ ...goal, status: isDone ? GoalStatus.TODO : GoalStatus.DONE, }), @@ -75,19 +73,20 @@ export const GoalItem = observer(function GoalItem({ goal }: Props) { icon: faCircleMinus, title: isWontDo ? "Unmark as won't do" : "Won't do", command: '⌥W', - onClick: () => store.wontDoSubmitModalOpen(goal), + onClick: () => store.callbacks?.onWontDo(goal), }, { icon: faClone, title: 'Clone', command: '⌥C', + hidden: !store.hasClone, onClick: () => store.cloneGoal(goal), }, { icon: faBoxArchive, title: goal.isArchived ? 'Unarchive' : 'Archive', command: '⌥A', - onClick: () => store.updateGoal({ + onClick: () => store.callbacks?.onUpdateGoal({ ...goal, isArchived: !goal.isArchived }), @@ -104,14 +103,14 @@ export const GoalItem = observer(function GoalItem({ goal }: Props) { content: 'Are you sure you would like to delete this goal?' }) ) { - await store.deleteGoal(goal.id); + await store.callbacks?.onDeleteGoal(goal.id); } }, }, ]; const handleChangeStartDate = (date: string) => { - return store.updateGoal({ + return store.callbacks?.onUpdateGoal({ ...goal, startDate: date, targetDate: DatePickerHelpers.isStartDateAfterEndDate(date, goal.targetDate) @@ -120,16 +119,16 @@ export const GoalItem = observer(function GoalItem({ goal }: Props) { }); }; const handleChangeTargetDate = (date: string) => { - return store.updateGoal({ ...goal, targetDate: date }); + return store.callbacks?.onUpdateGoal({ ...goal, targetDate: date }); }; const handleChangeTitle = (title: string) => { - return store.updateGoal({ ...goal, title }); + return store.callbacks?.onUpdateGoal({ ...goal, title }); }; const handleChangeIcon = (icon: string) => { - return store.updateGoal({ ...goal, icon: { ...goal.icon, value: icon } }); + return store.callbacks?.onUpdateGoal({ ...goal, icon: { ...goal.icon, value: icon } }); }; const handleColorChange = (color: string) => { - return store.updateGoal({ ...goal, icon: { ...goal.icon, color } }); + return store.callbacks?.onUpdateGoal({ ...goal, icon: { ...goal.icon, color } }); }; return ( @@ -150,7 +149,7 @@ export const GoalItem = observer(function GoalItem({ goal }: Props) { position='relative' cursor='pointer' height={124} - onClick={handleOpenGoal} + onClick={() => store.callbacks?.onOpenGoal(goal.id)} > { - return store.root.resources.spaces.update({ ...space, name }); + return store.updateSpace({ ...space, name }); }; const handleSpaceIconChange = (icon: string) => { - return store.root.resources.spaces.update({ ...space, icon }); + return store.updateSpace({ ...space, icon }); }; const handleSpaceColorChange = (color: string) => { - return store.root.resources.spaces.update({ ...space, color }); + return store.updateSpace({ ...space, color }); }; return ( diff --git a/components/pages/Goals/components/GoalList/index.tsx b/components/pages/Goals/components/GoalList/index.tsx new file mode 100644 index 00000000..0615c912 --- /dev/null +++ b/components/pages/Goals/components/GoalList/index.tsx @@ -0,0 +1,12 @@ +import { observer } from 'mobx-react-lite'; +import { GoalListProps } from "./types"; +import { GoalListStoreProvider } from './store'; +import { GoalListView } from "./view"; + +export const GoalList = observer(function GoalList(props: GoalListProps) { + return ( + + + + ); +}); diff --git a/components/pages/Goals/components/GoalList/store.ts b/components/pages/Goals/components/GoalList/store.ts new file mode 100644 index 00000000..b70f044d --- /dev/null +++ b/components/pages/Goals/components/GoalList/store.ts @@ -0,0 +1,65 @@ +import { makeAutoObservable, toJS } from 'mobx'; +import { RootStore } from '../../../../../stores/RootStore'; +import { getProvider } from '../../../../../helpers/StoreProvider'; +import { GoalDataExtended } from "../../types"; +import { GoalListProps, GoalListCallbacks } from './types'; +import { EDITABLE_TITLE_ID_SLUG } from "../../../../shared/EditableTitle"; +import { SpaceData } from "../../../Spaces/types"; + +export class GoalListStore { + listBySpaces: Record = {}; + callbacks: GoalListCallbacks; + + goalsRefs: Record = {}; + + constructor(public root: RootStore) { + makeAutoObservable(this); + } + + get hasClone() { + return Boolean(this.callbacks?.onCloneGoal); + } + + setGoalRef = (goalId: string, ref: HTMLDivElement) => { + this.goalsRefs[goalId] = ref; + } + + getGoalTitleElement = (goalId: string) => { + return this.goalsRefs[goalId].querySelector( + `#${EDITABLE_TITLE_ID_SLUG}-${goalId}` + ) as HTMLParagraphElement; + }; + + cloneGoal = async (goal: GoalDataExtended) => { + if (!this.hasClone) { + return; + } + + const clonedGoal = await this.callbacks.onCloneGoal(goal); + this.getGoalTitleElement(clonedGoal.id).click(); + } + + getSpace = (spaceId: string) => { + return toJS(this.root.resources.spaces.getById(spaceId)); + } + + updateSpace = (space: SpaceData) => { + return this.root.resources.spaces.update(space); + } + + update = ({ listBySpaces, onUpdateGoal, onDeleteGoal, onCloneGoal, onOpenGoal, onWontDo }: GoalListProps) => { + this.listBySpaces = listBySpaces; + this.callbacks = { + onCloneGoal, + onDeleteGoal, + onUpdateGoal, + onOpenGoal, + onWontDo, + }; + }; +} + +export const { + StoreProvider: GoalListStoreProvider, + useStore: useGoalListStore +} = getProvider(GoalListStore); diff --git a/components/pages/Goals/components/GoalList/types.ts b/components/pages/Goals/components/GoalList/types.ts new file mode 100644 index 00000000..ac3e99c2 --- /dev/null +++ b/components/pages/Goals/components/GoalList/types.ts @@ -0,0 +1,13 @@ +import { GoalData, GoalDataExtended } from "../../types"; + +export type GoalListCallbacks = { + onOpenGoal(goalId: string): void; + onDeleteGoal(goalId: string): Promise; + onUpdateGoal(goal: GoalDataExtended): void; + onWontDo(goal: GoalDataExtended): void; + onCloneGoal?(goal: GoalDataExtended): Promise; +}; + +export type GoalListProps = GoalListCallbacks & { + listBySpaces: Record; +}; diff --git a/components/pages/Goals/components/GoalList/view.tsx b/components/pages/Goals/components/GoalList/view.tsx new file mode 100644 index 00000000..fbc7b414 --- /dev/null +++ b/components/pages/Goals/components/GoalList/view.tsx @@ -0,0 +1,45 @@ +import { observer } from 'mobx-react-lite'; +import { Box, Flex } from '@chakra-ui/react'; +import React from 'react'; +import { faList } from '@fortawesome/pro-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { SpaceItem } from './SpaceItem'; +import { Filters } from "../../../../shared/Filters"; +import { ManArrowToIcon } from "../../../../images/icons/ManArrowToIcon"; +import { ManArrowOffIcon } from "../../../../images/icons/ManArrowOffIcon"; +import { useGoalListStore } from "./store"; + +const GOALS_LIST_FILTERS = [ + { + value: 'all', + label: 'All', + icon: + }, + { + value: 'created-by-me', + label: 'Created by me', + icon: + }, + { + value: 'assigned-to-me', + label: 'Assigned to me', + icon: + }, +]; + +export const GoalListView = observer(function GoalListView() { + const store = useGoalListStore(); + + const haveGoals = store.root.resources.goals.haveGoals; + + return ( + + {haveGoals && } + + {Object.entries(store.listBySpaces).map(([spaceId, goals]) => ( + + ))} + + + ); +}); diff --git a/components/pages/Goals/components/GoalsList/index.tsx b/components/pages/Goals/components/GoalsList/index.tsx deleted file mode 100644 index ec10094b..00000000 --- a/components/pages/Goals/components/GoalsList/index.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { observer } from 'mobx-react-lite'; -import { Box, Heading, Flex } from '@chakra-ui/react'; -import { useGoalsStore } from '../../store'; -import { GoalCreateNewButton } from '../GoalCreateNewButton'; -import React from 'react'; -import { useHotkeysHandler } from '../../../../../helpers/useHotkeysHandler'; -import { faPlus } from '@fortawesome/pro-regular-svg-icons'; -import { faList } from '@fortawesome/pro-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { SpaceItem } from '../SpaceItem'; -import { EmptyGoalListMessage } from '../EmptyGoalListMessage/EmptyGoalListMessage'; -import { Filters } from "../../../../shared/Filters"; -import { ManArrowToIcon } from "../../../../images/icons/ManArrowToIcon"; -import { ManArrowOffIcon } from "../../../../images/icons/ManArrowOffIcon"; - -const GOALS_LIST_FILTERS = [ - { - value: 'all', - label: 'All', - icon: - }, - { - value: 'created-by-me', - label: 'Created by me', - icon: - }, - { - value: 'assigned-to-me', - label: 'Assigned to me', - icon: - }, -] - -export const GoalsList = observer(function GoalsList() { - const store = useGoalsStore(); - - useHotkeysHandler(store.keymap, store.hotkeysHandlers, { - enabled: !store.modals.isOpen, - keyup: true, - }); - - const haveGoals = store.root.resources.goals.haveGoals; - - return ( - - - Goals - - - {haveGoals && } - - - {haveGoals - ? Object.entries(store.extendedGoals).map(([spaceId, goals]) => ( - - )) - : } - - - {haveGoals && ( - - - - )} - - ); -}); diff --git a/components/pages/Goals/modals/GoalCreationModal/components/GoalCreationDescription.tsx b/components/pages/Goals/modals/GoalCreationModal/components/GoalCreationDescription.tsx index 4a2fde80..d6e9e063 100644 --- a/components/pages/Goals/modals/GoalCreationModal/components/GoalCreationDescription.tsx +++ b/components/pages/Goals/modals/GoalCreationModal/components/GoalCreationDescription.tsx @@ -4,7 +4,7 @@ import { useGoalCreationModalStore } from '../store'; import { Editor } from '../../../../../shared/Editor'; import React from "react"; import { FormError } from "../../../../../shared/FormError"; -import { GoalEmojiSelect } from "../../../components/GoalEmojiSelect/GoalEmojiSelect"; +import { GoalEmojiSelect } from "../../../components/GoalEmojiSelect"; export const GoalCreationDescription = observer( function GoalCreationDescription() { diff --git a/components/pages/Goals/store.ts b/components/pages/Goals/stores/BaseGoalsStore.ts similarity index 61% rename from components/pages/Goals/store.ts rename to components/pages/Goals/stores/BaseGoalsStore.ts index c7d3be0b..93e19771 100644 --- a/components/pages/Goals/store.ts +++ b/components/pages/Goals/stores/BaseGoalsStore.ts @@ -1,14 +1,12 @@ -import { makeAutoObservable } from 'mobx'; -import { RootStore } from '../../../stores/RootStore'; -import { getProvider } from '../../../helpers/StoreProvider'; -import { ModalsController } from '../../../helpers/ModalsController'; -import { GoalCreationModal } from './modals/GoalCreationModal'; -import { GoalDataExtended, GoalState, GoalStatus } from './types'; -import { TaskData, TaskStatus } from "../../shared/TasksList/types"; -import { CreateGoalParams } from "../../../stores/RootStore/Resources/GoalsStore"; -import { GoalWontDoSubmitModal } from "./modals/GoalWontDoSubmitModal"; -import moment from "moment"; -import { EDITABLE_TITLE_ID_SLUG } from "../../shared/EditableTitle"; +import { action, computed, makeObservable, observable } from 'mobx'; +import { RootStore } from '../../../../stores/RootStore'; +import { ModalsController } from '../../../../helpers/ModalsController'; +import { GoalCreationModal } from '../modals/GoalCreationModal'; +import { GoalDataExtended, GoalState, GoalStatus } from '../types'; +import { TaskData, TaskStatus } from "../../../shared/TasksList/types"; +import { CreateGoalParams } from "../../../../stores/RootStore/Resources/GoalsStore"; +import { GoalWontDoSubmitModal } from "../modals/GoalWontDoSubmitModal"; +import moment, { Moment } from "moment"; export enum GoalsModalsTypes { CREATE_OR_UPDATE_GOAL, @@ -20,25 +18,28 @@ const GoalsModals = { [GoalsModalsTypes.WONT_DO_SUBMIT]: GoalWontDoSubmitModal, }; -export class GoalsStore { - taskList: TaskData[]; - goalsRefs: Record = {}; +export class BaseGoalsStore { + taskList: TaskData[] = []; + currentDay: Moment = moment(); + + modals = new ModalsController(GoalsModals); constructor(public root: RootStore) { - makeAutoObservable(this); + makeObservable(this, { + taskList: observable, + currentDay: observable, + extendedGoals: computed, + taskListByGoal: computed, + getStateByDate: action.bound, + wontDoSubmitModalOpen: action.bound, + editGoal: action.bound, + updateGoal: action.bound, + deleteGoal: action.bound, + loadTaskList: action.bound, + init: action.bound, + }); } - currentDay = moment(); - keymap = { - CREATE_GOAL: ['n'], - }; - - hotkeysHandlers = { - CREATE_GOAL: () => { - this.startGoalCreation(); - }, - }; - get taskListByGoal() { if (!this.taskList || !this.taskList.length) { return {}; @@ -61,7 +62,7 @@ export class GoalsStore { } get extendedGoals() { - return Object.entries(this.root.resources.goals.map).reduce((acc, [id, goal], index) => ({ + return Object.entries(this.root.resources.goals.map).reduce((acc, [id, goal]) => ({ ...acc, [goal.spaceId]: [ ...(acc[goal.spaceId] ?? []), @@ -79,12 +80,6 @@ export class GoalsStore { }), {} as Record); } - getGoalTitleElement = (goalId: string) => { - return this.goalsRefs[goalId].querySelector( - `#${EDITABLE_TITLE_ID_SLUG}-${goalId}` - ) as HTMLParagraphElement; - }; - getStateByDate = (startDate: string, targetDate: string) => { const start = moment(startDate); const target = moment(targetDate); @@ -101,25 +96,10 @@ export class GoalsStore { } }; - setGoalRef = (goalId: string, ref: HTMLDivElement) => { - this.goalsRefs[goalId] = ref; - } - - modals = new ModalsController(GoalsModals); - - startGoalCreation = () => { - this.modals.open({ - type: GoalsModalsTypes.CREATE_OR_UPDATE_GOAL, - props: { - onSave: this.createGoal, - onClose: this.modals.close, - }, - }); - }; - wontDoSubmitModalOpen = (goal: GoalDataExtended) => { if (goal.status === GoalStatus.WONT_DO) { - return this.updateGoal({ ...goal, status: GoalStatus.TODO }); + this.updateGoal({ ...goal, status: GoalStatus.TODO }); + return; } this.modals.open({ @@ -134,11 +114,6 @@ export class GoalsStore { }); }; - cloneGoal = async ({ customFields, ...goal }: GoalDataExtended) => { - const clonedGoal = await this.root.resources.goals.cloneGoal(goal); - this.getGoalTitleElement(clonedGoal.id).click(); - } - editGoal = (goalId: string) => { this.modals.open({ type: GoalsModalsTypes.CREATE_OR_UPDATE_GOAL, @@ -167,17 +142,8 @@ export class GoalsStore { await this.loadTaskList(); }; - createGoal = async ({ - goal: { customFields, ...goal }, - ...otherParams - }: CreateGoalParams) => { - await this.root.resources.goals.add({ goal, ...otherParams }); - await this.loadTaskList(); - this.modals.close(); - }; - - deleteGoal = async (goalId: string) => { - await this.root.resources.goals.delete([goalId]); + deleteGoal = (goalId: string) => { + return this.root.resources.goals.delete([goalId]); }; loadTaskList = async () => { @@ -190,6 +156,3 @@ export class GoalsStore { update = () => null; } - -export const { StoreProvider: GoalsStoreProvider, useStore: useGoalsStore } = - getProvider(GoalsStore); diff --git a/components/pages/Goals/view.tsx b/components/pages/Goals/view.tsx deleted file mode 100644 index 96935ce8..00000000 --- a/components/pages/Goals/view.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { observer } from 'mobx-react-lite'; -import Head from 'next/head'; -import { GoalsList } from './components/GoalsList'; -import { ModalsSwitcher } from '../../../helpers/ModalsController'; -import { useGoalsStore } from './store'; - -export const GoalsView = observer(function GoalsView() { - const store = useGoalsStore(); - - return ( - <> - - Goals - - - - - ); -}); diff --git a/components/pages/Goals/components/GoalIcon/index.tsx b/components/shared/GoalIcon/index.tsx similarity index 89% rename from components/pages/Goals/components/GoalIcon/index.tsx rename to components/shared/GoalIcon/index.tsx index 2e5a5bb3..fb78cf5c 100644 --- a/components/pages/Goals/components/GoalIcon/index.tsx +++ b/components/shared/GoalIcon/index.tsx @@ -1,5 +1,5 @@ import { Box, Text } from '@chakra-ui/react'; -import { GoalIconData, GoalIconVariants } from '../../types'; +import { GoalIconData, GoalIconVariants } from '../../pages/Goals/types'; import React from 'react'; import { observer } from 'mobx-react-lite'; diff --git a/components/shared/GoalsSelection/view.tsx b/components/shared/GoalsSelection/view.tsx index b1e9f6bb..278fa173 100644 --- a/components/shared/GoalsSelection/view.tsx +++ b/components/shared/GoalsSelection/view.tsx @@ -12,7 +12,7 @@ import { import { GoalsSelectionProps, useGoalsSelectionStore } from './store'; import React, { useRef } from 'react'; import { LargePlusIcon } from '../Icons/LargePlusIcon'; -import { GoalIcon } from '../../pages/Goals/components/GoalIcon'; +import { GoalIcon } from '../GoalIcon'; type GoalSelectionListItemProps = { id: string | null; diff --git a/components/shared/TaskQuickEditor/TaskQuickEditorGoal.tsx b/components/shared/TaskQuickEditor/TaskQuickEditorGoal.tsx index 61e6ae8f..7f31e167 100644 --- a/components/shared/TaskQuickEditor/TaskQuickEditorGoal.tsx +++ b/components/shared/TaskQuickEditor/TaskQuickEditorGoal.tsx @@ -3,7 +3,7 @@ import { Modes, useTaskQuickEditorStore } from './store'; import { Button, ButtonProps, chakra } from '@chakra-ui/react'; import React, { useEffect } from 'react'; import { TaskQuickEditorMenu } from './TaskQuickEditorMenu'; -import { GoalIcon } from '../../pages/Goals/components/GoalIcon'; +import { GoalIcon } from '../GoalIcon'; import { TaskQuickEditorEmptyButton } from './TaskQuickEditorEmptyButton'; export const TaskQuickEditorGoal = observer(function TaskQuickEditorGoal({ diff --git a/components/shared/TaskQuickEditor/modes/GoalModeStore.tsx b/components/shared/TaskQuickEditor/modes/GoalModeStore.tsx index d4e447b1..01733428 100644 --- a/components/shared/TaskQuickEditor/modes/GoalModeStore.tsx +++ b/components/shared/TaskQuickEditor/modes/GoalModeStore.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { makeAutoObservable } from 'mobx'; import { chakra } from '@chakra-ui/react'; -import { GoalIcon } from '../../../pages/Goals/components/GoalIcon'; +import { GoalIcon } from '../../GoalIcon'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faXmark } from '@fortawesome/pro-regular-svg-icons'; import { RootStore } from '../../../../stores/RootStore'; diff --git a/package-lock.json b/package-lock.json index 800cf110..9d843538 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "prosemirror-state": "^1.4.2", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", - "react-datepicker": "4.11.0", + "react-datepicker": "4.10.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.3.8", "tippy.js": "^6.3.7", @@ -7210,9 +7210,9 @@ } }, "node_modules/react-datepicker": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.11.0.tgz", - "integrity": "sha512-50n93o7mQwBEhg05tbopjFKgs8qgi8VBCAOMC4VqrKut72eAjESc/wXS/k5hRtnP0oe2FCGw7MJuIwh37wuXOw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.10.0.tgz", + "integrity": "sha512-6IfBCZyWj54ZZGLmEZJ9c4Yph0s9MVfEGDC2evOvf9AmVz+RRcfP2Czqad88Ff9wREbcbqa4dk7IFYeXF1d3Ag==", "dependencies": { "@popperjs/core": "^2.9.2", "classnames": "^2.2.6", @@ -13628,9 +13628,9 @@ } }, "react-datepicker": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.11.0.tgz", - "integrity": "sha512-50n93o7mQwBEhg05tbopjFKgs8qgi8VBCAOMC4VqrKut72eAjESc/wXS/k5hRtnP0oe2FCGw7MJuIwh37wuXOw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.10.0.tgz", + "integrity": "sha512-6IfBCZyWj54ZZGLmEZJ9c4Yph0s9MVfEGDC2evOvf9AmVz+RRcfP2Czqad88Ff9wREbcbqa4dk7IFYeXF1d3Ag==", "requires": { "@popperjs/core": "^2.9.2", "classnames": "^2.2.6", diff --git a/package.json b/package.json index 7b7159cd..e70c55a7 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "prosemirror-state": "^1.4.2", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", - "react-datepicker": "4.11.0", + "react-datepicker": "4.10.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.3.8", "tippy.js": "^6.3.7", diff --git a/pages/goals/archive.tsx b/pages/goals/archive.tsx new file mode 100644 index 00000000..8fd3778a --- /dev/null +++ b/pages/goals/archive.tsx @@ -0,0 +1,11 @@ +import dynamic from 'next/dynamic'; +import { PageLoader } from '../../components/shared/PageLoader'; + +const DynamicGoalsArchivePage = dynamic(() => import('../../components/pages/Goals/ArchivePage'), { + loading: () => , + ssr: false, +}); + +export default function GoalsArchive() { + return ; +} diff --git a/pages/goals/index.tsx b/pages/goals/index.tsx index f8c07289..c806abf6 100644 --- a/pages/goals/index.tsx +++ b/pages/goals/index.tsx @@ -1,7 +1,7 @@ import dynamic from 'next/dynamic'; import { PageLoader } from '../../components/shared/PageLoader'; -const DynamicGoalsPage = dynamic(() => import('../../components/pages/Goals'), { +const DynamicGoalsPage = dynamic(() => import('../../components/pages/Goals/MainPage'), { loading: () => , ssr: false, });