diff --git a/.eslintrc.json b/.eslintrc.json index dd7a6de13..392b5c2c3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,6 +21,7 @@ "extends": [ "airbnb", "eslint:recommended", + "@typescript-eslint/parser", "plugin:react/recommended", "plugin:import/errors", "plugin:react/recommended", diff --git a/package-lock.json b/package-lock.json index 4be1bb22f..b80ddb7e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "cypress": "^10.11.0", - "eslint": "^8.13.0", + "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^2.7.1", @@ -67,6 +67,15 @@ "vite-plugin-pwa": "^0.12.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@adobe/css-tools": { "version": "4.2.0", "license": "MIT" @@ -1860,21 +1869,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -1890,9 +1901,10 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -1905,8 +1917,9 @@ }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -1915,20 +1928,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.41.0", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -1948,9 +1963,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "dev": true, - "license": "BSD-3-Clause" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -4261,6 +4277,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@zeit/schemas": { "version": "2.29.0", "dev": true, @@ -4284,9 +4306,10 @@ } }, "node_modules/acorn": { - "version": "8.8.2", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -4316,8 +4339,9 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -4355,8 +4379,9 @@ }, "node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4514,8 +4539,9 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/aria-query": { "version": "5.1.3", @@ -6715,26 +6741,28 @@ } }, "node_modules/eslint": { - "version": "8.41.0", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -6744,7 +6772,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -6754,9 +6781,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -7072,9 +7098,10 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -7139,9 +7166,10 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -7198,11 +7226,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -8058,8 +8087,9 @@ }, "node_modules/import-fresh": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -12671,8 +12701,9 @@ }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -12766,8 +12797,9 @@ }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -12883,8 +12915,9 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -13767,16 +13800,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "license": "MIT", "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -13839,8 +13873,9 @@ }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -14082,8 +14117,9 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -15210,8 +15246,9 @@ }, "node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -16376,8 +16413,9 @@ }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, diff --git a/package.json b/package.json index 35d6c7167..8a374b1d2 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,13 @@ }, "dependencies": { "@jest/globals": "^28.1.0", + "@playwright/test": "^1.38.1", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^11.2.7", "@types/node": "^17.0.31", "@types/react": "^18.0.9", "@types/react-dom": "^18.0.3", "@types/uuid": "^8.3.4", - "@playwright/test": "^1.38.1", "antd": "^5.4.3", "crypto-js": "^4.2.0", "dexie": "^3.2.3", @@ -85,7 +85,7 @@ "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "cypress": "^10.11.0", - "eslint": "^8.13.0", + "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^2.7.1", diff --git a/playwright/utils/collaboration-feature-utils.ts b/playwright/utils/collaboration-feature-utils.ts index 2ea6e30b7..fb4440fc6 100644 --- a/playwright/utils/collaboration-feature-utils.ts +++ b/playwright/utils/collaboration-feature-utils.ts @@ -14,7 +14,7 @@ export async function goalActionFlow(page: Page, action: string) { .locator("div") .filter({ hasText: new RegExp(`^${action}$`) }) .first() - .click(); + .click({ force: true }); } export async function goToShareGoalModalFlow(page: Page) { diff --git a/src/Interfaces/IPopupModals.ts b/src/Interfaces/IPopupModals.ts index d4d620e91..31223eb39 100644 --- a/src/Interfaces/IPopupModals.ts +++ b/src/Interfaces/IPopupModals.ts @@ -5,22 +5,24 @@ export interface confirmActionState { delete: boolean; shareAnonymously: boolean; shareWithOne: boolean; + restore: boolean; }; collaboration: { colabRequest: boolean; delete: boolean; archive: boolean; + restore: boolean; }; } export interface confirmGoalAction { actionCategory: "goal"; - actionName: "archive" | "delete" | "shareAnonymously" | "shareWithOne"; + actionName: "archive" | "delete" | "shareAnonymously" | "shareWithOne" | "restore"; } export interface confirmColabGoalAction { actionCategory: "collaboration"; - actionName: "colabRequest" | "delete" | "archive"; + actionName: "colabRequest" | "delete" | "archive" | "restore"; } export interface ConfirmationModalProps { diff --git a/src/Interfaces/index.ts b/src/Interfaces/index.ts index 08b03190c..6ab837dc4 100644 --- a/src/Interfaces/index.ts +++ b/src/Interfaces/index.ts @@ -1,4 +1,4 @@ -import { ISubGoalHistory } from "@src/store/GoalsState"; +import { ISubGoalHistory, TDisplayGoalActions } from "@src/store/GoalsState"; import { GoalItem, IParticipant } from "@src/models/GoalItem"; import ContactItem from "@src/models/ContactItem"; import { ILanguage, ILanguageListProps } from "./ILanguage"; @@ -20,7 +20,7 @@ export interface ILocationState { displayInputNoteModal?: number; // show input modal for adding note for id changeTheme?: boolean; // theme changer mode expandedGoalId?: string; // id of goal to be expanded - displayGoalActions?: GoalItem; // id of goal whose actions have to be opened + displayGoalActions?: TDisplayGoalActions; // id of goal whose actions have to be opened displayPartnerMode?: boolean; // whether or not to display the partner goals displayAddContact?: boolean; // whether or not to display the add contact form displayParticipants?: string; // id of goal whose participants have to be displayed diff --git a/src/api/GoalsAPI/index.ts b/src/api/GoalsAPI/index.ts old mode 100644 new mode 100755 index c9c26ad10..e23d557c4 --- a/src/api/GoalsAPI/index.ts +++ b/src/api/GoalsAPI/index.ts @@ -3,6 +3,7 @@ import { db } from "@models"; import { GoalItem, IParticipant } from "@src/models/GoalItem"; import { shareGoal } from "@src/services/goal.service"; import { sortGoalsByProps } from "../GCustomAPI"; +import { addDeletedGoal } from "../TrashAPI"; export const addIntoSublist = async (parentGoalId: string, goalIds: string[]) => { db.transaction("rw", db.goalsCollection, async () => { @@ -132,8 +133,11 @@ export const unarchiveUserGoal = async (goal: GoalItem) => { await unarchiveGoal(goal); }; -export const removeGoal = async (goalId: string) => { - await db.goalsCollection.delete(goalId).catch((err) => console.log("failed to delete", err)); +export const removeGoal = async (goal: GoalItem) => { + await Promise.allSettled([ + db.goalsCollection.delete(goal.id).catch((err) => console.log("failed to delete", err)), + addDeletedGoal(goal), + ]); }; export const removeChildrenGoals = async (parentGoalId: string) => { @@ -143,7 +147,7 @@ export const removeChildrenGoals = async (parentGoalId: string) => { } childrenGoals.forEach((goal) => { removeChildrenGoals(goal.id); - removeGoal(goal.id); + removeGoal(goal); }); }; @@ -259,7 +263,7 @@ export const notifyNewColabRequest = async (id: string, relId: string) => { export const removeGoalWithChildrens = async (goal: GoalItem) => { await removeChildrenGoals(goal.id); - await removeGoal(goal.id); + await removeGoal(goal); if (goal.parentGoalId !== "root") { getGoal(goal.parentGoalId).then(async (parentGoal: GoalItem) => { const parentGoalSublist = parentGoal.sublist; diff --git a/src/api/InboxAPI/index.ts b/src/api/InboxAPI/index.ts index d27f9a677..5f8af9be6 100644 --- a/src/api/InboxAPI/index.ts +++ b/src/api/InboxAPI/index.ts @@ -30,7 +30,7 @@ export const addGoalChangesInID = async (id: string, relId: string, newChanges: .where("id") .equals(id) .modify((obj: InboxItem) => { - const currentState = obj.changes[relId] || getDefaultValueOfGoalChanges(); + const currentState = { ...getDefaultValueOfGoalChanges(), ...obj.changes[relId] }; Object.keys(currentState).forEach((changeType: typeOfChange) => { currentState[changeType] = [...currentState[changeType], ...newChanges[changeType]]; }); diff --git a/src/api/SharedWMAPI/index.ts b/src/api/SharedWMAPI/index.ts index d968939f0..7c86c7ddf 100644 --- a/src/api/SharedWMAPI/index.ts +++ b/src/api/SharedWMAPI/index.ts @@ -3,6 +3,7 @@ import { db } from "@models"; import { GoalItem } from "@src/models/GoalItem"; import { createGoalObjectFromTags } from "@src/helpers/GoalProcessor"; import { addGoal } from "../GoalsAPI"; +import { addDeletedGoal } from "../TrashAPI"; export const addSharedWMSublist = async (parentGoalId: string, goalIds: string[]) => { db.transaction("rw", db.sharedWMCollection, async () => { @@ -74,7 +75,7 @@ export const getRootGoalsOfPartner = async (relId: string) => { await db.sharedWMCollection .where("parentGoalId") .equals("root") - .and((x) => x.participants[0].relId === relId) + .and((x) => x.participants.length > 0 && x.participants[0].relId === relId) .sortBy("createdAt") ).reverse(); }; @@ -116,8 +117,11 @@ export const archiveSharedWMGoal = async (goal: GoalItem) => { await archiveGoal(goal); }; -export const removeSharedWMGoal = async (goalId: string) => { - await db.sharedWMCollection.delete(goalId).catch((err) => console.log("failed to delete", err)); +export const removeSharedWMGoal = async (goal: GoalItem) => { + await Promise.allSettled([ + addDeletedGoal(goal), + db.sharedWMCollection.delete(goal.id).catch((err) => console.log("failed to delete", err)), + ]); }; export const removeSharedWMChildrenGoals = async (parentGoalId: string) => { @@ -127,7 +131,7 @@ export const removeSharedWMChildrenGoals = async (parentGoalId: string) => { } childrenGoals.forEach((goal) => { removeSharedWMChildrenGoals(goal.id); - removeSharedWMGoal(goal.id); + removeSharedWMGoal(goal); }); }; @@ -138,7 +142,7 @@ export const transferToMyGoals = async (id: string) => { } childrenGoals.forEach((goal) => { transferToMyGoals(goal.id); - addGoal(goal).then(async () => removeSharedWMGoal(goal.id)); + addGoal(goal).then(async () => removeSharedWMGoal(goal)); }); }; @@ -147,7 +151,7 @@ export const convertSharedWMGoalToColab = async (goal: GoalItem) => { .then(async () => { addGoal({ ...goal, typeOfGoal: "shared" }) .then(async () => { - removeSharedWMGoal(goal.id); + removeSharedWMGoal(goal); }) .catch((err) => console.log(err)); }) diff --git a/src/api/TrashAPI/index.ts b/src/api/TrashAPI/index.ts new file mode 100755 index 000000000..0ec5cbd50 --- /dev/null +++ b/src/api/TrashAPI/index.ts @@ -0,0 +1,98 @@ +/* eslint-disable no-param-reassign */ +import { db } from "@models"; +import { GoalItem } from "@src/models/GoalItem"; +import { TrashItem } from "@src/models/TrashItem"; +import { addGoal } from "../GoalsAPI"; +import { addSharedWMGoal } from "../SharedWMAPI"; + +export const getDeletedGoals = async (parentGoalId: string) => { + const childrenGoals: TrashItem[] = await db.goalTrashCollection + .where("parentGoalId") + .equals(parentGoalId) + .sortBy("deletedAt"); + childrenGoals.reverse(); + return childrenGoals; +}; + +export const addDeletedGoal = async (goal: GoalItem) => { + await db + .transaction("rw", db.goalTrashCollection, async () => { + await db.goalTrashCollection.add({ ...goal, deletedAt: new Date().toISOString() }); + }) + .catch((e) => { + console.log(e.stack || e); + }); +}; + +export const getDeletedGoal = async (goalId: string) => { + const delGoal: TrashItem[] = await db.goalTrashCollection.where("id").equals(goalId).toArray(); + return delGoal.length > 0 ? delGoal[0] : null; +}; + +export const restoreGoal = async (goal: GoalItem, isShareWMType = false) => { + db.goalTrashCollection.delete(goal.id).catch((err) => console.log("failed to delete", err)); + if (isShareWMType) { + await addSharedWMGoal(goal); + } else { + await addGoal(goal); + } +}; + +export const restoreChildrenGoals = async (id: string, isShareWMType = false) => { + const childrenGoals: TrashItem[] = await getDeletedGoals(id); + if (childrenGoals) { + childrenGoals.forEach(async ({ deletedAt, ...goal }) => { + await restoreChildrenGoals(goal.id, isShareWMType); + await restoreGoal(goal, isShareWMType); + }); + } +}; + +export const restoreUserGoal = async (goal: GoalItem, isShareWMType = false) => { + await restoreChildrenGoals(goal.id, isShareWMType); + await restoreGoal(goal, isShareWMType); +}; + +export const removeDeletedGoal = async (goal: GoalItem) => { + await Promise.allSettled([ + db.goalsCollection.delete(goal.id).catch((err) => console.log("failed to delete", err)), + addDeletedGoal(goal), + ]); +}; + +export const removeDeletedChildrenGoals = async (parentGoalId: string) => { + const childrenGoals = await getDeletedGoals(parentGoalId); + if (childrenGoals.length === 0) { + return; + } + childrenGoals.forEach((goal) => { + removeDeletedChildrenGoals(goal.id); + removeDeletedGoal(goal); + }); +}; + +export const removeDeletedGoalWithChildrens = async (goal: GoalItem) => { + await removeDeletedChildrenGoals(goal.id); + await removeDeletedGoal(goal); + if (goal.parentGoalId !== "root") { + getDeletedGoal(goal.parentGoalId).then(async (deletedGoal) => { + if (!deletedGoal) { + return; + } + const parentGoalSublist = deletedGoal.sublist; + const childGoalIndex = parentGoalSublist.indexOf(goal.id); + if (childGoalIndex !== -1) { + parentGoalSublist.splice(childGoalIndex, 1); + } + await db.goalTrashCollection.update(deletedGoal.id, { sublist: parentGoalSublist }).then((updated) => updated); + }); + } +}; + +export const getParticipantsOfDeletedGoal = async (id: string) => { + const goals = await db.goalTrashCollection + .where("id") + .anyOf(...[id]) + .toArray(); + return goals.flatMap((goal) => goal.participants.map((participant) => ({ sub: participant, rootGoalId: goal.id }))); +}; diff --git a/src/components/GoalsComponents/ArchivedAccordion.tsx b/src/components/GoalsComponents/ArchivedAccordion.tsx deleted file mode 100644 index 4a147caf0..000000000 --- a/src/components/GoalsComponents/ArchivedAccordion.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; -import { useRecoilValue } from "recoil"; -import { darkModeState } from "@src/store"; - -import { GoalItem } from "@src/models/GoalItem"; -import ZAccordion from "@src/common/Accordion"; - -import MyGoal from "./MyGoal/MyGoal"; - -interface IArchivedAccordionProps { - archivedGoals: GoalItem[]; - showActions: { - open: string; - click: number; - }; - setShowActions: React.Dispatch< - React.SetStateAction<{ - open: string; - click: number; - }> - >; -} -const ArchivedAccordion: React.FC = ({ archivedGoals, showActions, setShowActions }) => { - const darkModeStatus = useRecoilValue(darkModeState); - return ( -
- {archivedGoals.length > 0 && ( - ( - - )), - }, - ]} - /> - )} -
- ); -}; - -export default ArchivedAccordion; diff --git a/src/components/GoalsComponents/DisplayChangesModal/AcceptBtn.tsx b/src/components/GoalsComponents/DisplayChangesModal/AcceptBtn.tsx index 3992d4896..0b2f87fa5 100644 --- a/src/components/GoalsComponents/DisplayChangesModal/AcceptBtn.tsx +++ b/src/components/GoalsComponents/DisplayChangesModal/AcceptBtn.tsx @@ -31,6 +31,7 @@ const AcceptBtn = ({ typeAtPriority, acceptChanges }: AcceptBtnProps) => { />   {typeAtPriority === "archived" && "Complete for me too"} + {typeAtPriority === "restored" && "Restore for me too"} {typeAtPriority === "deleted" && "Delete for me too"} {typeAtPriority === "subgoals" && "Add all checked"} {typeAtPriority === "modifiedGoals" && "Make all checked changes"} diff --git a/src/components/GoalsComponents/DisplayChangesModal/DisplayChangesModal.tsx b/src/components/GoalsComponents/DisplayChangesModal/DisplayChangesModal.tsx index b2e9a2802..738c35596 100644 --- a/src/components/GoalsComponents/DisplayChangesModal/DisplayChangesModal.tsx +++ b/src/components/GoalsComponents/DisplayChangesModal/DisplayChangesModal.tsx @@ -15,6 +15,7 @@ import { archiveUserGoal, getGoal, removeGoalWithChildrens, updateGoal } from "@ import { deleteGoalChangesInID, getInboxItem, removeGoalInbox, removePPTFromInboxOfGoal } from "@src/api/InboxAPI"; import { findGoalTagChanges, jumpToLowestChanges } from "@src/helpers/GoalProcessor"; import { acceptSelectedSubgoals, acceptSelectedTags } from "@src/helpers/InboxProcessor"; +import { getDeletedGoal, restoreUserGoal } from "@src/api/TrashAPI"; import SubHeader from "@src/common/SubHeader"; import ContactItem from "@src/models/ContactItem"; import ZModal from "@src/common/ZModal"; @@ -163,6 +164,8 @@ const DisplayChangesModal = () => { await removeGoalWithChildrens(activeGoal); } else if (currentDisplay === "archived") { await archiveUserGoal(activeGoal); + } else if (currentDisplay === "restored") { + await restoreUserGoal(activeGoal); } setCurrentDisplay("none"); }; @@ -185,21 +188,29 @@ const DisplayChangesModal = () => { const currPPT = participants[activePPT].relId; setActivePPT(0); setParticipants([...participants.filter((ele) => ele.relId !== currPPT)]); - } - const changedGoal = await getGoal(parentId); - if (changedGoal) { - setActiveGoal({ ...changedGoal }); - if (typeAtPriority === "subgoals") { - setNewGoals(goals || []); - } else if (typeAtPriority === "modifiedGoals") { - setUpdatesIntent(goals[0].intent); - const incGoal: GoalItem = { ...goals[0].goal }; - setUpdateList({ ...findGoalTagChanges(changedGoal, incGoal) }); + } else if (typeAtPriority === "restored") { + const goalToBeRestored = await getDeletedGoal(parentId); + console.log("🚀 ~ getChanges ~ goalToBeRestored:", goalToBeRestored); + if (goalToBeRestored) { + delete goalToBeRestored.deletedAt; + setActiveGoal(goalToBeRestored); } - if (currentDisplay !== typeAtPriority) { - setCurrentDisplay(typeAtPriority); + } else { + const changedGoal = await getGoal(parentId); + if (changedGoal) { + setActiveGoal({ ...changedGoal }); + if (typeAtPriority === "subgoals") { + setNewGoals(goals || []); + } else if (typeAtPriority === "modifiedGoals") { + setUpdatesIntent(goals[0].intent); + const incGoal: GoalItem = { ...goals[0].goal }; + setUpdateList({ ...findGoalTagChanges(changedGoal, incGoal) }); + } } } + if (currentDisplay !== typeAtPriority) { + setCurrentDisplay(typeAtPriority); + } } }; @@ -273,9 +284,10 @@ const DisplayChangesModal = () => { />

)} - {(currentDisplay === "archived" || currentDisplay === "deleted") &&
} + {["deleted", "archived", "restored"].includes(currentDisplay) &&
} {currentDisplay === "modifiedGoals" && getEditChangesList()} {currentDisplay === "subgoals" && getSubgoalsList()} +
{activeGoal && ( <> diff --git a/src/components/GoalsComponents/DisplayChangesModal/Header.tsx b/src/components/GoalsComponents/DisplayChangesModal/Header.tsx index e14fc414f..16f5cc644 100644 --- a/src/components/GoalsComponents/DisplayChangesModal/Header.tsx +++ b/src/components/GoalsComponents/DisplayChangesModal/Header.tsx @@ -36,6 +36,12 @@ const Header = ({ {contactName} completed {title}. ); + case "restored": + return ( + <> + {contactName} restored {title}. + + ); default: return <> ; } diff --git a/src/components/GoalsComponents/GoalSublist/GoalSublist.tsx b/src/components/GoalsComponents/GoalSublist/GoalSublist.tsx old mode 100644 new mode 100755 index f617ce98c..497bc9eb0 --- a/src/components/GoalsComponents/GoalSublist/GoalSublist.tsx +++ b/src/components/GoalsComponents/GoalSublist/GoalSublist.tsx @@ -2,11 +2,6 @@ import React, { useEffect, useState } from "react"; import { useRecoilValue } from "recoil"; import { useTranslation } from "react-i18next"; -import { GoalItem } from "@src/models/GoalItem"; -import { getChildrenGoals, getGoal } from "@src/api/GoalsAPI"; -import { createGoalObjectFromTags } from "@src/helpers/GoalProcessor"; -import { getSharedWMChildrenGoals, getSharedWMGoal } from "@src/api/SharedWMAPI"; -import { displayPartnerMode, lastAction } from "@src/store"; import { displayAddGoal, displayChangesModal, @@ -14,13 +9,19 @@ import { displaySuggestionsModal, displayUpdateGoal, } from "@src/store/GoalsState"; +import { GoalItem } from "@src/models/GoalItem"; +import { getDeletedGoals } from "@src/api/TrashAPI"; +import { createGoalObjectFromTags } from "@src/helpers/GoalProcessor"; +import { getChildrenGoals, getGoal } from "@src/api/GoalsAPI"; +import { displayPartnerMode, lastAction } from "@src/store"; +import { getSharedWMChildrenGoals, getSharedWMGoal } from "@src/api/SharedWMAPI"; import GoalsList from "../GoalsList"; import ConfigGoal from "../GoalConfigModal/ConfigGoal"; +import GoalHistory from "./GoalHistory"; +import GoalsAccordion from "../GoalsAccordion"; import "./GoalSublist.scss"; -import GoalHistory from "./GoalHistory"; -import ArchivedAccordion from "../ArchivedAccordion"; export const GoalSublist = () => { const { t } = useTranslation(); @@ -33,6 +34,7 @@ export const GoalSublist = () => { const showPartnerMode = useRecoilValue(displayPartnerMode); const [parentGoal, setParentGoal] = useState(null); const [childrenGoals, setChildrenGoals] = useState([]); + const [deletedGoals, setDeletedGoals] = useState([]); const [archivedChildren, setArchivedChildren] = useState([]); const [showActions, setShowActions] = useState({ open: "root", click: 1 }); @@ -42,12 +44,20 @@ export const GoalSublist = () => { }; useEffect(() => { - (showPartnerMode ? getSharedWMGoal(goalID) : getGoal(goalID)).then((parent) => setParentGoal(parent)); + (showPartnerMode ? getSharedWMGoal(goalID) : getGoal(goalID)).then((parent) => { + setParentGoal(parent); + getDeletedGoals(goalID).then((res) => { + setDeletedGoals([...res.map(({ deletedAt, ...goal }) => goal)]); + }); + }); }, [goalID]); useEffect(() => { (showPartnerMode ? getSharedWMChildrenGoals(goalID) : getChildrenGoals(goalID)).then((fetchedGoals) => { handleChildrenGoals(fetchedGoals); + getDeletedGoals(goalID).then((res) => { + setDeletedGoals([...res.map(({ deletedAt, ...goal }) => goal)]); + }); }); }, [action, parentGoal, showAddGoal, showSuggestionModal, showChangesModal, showUpdateGoal]); @@ -65,8 +75,15 @@ export const GoalSublist = () => { setGoals={setChildrenGoals} setShowActions={setShowActions} /> - + diff --git a/src/components/GoalsComponents/GoalsAccordion.tsx b/src/components/GoalsComponents/GoalsAccordion.tsx new file mode 100755 index 000000000..e74a8ef93 --- /dev/null +++ b/src/components/GoalsComponents/GoalsAccordion.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import { useRecoilValue } from "recoil"; +import { darkModeState } from "@src/store"; + +import { GoalItem } from "@src/models/GoalItem"; +import ZAccordion from "@src/common/Accordion"; + +import MyGoal from "./MyGoal/MyGoal"; +import { TAction, displayGoalActions } from "@src/store/GoalsState"; +import AccordionActions from "./MyGoalActions/AccordionActions"; + +interface IGoalsAccordionProps { + header: "Done" | "Trash"; + goals: GoalItem[]; + showActions: { + open: string; + click: number; + }; + setShowActions: React.Dispatch< + React.SetStateAction<{ + open: string; + click: number; + }> + >; +} + +const actionsMap: { + [key: string]: TAction; +} = { + Done: "archived", + Trash: "deleted", +}; + +const GoalsAccordion: React.FC = ({ header, goals, showActions, setShowActions }) => { + const darkModeStatus = useRecoilValue(darkModeState); + const actionType = actionsMap[header] || "regular"; + const showGoalActions = useRecoilValue(displayGoalActions); + + return ( +
+ {showGoalActions && ["archived", "deleted"].includes(showGoalActions.actionType) && ( + + )} + {goals.length > 0 && ( + ( + + )), + }, + ]} + /> + )} +
+ ); +}; + +export default GoalsAccordion; diff --git a/src/components/GoalsComponents/GoalsList.tsx b/src/components/GoalsComponents/GoalsList.tsx index 58fe77fec..7588480d9 100644 --- a/src/components/GoalsComponents/GoalsList.tsx +++ b/src/components/GoalsComponents/GoalsList.tsx @@ -2,10 +2,12 @@ import { updatePositionIndex } from "@src/api/GCustomAPI"; import DragAndDrop from "@src/layouts/DragAndDrop"; import { GoalItem } from "@src/models/GoalItem"; import React, { useState } from "react"; -import { displayUpdateGoal } from "@src/store/GoalsState"; +import { displayGoalActions, displayUpdateGoal } from "@src/store/GoalsState"; import { useRecoilValue } from "recoil"; import ConfigGoal from "./GoalConfigModal/ConfigGoal"; import MyGoal from "./MyGoal/MyGoal"; +import MyGoalActions from "./MyGoalActions/MyGoalActions"; +import RegularGoalActions from "./MyGoalActions/RegularGoalActions"; interface GoalsListProps { goals: GoalItem[]; @@ -22,12 +24,13 @@ interface GoalsListProps { >; } -const GoalsList: React.FC = ({ goals, showActions, setGoals, setShowActions }) => { +const GoalsList = ({ goals, showActions, setGoals, setShowActions }: GoalsListProps) => { const showUpdateGoal = useRecoilValue(displayUpdateGoal); + const showGoalActions = useRecoilValue(displayGoalActions); const [dragging, setDragging] = useState(false); const [draggedItem, setDraggedItem] = useState(null); - const handleDragStart = (e, index: number) => { + const handleDragStart = (e: any, index: number) => { setDragging(true); setDraggedItem(goals[index]); e.dataTransfer.effectAllowed = "move"; @@ -48,22 +51,29 @@ const GoalsList: React.FC = ({ goals, showActions, setGoals, set const posIndexPromises = goals.map(async (ele, index) => updatePositionIndex(ele.id, index)); Promise.all(posIndexPromises).catch((err) => console.log("error in sorting", err)); }; - return goals.map((goal: GoalItem, index: number) => ( - // eslint-disable-next-line react/no-array-index-key - - {showUpdateGoal?.goalId === goal.id && } - - - - - )); + return ( + <> + {showGoalActions && showGoalActions.actionType === "regular" && ( + + )} + {goals.map((goal: GoalItem, index: number) => ( + // eslint-disable-next-line react/no-array-index-key + + {showUpdateGoal?.goalId === goal.id && } + + + + + ))} + + ); }; export default GoalsList; diff --git a/src/components/GoalsComponents/MyGoal/MyGoal.tsx b/src/components/GoalsComponents/MyGoal/MyGoal.tsx index 84aa73204..c6b439ec4 100644 --- a/src/components/GoalsComponents/MyGoal/MyGoal.tsx +++ b/src/components/GoalsComponents/MyGoal/MyGoal.tsx @@ -1,18 +1,23 @@ import React, { useEffect, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import { useRecoilValue } from "recoil"; +import { useRecoilValue, useSetRecoilState } from "recoil"; + +import { unarchiveIcon } from "@src/assets"; import { GoalItem } from "@src/models/GoalItem"; +import { unarchiveUserGoal } from "@src/api/GoalsAPI"; -import { darkModeState, displayPartnerMode, lastAction } from "@src/store"; -import { displayGoalId, displayUpdateGoal, goalsHistory, displayChangesModal } from "@src/store/GoalsState"; +import { darkModeState, displayPartnerMode } from "@src/store"; +import { displayGoalId, displayUpdateGoal, goalsHistory, displayChangesModal, TAction } from "@src/store/GoalsState"; import GoalAvatar from "../GoalAvatar"; import GoalSummary from "./GoalSummary/GoalSummary"; import GoalDropdown from "./GoalDropdown"; import GoalTitle from "./GoalTitle"; +import { ILocationState } from "@src/Interfaces"; interface MyGoalProps { + actionType: TAction; goal: GoalItem; showActions: { open: string; @@ -22,12 +27,11 @@ interface MyGoalProps { React.SetStateAction<{ open: string; click: number; - // eslint-disable-next-line prettier/prettier }> >; } -const MyGoal: React.FC = ({ goal, showActions, setShowActions }) => { +const MyGoal: React.FC = ({ goal, actionType, showActions, setShowActions }) => { const archived = goal.archived === "true"; const defaultTap = { open: "root", click: 1 }; const isActionVisible = !archived && showActions.open === goal.id && showActions.click > 0; @@ -73,12 +77,11 @@ const MyGoal: React.FC = ({ goal, showActions, setShowActions }) => }; async function handleDropDown(e: React.MouseEvent) { e.stopPropagation(); - const navState = { ...location.state, from: "" }; + const navState: ILocationState = { ...location.state, from: "" }; if (goal.newUpdates) { navState.displayChanges = goal; } else { - navState.displayGoalActions = goal; - console.log("in navstate, displayGoalActions: ", navState); + navState.displayGoalActions = { actionType, goal }; } navigate("/MyGoals", { state: navState }); } diff --git a/src/components/GoalsComponents/MyGoalActions/AccordionActions.tsx b/src/components/GoalsComponents/MyGoalActions/AccordionActions.tsx new file mode 100644 index 000000000..45616b810 --- /dev/null +++ b/src/components/GoalsComponents/MyGoalActions/AccordionActions.tsx @@ -0,0 +1,122 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useRecoilValue, useSetRecoilState } from "recoil"; + +import ZModal from "@src/common/ZModal"; +import useGoalStore from "@src/hooks/useGoalStore"; +import ConfirmationModal from "@src/common/ConfirmationModal"; + +import { GoalItem } from "@src/models/GoalItem"; +import { restoreGoal } from "@src/helpers/GoalController"; +import { confirmAction } from "@src/Interfaces/IPopupModals"; +import { unarchiveIcon } from "@src/assets"; +import { unarchiveUserGoal } from "@src/api/GoalsAPI"; +import { TAction, goalsHistory } from "@src/store/GoalsState"; +import { archiveThisGoal, removeThisGoal } from "@src/helpers/GoalActionHelper"; +import { lastAction, openDevMode, displayConfirmation, displayPartnerMode, darkModeState } from "@src/store"; + +import ActionDiv from "./ActionDiv"; +import "./MyGoalActions.scss"; + +const AccordionActions = ({ actionType, goal, open }: { actionType: TAction; open: boolean; goal: GoalItem }) => { + const { t } = useTranslation(); + const { handleUpdateGoal, handleConfirmation } = useGoalStore(); + const confirmActionCategory = goal.typeOfGoal === "shared" && goal.parentGoalId === "root" ? "collaboration" : "goal"; + + const subGoalsHistory = useRecoilValue(goalsHistory); + const showConfirmation = useRecoilValue(displayConfirmation); + const isPartnerGoal = useRecoilValue(displayPartnerMode); + const setDevMode = useSetRecoilState(openDevMode); + const darkModeStatus = useRecoilValue(darkModeState); + const setLastAction = useSetRecoilState(lastAction); + const ancestors = subGoalsHistory.map((ele) => ele.goalID); + + const [confirmationAction, setConfirmationAction] = useState(null); + + const handleActionClick = async (action: string) => { + if (action === "delete") { + if (goal.title === "magic") { + setDevMode(false); + } + await removeThisGoal(goal, ancestors, isPartnerGoal); + setLastAction("goalDeleted"); + } else if (action === "archive") { + await archiveThisGoal(goal, ancestors); + setLastAction("goalArchived"); + } else if (action === "restore") { + if (actionType === "archived") { + await unarchiveUserGoal(goal); + } else if (actionType === "deleted") { + await restoreGoal(goal, ancestors); + } + setLastAction("goalUnarchived"); + } else { + return; + } + window.history.go(confirmationAction ? -2 : -1); + }; + + const openConfirmationPopUp = async (action: confirmAction) => { + const { actionCategory, actionName } = action; + if (actionCategory === "collaboration" && showConfirmation.collaboration[actionName]) { + handleConfirmation(); + setConfirmationAction({ ...action }); + } else if (actionCategory === "goal" && showConfirmation.goal[action.actionName]) { + handleConfirmation(); + setConfirmationAction({ ...action }); + } else { + await handleActionClick(actionName); + } + }; + + return ( + window.history.back()} type="interactables-modal"> +
{ + handleUpdateGoal(goal.id, !!goal.timeBudget?.perDay); + }} + > +

+ {t(`${goal.title}`)} +

+
+
+ {confirmationAction && } + +
{ + e.stopPropagation(); + await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "restore" }); + }} + > + + } + /> +
+ {!isPartnerGoal && ( +
{ + e.stopPropagation(); + await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "delete" }); + }} + > + +
+ )} +
+
+ ); +}; + +export default AccordionActions; diff --git a/src/components/GoalsComponents/MyGoalActions/MyGoalActions.tsx b/src/components/GoalsComponents/MyGoalActions/MyGoalActions.tsx deleted file mode 100644 index 30b9b90ab..000000000 --- a/src/components/GoalsComponents/MyGoalActions/MyGoalActions.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useRecoilValue, useSetRecoilState } from "recoil"; -import { useLocation, useNavigate } from "react-router-dom"; - -import useGoalStore from "@src/hooks/useGoalStore"; -import ConfirmationModal from "@src/common/ConfirmationModal"; -import { unarchiveUserGoal } from "@src/api/GoalsAPI"; -import { unarchiveIcon } from "@src/assets"; -import ZModal from "@src/common/ZModal"; - -import { - displayToast, - lastAction, - openDevMode, - displayConfirmation, - displayPartnerMode, - darkModeState, -} from "@src/store"; -import { GoalItem } from "@src/models/GoalItem"; -import { goalsHistory } from "@src/store/GoalsState"; -import { confirmAction } from "@src/Interfaces/IPopupModals"; -import { convertSharedWMGoalToColab } from "@src/api/SharedWMAPI"; -import { archiveThisGoal, removeThisGoal } from "@src/helpers/GoalActionHelper"; - -import ActionDiv from "./ActionDiv"; -import "./MyGoalActions.scss"; - -const MyGoalActions = ({ goal, open }: { open: boolean; goal: GoalItem }) => { - const { t } = useTranslation(); - const { handleUpdateGoal, handleShareGoal, handleConfirmation } = useGoalStore(); - const confirmActionCategory = goal.typeOfGoal === "shared" && goal.parentGoalId === "root" ? "collaboration" : "goal"; - - const subGoalsHistory = useRecoilValue(goalsHistory); - const showConfirmation = useRecoilValue(displayConfirmation); - const isPartnerGoal = useRecoilValue(displayPartnerMode); - const setDevMode = useSetRecoilState(openDevMode); - const setShowToast = useSetRecoilState(displayToast); - const darkModeStatus = useRecoilValue(darkModeState); - const setLastAction = useSetRecoilState(lastAction); - const ancestors = subGoalsHistory.map((ele) => ele.goalID); - - const [confirmationAction, setConfirmationAction] = useState(null); - - const handleActionClick = async (action: string) => { - if (action === "delete") { - if (goal.title === "magic") { - setDevMode(false); - } - await removeThisGoal(goal, ancestors, isPartnerGoal); - setLastAction("goalDeleted"); - } else if (action === "archive") { - await archiveThisGoal(goal, ancestors); - setLastAction("goalArchived"); - } else if (action === "restore") { - await unarchiveUserGoal(goal); - setLastAction("goalUnarchived"); - } else if (action === "colabRequest") { - await convertSharedWMGoalToColab(goal); - window.history.back(); - } else { - return; - } - window.history.go(confirmationAction ? -2 : -1); - }; - - const openConfirmationPopUp = async (action: confirmAction) => { - const { actionCategory, actionName } = action; - if (actionCategory === "collaboration" && showConfirmation.collaboration[actionName]) { - handleConfirmation(); - setConfirmationAction({ ...action }); - } else if (actionCategory === "goal" && showConfirmation.goal[action.actionName]) { - handleConfirmation(); - setConfirmationAction({ ...action }); - } else { - await handleActionClick(actionName); - } - }; - - const isGoalArchived = goal.archived; - - return ( - window.history.back()} type="interactables-modal"> -
{ - handleUpdateGoal(goal.id, !!goal.timeBudget?.perDay); - }} - > -

