From a9af6164d4acff312fbbaa6b0b281ef26d874a48 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Wed, 11 Dec 2024 20:45:02 -0500 Subject: [PATCH 1/5] Remove unnecessary Grid item in ChoreCard component --- src/views/Chores/ChoreCard.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/views/Chores/ChoreCard.jsx b/src/views/Chores/ChoreCard.jsx index 9e65134..44e8c1f 100644 --- a/src/views/Chores/ChoreCard.jsx +++ b/src/views/Chores/ChoreCard.jsx @@ -466,7 +466,6 @@ const ChoreCard = ({ > { navigate(`/chores/${chore.id}`) @@ -556,7 +555,6 @@ const ChoreCard = ({ */} Date: Wed, 11 Dec 2024 20:46:02 -0500 Subject: [PATCH 2/5] Add Section to separate different tasks --- src/views/Chores/MyChores.jsx | 230 ++++++++++++++++++++++++++++++---- 1 file changed, 207 insertions(+), 23 deletions(-) diff --git a/src/views/Chores/MyChores.jsx b/src/views/Chores/MyChores.jsx index 72cd809..6d4acb7 100644 --- a/src/views/Chores/MyChores.jsx +++ b/src/views/Chores/MyChores.jsx @@ -2,15 +2,20 @@ import { Add, CancelRounded, EditCalendar, + ExpandCircleDown, FilterAlt, PriorityHigh, Style, } from '@mui/icons-material' import { + Accordion, + AccordionDetails, + AccordionGroup, Box, Button, Chip, Container, + Divider, IconButton, Input, List, @@ -38,6 +43,8 @@ const MyChores = () => { const [chores, setChores] = useState([]) const [filteredChores, setFilteredChores] = useState([]) const [selectedFilter, setSelectedFilter] = useState('All') + const [choreSections, setChoreSections] = useState([]) + const [openChoreSections, setOpenChoreSections] = useState({}) const [searchTerm, setSearchTerm] = useState('') const [activeUserId, setActiveUserId] = useState(0) const [performers, setPerformers] = useState([]) @@ -74,6 +81,108 @@ const MyChores = () => { return aDueDate - bDueDate // Sort ascending by due date } + const sectionSorter = (t, 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) + }) + + var groups = [] + switch (t) { + case 'due_date': + var groupRaw = { + Today: [], + 'In a week': [], + 'This month': [], + Later: [], + Overdue: [], + Anytime: [], + } + chores.forEach(chore => { + if (chore.nextDueDate === null) { + groupRaw['Anytime'].push(chore) + } else if (new Date(chore.nextDueDate) < new Date()) { + groupRaw['Overdue'].push(chore) + } else if ( + new Date(chore.nextDueDate).toDateString() === + new Date().toDateString() + ) { + groupRaw['Today'].push(chore) + } else if ( + new Date(chore.nextDueDate) < + new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) && + new Date(chore.nextDueDate) > new Date() + ) { + groupRaw['In a week'].push(chore) + } else if ( + new Date(chore.nextDueDate).getMonth() === new Date().getMonth() + ) { + groupRaw['This month'].push(chore) + } else { + groupRaw['Later'].push(chore) + } + }) + groups = [ + { name: 'Overdue', content: groupRaw['Overdue'] }, + { name: 'Today', content: groupRaw['Today'] }, + { name: 'In a week', content: groupRaw['In a week'] }, + { name: 'This month', content: groupRaw['This month'] }, + { name: 'Later', content: groupRaw['Later'] }, + { name: 'Anytime', content: groupRaw['Anytime'] }, + ] + break + case 'priority': + groupRaw = { + p1: [], + p2: [], + p3: [], + p4: [], + no_priority: [], + } + chores.forEach(chore => { + switch (chore.priority) { + case 1: + groupRaw['p1'].push(chore) + break + case 2: + groupRaw['p2'].push(chore) + break + case 3: + groupRaw['p3'].push(chore) + break + case 4: + groupRaw['p4'].push(chore) + break + } + }) + break + case 'labels': + groupRaw = {} + chores.forEach(chore => { + chore.labelsV2.forEach(label => { + if (groupRaw[label.id] === undefined) { + groupRaw[label.id] = [] + } + groupRaw[label.id].push(chore) + }) + }) + } + return groups + } + useEffect(() => { if (userProfile === null) { GetUserProfile() @@ -100,6 +209,14 @@ const MyChores = () => { const sortedChores = choresData.res.sort(choreSorter) setChores(sortedChores) setFilteredChores(sortedChores) + const sections = sectionSorter('due_date', sortedChores) + setChoreSections(sections) + setOpenChoreSections( + Object.keys(sections).reduce((acc, key) => { + acc[key] = true + return acc + }, {}), + ) } }, [choresData, choresLoading]) @@ -231,11 +348,6 @@ const MyChores = () => { return ( - {/* - My Chores - */} - {/* */} - {/* Search box to filter */} { mt: 1, mb: 1, borderRadius: 24, - // border: '1px solid', height: 24, borderColor: 'text.disabled', padding: 1, @@ -271,7 +382,6 @@ const MyChores = () => { } /> } options={Priorities} selectedItem={selectedFilter} @@ -282,7 +392,6 @@ const MyChores = () => { isActive={selectedFilter.startsWith('Priority: ')} /> } // TODO : this need simplification we want to display both user labels and chore labels // that why we are merging them here. @@ -395,7 +504,6 @@ const MyChores = () => { Current Filter: {selectedFilter} )} - {/* */} {filteredChores.length === 0 && ( { {chores.length > 0 && ( <> - )} + + {showTokenId === token?.id && ( + + { + navigator.clipboard.writeText(token.token) + alert('Token copied to clipboard') + setShowTokenId(null) + }} + > + + + } + /> + + )} ))} From aff432b74ae33e46ba18c660e89dbae4d90461d6 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sat, 14 Dec 2024 02:11:59 -0500 Subject: [PATCH 4/5] Add notification target update functionality --- src/utils/Fetcher.jsx | 9 ++ src/views/Authorization/Signup.jsx | 7 +- src/views/Settings/NotificationSetting.jsx | 180 ++++++++++++++------- 3 files changed, 132 insertions(+), 64 deletions(-) diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx index 836431e..0bdeb21 100644 --- a/src/utils/Fetcher.jsx +++ b/src/utils/Fetcher.jsx @@ -225,6 +225,14 @@ const UpdateUserDetails = userDetails => { }) } +const UpdateNotificationTarget = notificationTarget => { + return Fetch(`${API_URL}/users/targets`, { + method: 'PUT', + headers: HEADERS(), + body: JSON.stringify(notificationTarget), + }) +} + const GetSubscriptionSession = () => { return Fetch(API_URL + `/payments/create-subscription`, { method: 'GET', @@ -373,6 +381,7 @@ export { UpdateChoreHistory, UpdateChorePriority, UpdateLabel, + UpdateNotificationTarget, UpdatePassword, UpdateThingState, UpdateUserDetails, diff --git a/src/views/Authorization/Signup.jsx b/src/views/Authorization/Signup.jsx index 9e6cc70..d9665be 100644 --- a/src/views/Authorization/Signup.jsx +++ b/src/views/Authorization/Signup.jsx @@ -42,7 +42,7 @@ const SignupView = () => { }) } else { console.log('Login failed', response) - + // Navigate('/login') } }) @@ -103,12 +103,13 @@ const SignupView = () => { signUp(username, password, displayName, email).then(response => { if (response.status === 201) { handleLogin(username, password) + } else if (response.status === 403) { + setError('Signup disabled, please contact admin') } else { console.log('Signup failed') response.json().then(res => { setError(res.error) - } - ) + }) } }) } diff --git a/src/views/Settings/NotificationSetting.jsx b/src/views/Settings/NotificationSetting.jsx index 4ead3b9..5a09ecd 100644 --- a/src/views/Settings/NotificationSetting.jsx +++ b/src/views/Settings/NotificationSetting.jsx @@ -1,85 +1,143 @@ import { Button, Divider, Input, Option, Select, Typography } from '@mui/joy' -import { useContext, useEffect, useState } from 'react' +import { useContext, useState } from 'react' import { UserContext } from '../../contexts/UserContext' -import { GetUserProfile, UpdateUserDetails } from '../../utils/Fetcher' +import { UpdateNotificationTarget } from '../../utils/Fetcher' const NotificationSetting = () => { const { userProfile, setUserProfile } = useContext(UserContext) - useEffect(() => { - if (!userProfile) { - GetUserProfile().then(resp => { - resp.json().then(data => { - setUserProfile(data.res) - setChatID(data.res.chatID) - }) - }) - } - }, []) - const [chatID, setChatID] = useState(userProfile?.chatID) + const [notificationTarget, setNotificationTarget] = useState( + userProfile?.notification_target + ? String(userProfile.notification_target.type) + : '0', + ) + const [chatID, setChatID] = useState( + userProfile?.notification_target?.target_id, + ) + const [error, setError] = useState('') + const SaveValidation = () => { + switch (notificationTarget) { + case '1': + if (chatID === '') { + setError('Chat ID is required') + return false + } else if (isNaN(chatID) || chatID === '0') { + setError('Invalid Chat ID') + return false + } + break + case '2': + if (chatID === '') { + setError('User key is required') + return false + } + break + default: + break + } + setError('') + return true + } + const handleSave = () => { + if (!SaveValidation()) return + + UpdateNotificationTarget({ + target: chatID, + type: Number(notificationTarget), + }).then(resp => { + alert('Notification target updated') + setUserProfile({ + ...userProfile, + notification_target: { + target: chatID, + type: Number(notificationTarget), + }, + }) + }) + } return (
Notification Settings Manage your notification settings - setNotificationTarget(selected)} + > + + + + {notificationTarget === '1' && ( + <> + + You need to initiate a message to the bot in order for the Telegram + notification to work{' '} + + Click here + {' '} + to start a chat + - - You need to initiate a message to the bot in order for the Telegram - notification to work{' '} - - Click here - {' '} - to start a chat - + Chat ID - setChatID(e.target.value)} - placeholder='User ID / Chat ID' - sx={{ - width: '200px', - }} - /> - - If you don't know your Chat ID, start chat with userinfobot and it will - send you your Chat ID.{' '} - - Click here - {' '} - to start chat with userinfobot{' '} - + setChatID(e.target.value)} + placeholder='User ID / Chat ID' + sx={{ + width: '200px', + }} + /> + + If you don't know your Chat ID, start chat with userinfobot and it + will send you your Chat ID.{' '} + + Click here + {' '} + to start chat with userinfobot{' '} + + + )} + {notificationTarget === '2' && ( + <> + User key + setChatID(e.target.value)} + placeholder='User ID' + sx={{ + width: '200px', + }} + /> + + )} + {error && ( + + {error} + + )} From 7ea0a03d53015fe3a7bb9991709665378282cf9e Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sat, 14 Dec 2024 02:13:14 -0500 Subject: [PATCH 5/5] Update package.json version to 0.1.82 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 95048d1..acf57ae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "donetick", "private": true, - "version": "0.1.80", + "version": "0.1.82", "type": "module", "lint-staged": { "*.{js,jsx,ts,tsx}": [