diff --git a/package-lock.json b/package-lock.json index 88f86ec..fb72e97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "donetick", - "version": "0.1.91", + "version": "0.1.94", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "donetick", - "version": "0.1.91", + "version": "0.1.94", "dependencies": { "@capacitor/android": "^6.1.1", "@capacitor/app": "^6.0.0", @@ -18,6 +18,8 @@ "@capacitor/preferences": "^6.0.1", "@capacitor/push-notifications": "^6.0.1", "@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.4", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.15.2", @@ -1804,6 +1806,59 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", diff --git a/package.json b/package.json index d92ccd3..3681a54 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "donetick", "private": true, - "version": "0.1.94", + "version": "0.1.95", "type": "module", "lint-staged": { "*.{js,jsx,ts,tsx}": [ @@ -32,6 +32,8 @@ "@capacitor/preferences": "^6.0.1", "@capacitor/push-notifications": "^6.0.1", "@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.4", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.15.2", diff --git a/src/utils/Chores.jsx b/src/utils/Chores.jsx index ad917b0..4366bfa 100644 --- a/src/utils/Chores.jsx +++ b/src/utils/Chores.jsx @@ -1,25 +1,11 @@ import moment from 'moment' import { TASK_COLOR } from './Colors.jsx' +const priorityOrder = [1, 2, 3, 4, 0] + export const ChoresGrouper = (groupBy, chores) => { // sort by priority then due date: - chores.sort((a, b) => { - // no priority is lowest priority: - if (a.priority === 0) { - return 1 - } - if (a.priority !== b.priority) { - return a.priority - b.priority - } - if (a.nextDueDate === null) { - return 1 - } - if (b.nextDueDate === null) { - return -1 - } - return new Date(a.nextDueDate) - new Date(b.nextDueDate) - }) - + chores.sort(ChoreSorter) var groups = [] switch (groupBy) { case 'due_date': @@ -159,7 +145,25 @@ export const ChoresGrouper = (groupBy, chores) => { } return groups } +export const ChoreSorter = (a, b) => { + const priorityA = priorityOrder.indexOf(a.priority) + const priorityB = priorityOrder.indexOf(b.priority) + if (priorityA !== priorityB) { + return priorityA - priorityB + } + // Status sorting (0 > 1 > ... ascending order) + if (a.status !== b.status) { + return a.status - b.status + } + + // Due date sorting (earlier dates first, null/undefined last) + if (!a.nextDueDate && !b.nextDueDate) return 0 + if (!a.nextDueDate) return 1 + if (!b.nextDueDate) return -1 + + return new Date(a.nextDueDate) - new Date(b.nextDueDate) +} export const notInCompletionWindow = chore => { return ( chore.completionWindow && diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx index 9449cf5..e2b0630 100644 --- a/src/utils/Fetcher.jsx +++ b/src/utils/Fetcher.jsx @@ -126,6 +126,15 @@ const MarkChoreComplete = (id, note, completedDate, performer) => { }) } +const CompleteSubTask = (id, choreId, completedAt) => { + var markChoreURL = `/chores/${choreId}/subtask` + return Fetch(markChoreURL, { + method: 'PUT', + headers: HEADERS(), + body: JSON.stringify({ completedAt, id, choreId }), + }) +} + const SkipChore = id => { return Fetch(`/chores/${id}/skip`, { method: 'POST', @@ -476,6 +485,7 @@ export { ArchiveChore, CancelSubscription, ChangePassword, + CompleteSubTask, CreateChore, CreateLabel, CreateLongLiveToken, diff --git a/src/utils/Priorities.jsx b/src/utils/Priorities.jsx index 95b4ca6..ac03d03 100644 --- a/src/utils/Priorities.jsx +++ b/src/utils/Priorities.jsx @@ -7,16 +7,10 @@ import { const Priorities = [ { - name: 'P4', - value: 4, - icon: , - color: '', - }, - { - name: 'P3 ', - value: 3, - icon: , - color: '', + name: 'P1', + value: 1, + icon: , + color: 'danger', }, { name: 'P2', @@ -25,10 +19,16 @@ const Priorities = [ color: 'warning', }, { - name: 'P1', - value: 1, - icon: , - color: 'danger', + name: 'P3 ', + value: 3, + icon: , + color: '', + }, + { + name: 'P4', + value: 4, + icon: , + color: '', }, ] diff --git a/src/views/ChoreEdit/ChoreEdit.jsx b/src/views/ChoreEdit/ChoreEdit.jsx index c82ad07..ddcb853 100644 --- a/src/views/ChoreEdit/ChoreEdit.jsx +++ b/src/views/ChoreEdit/ChoreEdit.jsx @@ -9,7 +9,6 @@ import { Divider, FormControl, FormHelperText, - FormLabel, Input, List, ListItem, @@ -40,6 +39,8 @@ import { SaveChore, } from '../../utils/Fetcher' import { isPlusAccount } from '../../utils/Helpers' +import Priorities from '../../utils/Priorities.jsx' +import SubTasks from '../components/SubTask.jsx' import { useLabels } from '../Labels/LabelQueries' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import LabelModal from '../Modals/Inputs/LabelModal' @@ -76,7 +77,9 @@ const ChoreEdit = () => { const [frequencyMetadata, setFrequencyMetadata] = useState({}) const [labels, setLabels] = useState([]) const [labelsV2, setLabelsV2] = useState([]) + const [priority, setPriority] = useState(0) const [points, setPoints] = useState(-1) + const [subTasks, setSubTasks] = useState(null) const [completionWindow, setCompletionWindow] = useState(-1) const [allUserThings, setAllUserThings] = useState([]) const [thingTrigger, setThingTrigger] = useState(null) @@ -201,10 +204,12 @@ const ChoreEdit = () => { notification: isNotificable, labels: labels.map(l => l.name), labelsV2: labelsV2, + subTasks: subTasks, notificationMetadata: notificationMetadata, thingTrigger: thingTrigger, points: points < 0 ? null : points, completionWindow: completionWindow < 0 ? null : completionWindow, + priority: priority, } let SaveFunction = CreateChore if (choreId > 0) { @@ -265,6 +270,8 @@ const ChoreEdit = () => { ) setLabelsV2(data.res.labelsV2) + setSubTasks(data.res.subTasks) + setPriority(data.res.priority) setAssignStrategy( data.res.assignStrategy ? data.res.assignStrategy @@ -382,7 +389,7 @@ const ChoreEdit = () => { */} - Title : + Name : What is the name of this chore? setName(e.target.value)} /> {errors.name} @@ -390,8 +397,8 @@ const ChoreEdit = () => { - Details: - What is this chore about? + Additional Details : + What is this task about?