- {t(`${goal.title}`)} -

-
-
- {confirmationAction && } - {isGoalArchived === "true" ? ( - <> -
{ - e.stopPropagation(); - await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "restore" }); - }} - > - - } - /> -
- {!isPartnerGoal && ( -
{ - e.stopPropagation(); - await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "delete" }); - }} - > - -
- )} - - ) : ( - <> -
{ - e.stopPropagation(); - await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "delete" }); - }} - > - -
- {!isPartnerGoal && ( -
{ - e.stopPropagation(); - await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "archive" }); - }} - > - -
- )} - {((isPartnerGoal && goal.parentGoalId === "root") || !isPartnerGoal) && ( -
{ - e.stopPropagation(); - if (!isPartnerGoal) { - handleShareGoal(goal); - } else { - await openConfirmationPopUp({ actionCategory: "collaboration", actionName: "colabRequest" }); - } - }} - > - -
- )} -
{ - handleUpdateGoal(goal.id, !!goal.timeBudget?.perDay); - }} - > - -
- - )} -
-
- ); -}; - -export default MyGoalActions; diff --git a/src/components/GoalsComponents/MyGoalActions/RegularGoalActions.tsx b/src/components/GoalsComponents/MyGoalActions/RegularGoalActions.tsx new file mode 100755 index 000000000..a195022b1 --- /dev/null +++ b/src/components/GoalsComponents/MyGoalActions/RegularGoalActions.tsx @@ -0,0 +1,131 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useRecoilValue, useSetRecoilState } from "recoil"; + +import useGoalStore from "@src/hooks/useGoalStore"; +import ConfirmationModal from "@src/common/ConfirmationModal"; +import ZModal from "@src/common/ZModal"; + +import { lastAction, openDevMode, displayConfirmation, displayPartnerMode } from "@src/store"; +import { GoalItem } from "@src/models/GoalItem"; +import { goalsHistory } from "@src/store/GoalsState"; +import { confirmAction } from "@src/Interfaces/IPopupModals"; +import { convertSharedWMGoalToColab } from "@src/api/SharedWMAPI"; +import { archiveThisGoal, removeThisGoal } from "@src/helpers/GoalActionHelper"; + +import ActionDiv from "./ActionDiv"; +import "./MyGoalActions.scss"; + +const RegularGoalActions = ({ goal, open }: { open: boolean; goal: GoalItem }) => { + const { t } = useTranslation(); + const { handleUpdateGoal, handleShareGoal, handleConfirmation } = useGoalStore(); + const confirmActionCategory = goal.typeOfGoal === "shared" && goal.parentGoalId === "root" ? "collaboration" : "goal"; + + const subGoalsHistory = useRecoilValue(goalsHistory); + const showConfirmation = useRecoilValue(displayConfirmation); + const isPartnerGoal = useRecoilValue(displayPartnerMode); + const setDevMode = useSetRecoilState(openDevMode); + const setLastAction = useSetRecoilState(lastAction); + const ancestors = subGoalsHistory.map((ele) => ele.goalID); + + const [confirmationAction, setConfirmationAction] = useState(null); + + const handleActionClick = async (action: string) => { + if (action === "delete") { + if (goal.title === "magic") { + setDevMode(false); + } + await removeThisGoal(goal, ancestors, isPartnerGoal); + setLastAction("goalDeleted"); + } else if (action === "archive") { + await archiveThisGoal(goal, ancestors); + setLastAction("goalArchived"); + } else if (action === "colabRequest") { + await convertSharedWMGoalToColab(goal); + window.history.back(); + } else { + return; + } + window.history.go(confirmationAction ? -2 : -1); + }; + + const openConfirmationPopUp = async (action: confirmAction) => { + const { actionCategory, actionName } = action; + if (actionCategory === "collaboration" && showConfirmation.collaboration[actionName]) { + handleConfirmation(); + setConfirmationAction({ ...action }); + } else if (actionCategory === "goal" && showConfirmation.goal[action.actionName]) { + handleConfirmation(); + setConfirmationAction({ ...action }); + } else { + await handleActionClick(actionName); + } + }; + + return ( + window.history.back()} type="interactables-modal"> +
{ + handleUpdateGoal(goal.id, !!goal.timeBudget?.perDay); + }} + > +

+ {t(`${goal.title}`)} +

+
+
+ {confirmationAction && } +
{ + e.stopPropagation(); + await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "delete" }); + }} + > + +
+ {!isPartnerGoal && ( +
{ + e.stopPropagation(); + await openConfirmationPopUp({ actionCategory: confirmActionCategory, actionName: "archive" }); + }} + > + +
+ )} + {((isPartnerGoal && goal.parentGoalId === "root") || !isPartnerGoal) && ( +
{ + e.stopPropagation(); + if (!isPartnerGoal) { + handleShareGoal(goal); + } else { + await openConfirmationPopUp({ actionCategory: "collaboration", actionName: "colabRequest" }); + } + }} + > + +
+ )} +
{ + handleUpdateGoal(goal.id, !!goal.timeBudget?.perDay); + }} + > + +
+
+
+ ); +}; + +export default RegularGoalActions; diff --git a/src/helpers/GoalController.ts b/src/helpers/GoalController.ts index b9d5e5eaa..452a4c832 100644 --- a/src/helpers/GoalController.ts +++ b/src/helpers/GoalController.ts @@ -12,6 +12,7 @@ import { } from "@src/api/GoalsAPI"; import { createGoalObjectFromTags } from "./GoalProcessor"; import { sendFinalUpdateOnGoal, sendUpdatedGoal } from "./PubSubController"; +import { restoreUserGoal } from "@src/api/TrashAPI"; export const createGoal = async ( parentGoalId: string, @@ -82,12 +83,19 @@ export const archiveGoal = async (goal: GoalItem, ancestors: string[]) => { }; export const deleteGoal = async (goal: GoalItem, ancestors: string[]) => { - sendFinalUpdateOnGoal(goal.id, "deleted", ancestors, false).then(() => { + sendFinalUpdateOnGoal(goal.id, "deleted", [...ancestors, goal.id], false).then(() => { console.log("Update Sent"); }); await removeGoalWithChildrens(goal); }; +export const restoreGoal = async (goal: GoalItem, ancestors: string[]) => { + sendFinalUpdateOnGoal(goal.id, "restored", [...ancestors, goal.id], false).then(() => { + console.log("Update Sent"); + }); + await restoreUserGoal(goal); +}; + export const deleteSharedGoal = async (goal: GoalItem) => { await removeSharedWMChildrenGoals(goal.id); if (goal.parentGoalId !== "root") { diff --git a/src/helpers/GoalProcessor.ts b/src/helpers/GoalProcessor.ts index c5352ac56..658d0fec9 100644 --- a/src/helpers/GoalProcessor.ts +++ b/src/helpers/GoalProcessor.ts @@ -126,6 +126,8 @@ export const getTypeAtPriority = (goalChanges: IChangesInGoal) => { typeAtPriority = "archived"; } else if (goalChanges.deleted.length > 0) { typeAtPriority = "deleted"; + } else if (goalChanges.restored.length > 0) { + typeAtPriority = "restored"; } return { typeAtPriority }; }; diff --git a/src/helpers/InboxProcessor.ts b/src/helpers/InboxProcessor.ts index ba2bbee68..5cbfff889 100644 --- a/src/helpers/InboxProcessor.ts +++ b/src/helpers/InboxProcessor.ts @@ -20,6 +20,7 @@ import { } from "@src/api/SharedWMAPI"; import { ITagChangesSchemaVersion, ITagsChanges } from "@src/Interfaces/IDisplayChangesModal"; import { fixDateVlauesInGoalObject } from "@src/utils"; +import { getDeletedGoal, restoreUserGoal } from "@src/api/TrashAPI"; export const handleIncomingChanges = async (payload, relId) => { if (payload.type === "sharer" && (await getSharedWMGoal(payload.rootGoalId))) { @@ -39,24 +40,28 @@ export const handleIncomingChanges = async (payload, relId) => { ]; await updateSharedWMGoal(changes[0].goal.id, changes[0].goal); } else if (payload.changeType === "deleted") { - await removeSharedWMChildrenGoals(payload.changes[0].id); - await removeSharedWMGoal(payload.changes[0].id); - getSharedWMGoal(payload.changes[0].id).then((goal: GoalItem) => { - if (goal.parentGoalId !== "root") { - getSharedWMGoal(goal.parentGoalId).then(async (parentGoal: GoalItem) => { - const parentGoalSublist = parentGoal.sublist; - const childGoalIndex = parentGoalSublist.indexOf(goal.id); - if (childGoalIndex !== -1) { - parentGoalSublist.splice(childGoalIndex, 1); - } - await updateSharedWMGoal(parentGoal.id, { sublist: parentGoalSublist }); - }); - } - }); + const goalToBeDeleted = await getSharedWMGoal(payload.changes[0].id); + await removeSharedWMChildrenGoals(goalToBeDeleted.id); + await removeSharedWMGoal(goalToBeDeleted); + if (goalToBeDeleted.parentGoalId !== "root") { + getSharedWMGoal(goalToBeDeleted.parentGoalId).then(async (parentGoal: GoalItem) => { + const parentGoalSublist = parentGoal.sublist; + const childGoalIndex = parentGoalSublist.indexOf(goalToBeDeleted.id); + if (childGoalIndex !== -1) { + parentGoalSublist.splice(childGoalIndex, 1); + } + await updateSharedWMGoal(parentGoal.id, { sublist: parentGoalSublist }); + }); + } } else if (payload.changeType === "archived") { getSharedWMGoal(payload.changes[0].id).then(async (goal: GoalItem) => archiveSharedWMGoal(goal).catch((err) => console.log(err, "failed to archive")), ); + } else if (payload.changeType === "restored") { + const goalToBeRestored = await getDeletedGoal(payload.changes[0].id); + if (goalToBeRestored) { + await restoreUserGoal(goalToBeRestored, true); + } } } else if (["sharer", "suggestion"].includes(payload.type)) { const { rootGoalId, changes, changeType } = payload; diff --git a/src/helpers/PubSubController.ts b/src/helpers/PubSubController.ts index a3b4d3d5a..cf63ee0d1 100644 --- a/src/helpers/PubSubController.ts +++ b/src/helpers/PubSubController.ts @@ -2,6 +2,7 @@ import { sendUpdatesToSubscriber } from "@src/services/contact.service"; import { getGoal, getParticipantsOfGoals } from "@src/api/GoalsAPI"; import { getHistoryUptoGoal } from "./GoalProcessor"; +import { getParticipantsOfDeletedGoal } from "@src/api/TrashAPI"; export const sendUpdatedGoal = async ( goalId: string, @@ -29,13 +30,18 @@ export const sendUpdatedGoal = async ( export const sendFinalUpdateOnGoal = async ( goalId: string, - action: "archived" | "deleted", + action: "archived" | "deleted" | "restored", ancestors: string[] = [], redefineAncestors = true, excludeSubs: string[] = [], ) => { const ancestorGoalIds = redefineAncestors ? (await getHistoryUptoGoal(goalId)).map((ele) => ele.goalID) : ancestors; const subscribers = await getParticipantsOfGoals(ancestorGoalIds); + if (action === "restored") { + (await getParticipantsOfDeletedGoal(goalId)).forEach((doc) => { + subscribers.push(doc); + }); + } subscribers .filter((ele) => !excludeSubs.includes(ele.sub.relId)) .forEach(async ({ sub, rootGoalId }) => { diff --git a/src/models/InboxItem.ts b/src/models/InboxItem.ts index 102e6a101..d8e1358be 100644 --- a/src/models/InboxItem.ts +++ b/src/models/InboxItem.ts @@ -1,6 +1,6 @@ import { GoalItem } from "./GoalItem"; -export type typeOfChange = "subgoals" | "modifiedGoals" | "archived" | "deleted"; +export type typeOfChange = "subgoals" | "modifiedGoals" | "archived" | "deleted" | "restored"; export type typeOfIntent = "suggestion" | "shared"; export type changesInId = { level: number; id: string; intent: typeOfIntent }; @@ -11,6 +11,7 @@ export interface IChangesInGoal { modifiedGoals: changesInGoal[]; archived: changesInId[]; deleted: changesInId[]; + restored: changesInId[]; } export interface InboxItem { diff --git a/src/models/TrashItem.ts b/src/models/TrashItem.ts new file mode 100755 index 000000000..7547b7fb2 --- /dev/null +++ b/src/models/TrashItem.ts @@ -0,0 +1,5 @@ +import { GoalItem } from "./GoalItem"; + +export interface TrashItem extends GoalItem { + deletedAt: String; +} diff --git a/src/models/db.ts b/src/models/db.ts old mode 100644 new mode 100755 index b23abaeca..04426b2dd --- a/src/models/db.ts +++ b/src/models/db.ts @@ -7,6 +7,7 @@ import { InboxItem } from "./InboxItem"; import { TaskItem } from "./TaskItem"; import { GCustomItem } from "./GCustomItem"; import { DumpboxItem } from "./DumpboxItem"; +import { TrashItem } from "./TrashItem"; export const dexieVersion = 17; @@ -30,6 +31,8 @@ export class ZinZenDB extends Dexie { dumpboxCollection!: Table; + goalTrashCollection!: Table; + constructor() { super("ZinZenDB"); this.version(dexieVersion) @@ -49,6 +52,8 @@ export class ZinZenDB extends Dexie { customizationCollection: "++id, goalId, posIndex", dumpboxCollection: "id, key, value", partnersCollection: null, + goalTrashCollection: + "id, deletedAt, title, duration, sublist, habit, on, start, due, afterTime, beforeTime, createdAt, parentGoalId, archived, participants, goalColor, language, link, rootGoalId, timeBudget, typeOfGoal", }) .upgrade((trans) => { console.log("🚀 ~ file: db.ts:63 ~ ZinZenDB ~ .upgrade ~ this.verno:", currentVersion); diff --git a/src/pages/GoalsPage/MyGoals.tsx b/src/pages/GoalsPage/MyGoals.tsx old mode 100644 new mode 100755 index fc218d954..848fc0ac2 --- a/src/pages/GoalsPage/MyGoals.tsx +++ b/src/pages/GoalsPage/MyGoals.tsx @@ -9,7 +9,6 @@ import ZinZenTextDark from "@assets/images/LogoTextDark.svg"; import { displayAddGoal, displayChangesModal, - displayGoalActions, displayGoalId, displayShareModal, displaySuggestionsModal, @@ -24,15 +23,17 @@ import { darkModeState, lastAction, searchActive } from "@src/store"; import AppLayout from "@src/layouts/AppLayout"; import GoalsList from "@components/GoalsComponents/GoalsList"; import ConfigGoal from "@components/GoalsComponents/GoalConfigModal/ConfigGoal"; -import MyGoalActions from "@components/GoalsComponents/MyGoalActions/MyGoalActions"; import ShareGoalModal from "@components/GoalsComponents/ShareGoalModal/ShareGoalModal"; -import ArchivedAccordion from "@components/GoalsComponents/ArchivedAccordion"; +import GoalsAccordion from "@components/GoalsComponents/GoalsAccordion"; import DisplayChangesModal from "@components/GoalsComponents/DisplayChangesModal/DisplayChangesModal"; +import { TrashItem } from "@src/models/TrashItem"; +import { getDeletedGoals } from "@src/api/TrashAPI"; export const MyGoals = () => { let debounceTimeout: ReturnType; const [activeGoals, setActiveGoals] = useState([]); const [archivedGoals, setArchivedGoals] = useState([]); + const [deletedGoals, setDeletedGoals] = useState([]); const [showActions, setShowActions] = useState({ open: "root", click: 1 }); const showAddGoal = useRecoilValue(displayAddGoal); @@ -41,23 +42,31 @@ export const MyGoals = () => { const darkModeStatus = useRecoilValue(darkModeState); const showShareModal = useRecoilValue(displayShareModal); const showUpdateGoal = useRecoilValue(displayUpdateGoal); - const showGoalActions = useRecoilValue(displayGoalActions); const showChangesModal = useRecoilValue(displayChangesModal); const showSuggestionModal = useRecoilValue(displaySuggestionsModal); const [action, setLastAction] = useRecoilState(lastAction); - const handleUserGoals = (goals: GoalItem[]) => { + const getAllGoals = async () => { + const [goals, delGoals] = await Promise.all([getActiveGoals("true"), getDeletedGoals("root")]); + console.log("🚀 ~ getAllGoals ~ goals, delGoals:", goals, delGoals); + return { goals, delGoals }; + }; + const handleUserGoals = (goals: GoalItem[], delGoals: TrashItem[]) => { + setDeletedGoals([...delGoals.map(({ deletedAt, ...goal }) => goal)]); setActiveGoals([...goals.filter((goal) => goal.archived === "false")]); setArchivedGoals([...goals.filter((goal) => goal.archived === "true" && goal.typeOfGoal === "myGoal")]); }; const refreshActiveGoals = async () => { - const goals: GoalItem[] = await getActiveGoals("true"); - handleUserGoals(goals); + const { goals, delGoals } = await getAllGoals(); + handleUserGoals(goals, delGoals); }; const search = async (text: string) => { - const goals: GoalItem[] = await getActiveGoals("true"); - handleUserGoals(goals.filter((goal) => goal.title.toUpperCase().includes(text.toUpperCase()))); + const { goals, delGoals } = await getAllGoals(); + handleUserGoals( + goals.filter((goal) => goal.title.toUpperCase().includes(text.toUpperCase())), + delGoals.filter(({ deletedAt, ...goal }) => goal.title.toUpperCase().includes(text.toUpperCase())), + ); }; const debounceSearch = (event: ChangeEvent) => { if (debounceTimeout) { @@ -88,7 +97,6 @@ export const MyGoals = () => { return ( {showShareModal && } - {showGoalActions && } {showChangesModal && }
{selectedGoalId === "root" ? ( @@ -104,8 +112,17 @@ export const MyGoals = () => { />
{archivedGoals.length > 0 && ( - + )} + {deletedGoals.length > 0 && ( + diff --git a/src/pages/GoalsPage/PartnerGoals.tsx b/src/pages/GoalsPage/PartnerGoals.tsx old mode 100644 new mode 100755 index 213acaca6..14c3b386c --- a/src/pages/GoalsPage/PartnerGoals.tsx +++ b/src/pages/GoalsPage/PartnerGoals.tsx @@ -11,13 +11,13 @@ import { GoalSublist } from "@components/GoalsComponents/GoalSublist/GoalSublist import { displayGoalActions, displayGoalId } from "@src/store/GoalsState"; import { darkModeState, lastAction, searchActive } from "@src/store"; -import ArchivedAccordion from "@components/GoalsComponents/ArchivedAccordion"; -import GoalLocStateHandler from "@src/helpers/GoalLocStateHandler"; -import AppLayout from "@src/layouts/AppLayout"; import GoalsList from "@components/GoalsComponents/GoalsList"; -import MyGoalActions from "@components/GoalsComponents/MyGoalActions/MyGoalActions"; +import AppLayout from "@src/layouts/AppLayout"; import ContactItem from "@src/models/ContactItem"; +import GoalsAccordion from "@components/GoalsComponents/GoalsAccordion"; +import GoalLocStateHandler from "@src/helpers/GoalLocStateHandler"; import { getRootGoalsOfPartner } from "@src/api/SharedWMAPI"; +import RegularGoalActions from "@components/GoalsComponents/MyGoalActions/RegularGoalActions"; import InvitationStatus from "./InvitationStatus"; @@ -76,7 +76,7 @@ const PartnerGoals = ({ partner }: { partner: ContactItem }) => { return ( - {showGoalActions && } + {showGoalActions && }
{selectedGoalId === "root" ? (
@@ -89,8 +89,9 @@ const PartnerGoals = ({ partner }: { partner: ContactItem }) => { setGoals={setActiveGoals} />
- diff --git a/src/store/GoalsState.ts b/src/store/GoalsState.ts index b42e48ef9..998842067 100644 --- a/src/store/GoalsState.ts +++ b/src/store/GoalsState.ts @@ -9,9 +9,12 @@ export interface ISubGoalHistory { goalTitle: string; } +export type TAction = "deleted" | "archived" | "regular"; +export type TDisplayGoalActions = { actionType: TAction; goal: GoalItem }; + export const displayGoalActions = atom({ key: "displayGoalActions", - default: null as GoalItem | null, + default: null as TDisplayGoalActions | null, }); export const displayGoalConfigModal = atom({ diff --git a/src/store/index.ts b/src/store/index.ts old mode 100644 new mode 100755 index 11c315373..360cc01c2 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -3,26 +3,38 @@ import { confirmActionState } from "@src/Interfaces/IPopupModals"; import ContactItem from "@src/models/ContactItem"; import { darkModeState } from "./DarkModeState"; import { languageSelectionState } from "./LanguageSelectionState"; +import { isJSONParsable } from "@src/utils/patterns"; + +const defaultConfirmationObj: confirmActionState = { + open: false, + goal: { + archive: true, + delete: true, + shareAnonymously: true, + shareWithOne: true, + restore: true, + }, + collaboration: { + colabRequest: true, + delete: true, + archive: true, + restore: true, + }, +}; + +if (isJSONParsable(localStorage.getItem("confirmationState"))) { + //@ts-ignore + const savedConfirmationState = JSON.parse(localStorage.getItem("confirmationService")); + defaultConfirmationObj.goal = { ...defaultConfirmationObj.goal, ...(savedConfirmationState?.goal || {}) }; + defaultConfirmationObj.collaboration = { + ...defaultConfirmationObj.collaboration, + ...(savedConfirmationState?.collaboration || {}), + }; +} export const displayConfirmation = atom({ key: "displayConfirmation", - default: JSON.parse( - localStorage.getItem("confirmationState") || - JSON.stringify({ - open: false, - goal: { - archive: true, - delete: true, - shareAnonymously: true, - shareWithOne: true, - }, - collaboration: { - colabRequest: true, - delete: true, - archive: true, - }, - }), - ) as confirmActionState, + default: defaultConfirmationObj, }); export const lastAction = atom({ diff --git a/src/utils/defaultGenerators.ts b/src/utils/defaultGenerators.ts index eb0f6b553..4d71305e7 100644 --- a/src/utils/defaultGenerators.ts +++ b/src/utils/defaultGenerators.ts @@ -27,5 +27,6 @@ export function getDefaultValueOfGoalChanges() { modifiedGoals: [], archived: [], deleted: [], + restored: [], }; } diff --git a/src/utils/patterns.ts b/src/utils/patterns.ts old mode 100644 new mode 100755 index 4d1ec9b1e..cd93fc016 --- a/src/utils/patterns.ts +++ b/src/utils/patterns.ts @@ -34,3 +34,13 @@ export function summarizeUrl(url: string) { } return ""; } + +export function isJSONParsable(str: string | null | undefined): boolean { + if (!str) return false; + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } +}