Add Support for LabelV2, Add LabelModal and LabelView.
Add React Query
This commit is contained in:
parent
5e590bfe9f
commit
42182371ff
18 changed files with 839 additions and 71 deletions
118
package-lock.json
generated
118
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "donetick",
|
"name": "donetick",
|
||||||
"version": "0.1.72",
|
"version": "0.1.78",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "donetick",
|
"name": "donetick",
|
||||||
"version": "0.1.72",
|
"version": "0.1.78",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.3",
|
"@emotion/react": "^11.11.3",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
|
@ -23,7 +23,9 @@
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
"react-router-dom": "^6.21.1",
|
"react-router-dom": "^6.21.1",
|
||||||
|
"react-transition-group": "^4.4.5",
|
||||||
"reactjs-social-login": "^2.6.3",
|
"reactjs-social-login": "^2.6.3",
|
||||||
"vite-plugin-pwa": "^0.20.0"
|
"vite-plugin-pwa": "^0.20.0"
|
||||||
},
|
},
|
||||||
|
@ -4142,6 +4144,15 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/big-integer": {
|
||||||
|
"version": "1.6.52",
|
||||||
|
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
|
||||||
|
"integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
|
||||||
|
"license": "Unlicense",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
@ -4182,6 +4193,22 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/broadcast-channel": {
|
||||||
|
"version": "3.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz",
|
||||||
|
"integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.2",
|
||||||
|
"detect-node": "^2.1.0",
|
||||||
|
"js-sha3": "0.8.0",
|
||||||
|
"microseconds": "0.2.0",
|
||||||
|
"nano-time": "1.0.0",
|
||||||
|
"oblivious-set": "1.0.0",
|
||||||
|
"rimraf": "3.0.2",
|
||||||
|
"unload": "2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.23.1",
|
"version": "4.23.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz",
|
||||||
|
@ -4664,6 +4691,12 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-node": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
|
@ -6360,6 +6393,12 @@
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-sha3": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
@ -6574,6 +6613,16 @@
|
||||||
"sourcemap-codec": "^1.4.8"
|
"sourcemap-codec": "^1.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/match-sorter": {
|
||||||
|
"version": "6.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz",
|
||||||
|
"integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.8",
|
||||||
|
"remove-accents": "0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
@ -6594,6 +6643,12 @@
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/microseconds": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/mimic-response": {
|
"node_modules/mimic-response": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||||
|
@ -6665,6 +6720,15 @@
|
||||||
"thenify-all": "^1.0.0"
|
"thenify-all": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nano-time": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"big-integer": "^1.6.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
|
@ -6864,6 +6928,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oblivious-set": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
@ -7481,6 +7551,32 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-query": {
|
||||||
|
"version": "3.39.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
|
||||||
|
"integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"broadcast-channel": "^3.4.1",
|
||||||
|
"match-sorter": "^6.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "6.21.1",
|
"version": "6.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.1.tgz",
|
||||||
|
@ -7515,6 +7611,7 @@
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"dom-helpers": "^5.0.1",
|
"dom-helpers": "^5.0.1",
|
||||||
|
@ -7673,6 +7770,12 @@
|
||||||
"jsesc": "bin/jsesc"
|
"jsesc": "bin/jsesc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/remove-accents": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/require-from-string": {
|
"node_modules/require-from-string": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
@ -7728,7 +7831,6 @@
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
},
|
},
|
||||||
|
@ -8857,6 +8959,16 @@
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unload": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.6.2",
|
||||||
|
"detect-node": "^2.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/upath": {
|
"node_modules/upath": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
|
||||||
|
|
|
@ -35,7 +35,9 @@
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
"react-router-dom": "^6.21.1",
|
"react-router-dom": "^6.21.1",
|
||||||
|
"react-transition-group": "^4.4.5",
|
||||||
"reactjs-social-login": "^2.6.3",
|
"reactjs-social-login": "^2.6.3",
|
||||||
"vite-plugin-pwa": "^0.20.0"
|
"vite-plugin-pwa": "^0.20.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,7 @@ import NavBar from '@/views/components/NavBar'
|
||||||
import { Button, Snackbar, Typography, useColorScheme } from '@mui/joy'
|
import { Button, Snackbar, Typography, useColorScheme } from '@mui/joy'
|
||||||
import Tracker from '@openreplay/tracker'
|
import Tracker from '@openreplay/tracker'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import { QueryClient, QueryClientProvider } from 'react-query'
|
||||||
import { Outlet } from 'react-router-dom'
|
import { Outlet } from 'react-router-dom'
|
||||||
import { useRegisterSW } from 'virtual:pwa-register/react'
|
import { useRegisterSW } from 'virtual:pwa-register/react'
|
||||||
import { UserContext } from './contexts/UserContext'
|
import { UserContext } from './contexts/UserContext'
|
||||||
|
@ -20,6 +21,8 @@ const remove = className => {
|
||||||
const intervalMS = 5 * 60 * 1000 // 5 minutes
|
const intervalMS = 5 * 60 * 1000 // 5 minutes
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const queryClient = new QueryClient()
|
||||||
|
|
||||||
startOpenReplay()
|
startOpenReplay()
|
||||||
|
|
||||||
const { mode, systemMode } = useColorScheme()
|
const { mode, systemMode } = useColorScheme()
|
||||||
|
@ -92,6 +95,7 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='min-h-screen'>
|
<div className='min-h-screen'>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
<AuthenticationProvider />
|
<AuthenticationProvider />
|
||||||
<UserContext.Provider value={{ userProfile, setUserProfile }}>
|
<UserContext.Provider value={{ userProfile, setUserProfile }}>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
|
@ -114,6 +118,8 @@ function App() {
|
||||||
</Button>
|
</Button>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
)}
|
)}
|
||||||
|
</QueryClientProvider>
|
||||||
|
,
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ChoreView from '../views/ChoreEdit/ChoreView'
|
||||||
import MyChores from '../views/Chores/MyChores'
|
import MyChores from '../views/Chores/MyChores'
|
||||||
import JoinCircleView from '../views/Circles/JoinCircle'
|
import JoinCircleView from '../views/Circles/JoinCircle'
|
||||||
import ChoreHistory from '../views/History/ChoreHistory'
|
import ChoreHistory from '../views/History/ChoreHistory'
|
||||||
|
import LabelView from '../views/Labels/LabelView'
|
||||||
import Landing from '../views/Landing/Landing'
|
import Landing from '../views/Landing/Landing'
|
||||||
import PaymentCancelledView from '../views/Payments/PaymentFailView'
|
import PaymentCancelledView from '../views/Payments/PaymentFailView'
|
||||||
import PaymentSuccessView from '../views/Payments/PaymentSuccessView'
|
import PaymentSuccessView from '../views/Payments/PaymentSuccessView'
|
||||||
|
@ -116,6 +117,10 @@ const Router = createBrowserRouter([
|
||||||
path: 'things/:id',
|
path: 'things/:id',
|
||||||
element: <ThingsHistory />,
|
element: <ThingsHistory />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'labels/',
|
||||||
|
element: <LabelView />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
17
src/queries/ChoreQueries.jsx
Normal file
17
src/queries/ChoreQueries.jsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||||
|
import { useQuery } from 'react-query'
|
||||||
|
import { CreateChore, GetChoresNew } from '../utils/Fetcher'
|
||||||
|
|
||||||
|
export const useChores = () => {
|
||||||
|
return useQuery('chores', GetChoresNew)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCreateChore = () => {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(CreateChore, {
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries('chores')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
6
src/queries/UserQueries.jsx
Normal file
6
src/queries/UserQueries.jsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { useQuery } from 'react-query'
|
||||||
|
import { GetAllUsers } from '../utils/Fetcher'
|
||||||
|
|
||||||
|
export const useAllUsers = () => {
|
||||||
|
return useQuery('allUsers', GetAllUsers)
|
||||||
|
}
|
|
@ -45,6 +45,13 @@ const GetAllUsers = () => {
|
||||||
headers: HEADERS(),
|
headers: HEADERS(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const GetChoresNew = async () => {
|
||||||
|
const resp = await Fetch(`${API_URL}/chores/`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: HEADERS(),
|
||||||
|
})
|
||||||
|
return resp.json()
|
||||||
|
}
|
||||||
|
|
||||||
const GetChores = () => {
|
const GetChores = () => {
|
||||||
return Fetch(`${API_URL}/chores/`, {
|
return Fetch(`${API_URL}/chores/`, {
|
||||||
|
@ -294,16 +301,49 @@ const GetLongLiveTokens = () => {
|
||||||
headers: HEADERS(),
|
headers: HEADERS(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CreateLabel = label => {
|
||||||
|
return Fetch(`${API_URL}/labels`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: HEADERS(),
|
||||||
|
body: JSON.stringify(label),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetLabels = async () => {
|
||||||
|
const resp = await Fetch(`${API_URL}/labels`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: HEADERS(),
|
||||||
|
})
|
||||||
|
return resp.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateLabel = label => {
|
||||||
|
return Fetch(`${API_URL}/labels`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: HEADERS(),
|
||||||
|
body: JSON.stringify(label),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const DeleteLabel = id => {
|
||||||
|
return Fetch(`${API_URL}/labels/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: HEADERS(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AcceptCircleMemberRequest,
|
AcceptCircleMemberRequest,
|
||||||
CancelSubscription,
|
CancelSubscription,
|
||||||
createChore,
|
createChore,
|
||||||
CreateChore,
|
CreateChore,
|
||||||
|
CreateLabel,
|
||||||
CreateLongLiveToken,
|
CreateLongLiveToken,
|
||||||
CreateThing,
|
CreateThing,
|
||||||
DeleteChore,
|
DeleteChore,
|
||||||
DeleteChoreHistory,
|
DeleteChoreHistory,
|
||||||
DeleteCircleMember,
|
DeleteCircleMember,
|
||||||
|
DeleteLabel,
|
||||||
DeleteLongLiveToken,
|
DeleteLongLiveToken,
|
||||||
DeleteThing,
|
DeleteThing,
|
||||||
GetAllCircleMembers,
|
GetAllCircleMembers,
|
||||||
|
@ -312,7 +352,9 @@ export {
|
||||||
GetChoreDetailById,
|
GetChoreDetailById,
|
||||||
GetChoreHistory,
|
GetChoreHistory,
|
||||||
GetChores,
|
GetChores,
|
||||||
|
GetChoresNew,
|
||||||
GetCircleMemberRequests,
|
GetCircleMemberRequests,
|
||||||
|
GetLabels,
|
||||||
GetLongLiveTokens,
|
GetLongLiveTokens,
|
||||||
GetSubscriptionSession,
|
GetSubscriptionSession,
|
||||||
GetThingHistory,
|
GetThingHistory,
|
||||||
|
@ -330,6 +372,7 @@ export {
|
||||||
UpdateChoreAssignee,
|
UpdateChoreAssignee,
|
||||||
UpdateChoreHistory,
|
UpdateChoreHistory,
|
||||||
UpdateChorePriority,
|
UpdateChorePriority,
|
||||||
|
UpdateLabel,
|
||||||
UpdatePassword,
|
UpdatePassword,
|
||||||
UpdateThingState,
|
UpdateThingState,
|
||||||
UpdateUserDetails,
|
UpdateUserDetails,
|
||||||
|
|
38
src/utils/LabelColors.jsx
Normal file
38
src/utils/LabelColors.jsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
const LABEL_COLORS = [
|
||||||
|
{ name: 'Default', value: '#FFFFFF' },
|
||||||
|
{ name: 'Salmon', value: '#ff7961' },
|
||||||
|
{ name: 'Teal', value: '#26a69a' },
|
||||||
|
{ name: 'Sky Blue', value: '#80d8ff' },
|
||||||
|
{ name: 'Grape', value: '#7e57c2' },
|
||||||
|
{ name: 'Sunshine', value: '#ffee58' },
|
||||||
|
{ name: 'Coral', value: '#ff7043' },
|
||||||
|
{ name: 'Lavender', value: '#ce93d8' },
|
||||||
|
{ name: 'Rose', value: '#f48fb1' },
|
||||||
|
{ name: 'Charcoal', value: '#616161' },
|
||||||
|
{ name: 'Sienna', value: '#8d6e63' },
|
||||||
|
{ name: 'Mint', value: '#a7ffeb' },
|
||||||
|
{ name: 'Amber', value: '#ffc107' },
|
||||||
|
{ name: 'Cobalt', value: '#3f51b5' },
|
||||||
|
{ name: 'Emerald', value: '#4caf50' },
|
||||||
|
{ name: 'Peach', value: '#ffab91' },
|
||||||
|
{ name: 'Ocean', value: '#0288d1' },
|
||||||
|
{ name: 'Mustard', value: '#ffca28' },
|
||||||
|
{ name: 'Ruby', value: '#d32f2f' },
|
||||||
|
{ name: 'Periwinkle', value: '#b39ddb' },
|
||||||
|
{ name: 'Turquoise', value: '#00bcd4' },
|
||||||
|
{ name: 'Lime', value: '#cddc39' },
|
||||||
|
{ name: 'Blush', value: '#f8bbd0' },
|
||||||
|
{ name: 'Ash', value: '#90a4ae' },
|
||||||
|
{ name: 'Sand', value: '#d7ccc8' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export default LABEL_COLORS
|
||||||
|
|
||||||
|
export const getTextColorFromBackgroundColor = bgColor => {
|
||||||
|
if (!bgColor) return ''
|
||||||
|
const hex = bgColor.replace('#', '')
|
||||||
|
const r = parseInt(hex.substring(0, 2), 16)
|
||||||
|
const g = parseInt(hex.substring(2, 4), 16)
|
||||||
|
const b = parseInt(hex.substring(4, 6), 16)
|
||||||
|
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#ffffff'
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Add } from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
@ -11,6 +12,7 @@ import {
|
||||||
Input,
|
Input,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
|
MenuItem,
|
||||||
Option,
|
Option,
|
||||||
Radio,
|
Radio,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
|
@ -34,8 +36,10 @@ import {
|
||||||
SaveChore,
|
SaveChore,
|
||||||
} from '../../utils/Fetcher'
|
} from '../../utils/Fetcher'
|
||||||
import { isPlusAccount } from '../../utils/Helpers'
|
import { isPlusAccount } from '../../utils/Helpers'
|
||||||
import FreeSoloCreateOption from '../components/AutocompleteSelect'
|
import { getTextColorFromBackgroundColor } from '../../utils/LabelColors'
|
||||||
|
import { useLabels } from '../Labels/LabelQueries'
|
||||||
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
||||||
|
import LabelModal from '../Modals/Inputs/LabelModal'
|
||||||
import RepeatSection from './RepeatSection'
|
import RepeatSection from './RepeatSection'
|
||||||
const ASSIGN_STRATEGIES = [
|
const ASSIGN_STRATEGIES = [
|
||||||
'random',
|
'random',
|
||||||
|
@ -66,6 +70,7 @@ const ChoreEdit = () => {
|
||||||
const [frequency, setFrequency] = useState(1)
|
const [frequency, setFrequency] = useState(1)
|
||||||
const [frequencyMetadata, setFrequencyMetadata] = useState({})
|
const [frequencyMetadata, setFrequencyMetadata] = useState({})
|
||||||
const [labels, setLabels] = useState([])
|
const [labels, setLabels] = useState([])
|
||||||
|
const [labelsV2, setLabelsV2] = useState([])
|
||||||
const [allUserThings, setAllUserThings] = useState([])
|
const [allUserThings, setAllUserThings] = useState([])
|
||||||
const [thingTrigger, setThingTrigger] = useState(null)
|
const [thingTrigger, setThingTrigger] = useState(null)
|
||||||
const [isThingValid, setIsThingValid] = useState(false)
|
const [isThingValid, setIsThingValid] = useState(false)
|
||||||
|
@ -82,6 +87,9 @@ const ChoreEdit = () => {
|
||||||
const [isSnackbarOpen, setIsSnackbarOpen] = useState(false)
|
const [isSnackbarOpen, setIsSnackbarOpen] = useState(false)
|
||||||
const [snackbarMessage, setSnackbarMessage] = useState('')
|
const [snackbarMessage, setSnackbarMessage] = useState('')
|
||||||
const [snackbarColor, setSnackbarColor] = useState('warning')
|
const [snackbarColor, setSnackbarColor] = useState('warning')
|
||||||
|
const [addLabelModalOpen, setAddLabelModalOpen] = useState(false)
|
||||||
|
const { data: userLabels, isLoading: isUserLabelsLoading } = useLabels()
|
||||||
|
|
||||||
const Navigate = useNavigate()
|
const Navigate = useNavigate()
|
||||||
|
|
||||||
const HandleValidateChore = () => {
|
const HandleValidateChore = () => {
|
||||||
|
@ -172,7 +180,8 @@ const ChoreEdit = () => {
|
||||||
isRolling: isRolling,
|
isRolling: isRolling,
|
||||||
isActive: isActive,
|
isActive: isActive,
|
||||||
notification: isNotificable,
|
notification: isNotificable,
|
||||||
labels: labels,
|
labels: labels.map(l => l.name),
|
||||||
|
labelsV2: labelsV2,
|
||||||
notificationMetadata: notificationMetadata,
|
notificationMetadata: notificationMetadata,
|
||||||
thingTrigger: thingTrigger,
|
thingTrigger: thingTrigger,
|
||||||
}
|
}
|
||||||
|
@ -226,8 +235,9 @@ const ChoreEdit = () => {
|
||||||
setFrequency(data.res.frequency)
|
setFrequency(data.res.frequency)
|
||||||
|
|
||||||
setNotificationMetadata(JSON.parse(data.res.notificationMetadata))
|
setNotificationMetadata(JSON.parse(data.res.notificationMetadata))
|
||||||
setLabels(data.res.labels ? data.res.labels.split(',') : [])
|
// setLabels(data.res.labels ? data.res.labels.split(',') : [])
|
||||||
|
|
||||||
|
setLabelsV2(data.res.labelsV2)
|
||||||
setAssignStrategy(
|
setAssignStrategy(
|
||||||
data.res.assignStrategy
|
data.res.assignStrategy
|
||||||
? data.res.assignStrategy
|
? data.res.assignStrategy
|
||||||
|
@ -275,6 +285,14 @@ const ChoreEdit = () => {
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (userLabels && userLabels.length == 0 && labelsV2.length == 0) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// const labelIds = labelsV2.map(l => l.id)
|
||||||
|
// setLabelsV2(userLabels.filter(l => labelIds.indexOf(l.id) > -1))
|
||||||
|
// }, [userLabels, labelsV2])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if frequancy type change to somthing need a due date then set it to the current date:
|
// if frequancy type change to somthing need a due date then set it to the current date:
|
||||||
if (!NO_DUE_DATE_REQUIRED_TYPE.includes(frequencyType) && !dueDate) {
|
if (!NO_DUE_DATE_REQUIRED_TYPE.includes(frequencyType) && !dueDate) {
|
||||||
|
@ -329,6 +347,7 @@ const ChoreEdit = () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth='md'>
|
<Container maxWidth='md'>
|
||||||
{/* <Typography level='h3' mb={1.5}>
|
{/* <Typography level='h3' mb={1.5}>
|
||||||
|
@ -726,8 +745,9 @@ const ChoreEdit = () => {
|
||||||
<Typography level='h5'>
|
<Typography level='h5'>
|
||||||
Things to remember about this chore or to tag it
|
Things to remember about this chore or to tag it
|
||||||
</Typography>
|
</Typography>
|
||||||
<FreeSoloCreateOption
|
{/* <FreeSoloCreateOption
|
||||||
options={labels}
|
options={[...labels, 'test']}
|
||||||
|
selected={labels}
|
||||||
onSelectChange={changes => {
|
onSelectChange={changes => {
|
||||||
const newLabels = []
|
const newLabels = []
|
||||||
changes.map(change => {
|
changes.map(change => {
|
||||||
|
@ -741,7 +761,99 @@ const ChoreEdit = () => {
|
||||||
})
|
})
|
||||||
setLabels(newLabels)
|
setLabels(newLabels)
|
||||||
}}
|
}}
|
||||||
|
/> */}
|
||||||
|
<Select
|
||||||
|
multiple
|
||||||
|
onChange={(event, newValue) => {
|
||||||
|
setLabelsV2(userLabels.filter(l => newValue.indexOf(l.name) > -1))
|
||||||
|
}}
|
||||||
|
value={labelsV2.map(l => l.name)}
|
||||||
|
renderValue={selected => (
|
||||||
|
<Box sx={{ display: 'flex', gap: '0.25rem' }}>
|
||||||
|
{labelsV2.map(selectedOption => {
|
||||||
|
return (
|
||||||
|
<Chip
|
||||||
|
variant='soft'
|
||||||
|
color='primary'
|
||||||
|
key={selectedOption.id}
|
||||||
|
size='lg'
|
||||||
|
sx={{
|
||||||
|
background: selectedOption.color,
|
||||||
|
color: getTextColorFromBackgroundColor(
|
||||||
|
selectedOption.color,
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedOption.name}
|
||||||
|
</Chip>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
sx={{ minWidth: '15rem' }}
|
||||||
|
slotProps={{
|
||||||
|
listbox: {
|
||||||
|
sx: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{userLabels &&
|
||||||
|
userLabels
|
||||||
|
// .map(l => l.name)
|
||||||
|
.map(label => (
|
||||||
|
<Option key={label.id + label.name} value={label.name}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '20 px',
|
||||||
|
height: '20 px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: label.color,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
{label.name}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
<MenuItem
|
||||||
|
key={'addNewLabel'}
|
||||||
|
value={' New Label'}
|
||||||
|
onClick={() => {
|
||||||
|
setAddLabelModalOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Add />
|
||||||
|
Add New Label
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
{/* <Card>
|
||||||
|
<List
|
||||||
|
orientation='horizontal'
|
||||||
|
wrap
|
||||||
|
sx={{
|
||||||
|
'--List-gap': '8px',
|
||||||
|
'--ListItem-radius': '20px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{labels?.map((label, index) => (
|
||||||
|
<ListItem key={label}>
|
||||||
|
<Chip
|
||||||
|
onClick={() => {
|
||||||
|
setLabels(labels.filter(l => l !== label))
|
||||||
|
}}
|
||||||
|
checked={true}
|
||||||
|
overlay
|
||||||
|
variant='soft'
|
||||||
|
color='primary'
|
||||||
|
size='lg'
|
||||||
|
endDecorator={<Cancel />}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Chip>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Card> */}
|
||||||
</Box>
|
</Box>
|
||||||
{choreId > 0 && (
|
{choreId > 0 && (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
||||||
|
@ -822,6 +934,16 @@ const ChoreEdit = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
<ConfirmationModal config={confirmModelConfig} />
|
<ConfirmationModal config={confirmModelConfig} />
|
||||||
|
{addLabelModalOpen && (
|
||||||
|
<LabelModal
|
||||||
|
isOpen={addLabelModalOpen}
|
||||||
|
onSave={label => {
|
||||||
|
setLabels([...labels, label])
|
||||||
|
setAddLabelModalOpen(false)
|
||||||
|
}}
|
||||||
|
onClose={() => setAddLabelModalOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/* <ChoreHistory ChoreHistory={choresHistory} UsersData={performers} /> */}
|
{/* <ChoreHistory ChoreHistory={choresHistory} UsersData={performers} /> */}
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={isSnackbarOpen}
|
open={isSnackbarOpen}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import {
|
||||||
SkipChore,
|
SkipChore,
|
||||||
UpdateChorePriority,
|
UpdateChorePriority,
|
||||||
} from '../../utils/Fetcher'
|
} from '../../utils/Fetcher'
|
||||||
|
import { getTextColorFromBackgroundColor } from '../../utils/LabelColors'
|
||||||
import Priorities from '../../utils/Priorities'
|
import Priorities from '../../utils/Priorities'
|
||||||
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
||||||
const IconCard = styled('div')({
|
const IconCard = styled('div')({
|
||||||
|
@ -264,6 +265,22 @@ const ChoreView = () => {
|
||||||
? `Due at ${moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A')}`
|
? `Due at ${moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A')}`
|
||||||
: 'N/A'}
|
: 'N/A'}
|
||||||
</Chip>
|
</Chip>
|
||||||
|
{/* show each label : */}
|
||||||
|
{chore?.labelsV2?.map((label, index) => (
|
||||||
|
<Chip
|
||||||
|
key={index}
|
||||||
|
sx={{
|
||||||
|
position: 'relative',
|
||||||
|
ml: index === 0 ? 0 : 0.5,
|
||||||
|
top: 2,
|
||||||
|
zIndex: 1,
|
||||||
|
backgroundColor: label?.color,
|
||||||
|
color: getTextColorFromBackgroundColor(label?.color),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label?.name}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Sheet
|
<Sheet
|
||||||
|
|
|
@ -47,6 +47,7 @@ import {
|
||||||
SkipChore,
|
SkipChore,
|
||||||
UpdateChoreAssignee,
|
UpdateChoreAssignee,
|
||||||
} from '../../utils/Fetcher'
|
} from '../../utils/Fetcher'
|
||||||
|
import { getTextColorFromBackgroundColor } from '../../utils/LabelColors'
|
||||||
import { Fetch } from '../../utils/TokenManager'
|
import { Fetch } from '../../utils/TokenManager'
|
||||||
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
||||||
import DateModal from '../Modals/Inputs/DateModal'
|
import DateModal from '../Modals/Inputs/DateModal'
|
||||||
|
@ -58,6 +59,7 @@ const ChoreCard = ({
|
||||||
performers,
|
performers,
|
||||||
onChoreUpdate,
|
onChoreUpdate,
|
||||||
onChoreRemove,
|
onChoreRemove,
|
||||||
|
userLabels,
|
||||||
sx,
|
sx,
|
||||||
viewOnly,
|
viewOnly,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -407,7 +409,7 @@ const ChoreCard = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box key={chore.id + '-box'}>
|
||||||
<Chip
|
<Chip
|
||||||
variant='soft'
|
variant='soft'
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -455,6 +457,7 @@ const ChoreCard = ({
|
||||||
// backgroundColor: 'white',
|
// backgroundColor: 'white',
|
||||||
boxShadow: 'sm',
|
boxShadow: 'sm',
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
|
key: `${chore.id}-card`,
|
||||||
|
|
||||||
// mb: 2,
|
// mb: 2,
|
||||||
}}
|
}}
|
||||||
|
@ -485,7 +488,7 @@ const ChoreCard = ({
|
||||||
</Chip>
|
</Chip>
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Box>
|
<Box key={`${chore.id}-labels`}>
|
||||||
{chore.priority > 0 && (
|
{chore.priority > 0 && (
|
||||||
<Chip
|
<Chip
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -505,22 +508,27 @@ const ChoreCard = ({
|
||||||
P{chore.priority}
|
P{chore.priority}
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
{chore.labels?.split(',').map((label, index) => (
|
{chore.labelsV2?.map((l, index) => {
|
||||||
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
variant='solid'
|
variant='solid'
|
||||||
key={label}
|
key={l.id}
|
||||||
color='primary'
|
color='primary'
|
||||||
sx={{
|
sx={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
ml: index === 0 ? 0 : 0.5,
|
ml: index === 0 ? 0 : 0.5,
|
||||||
top: 2,
|
top: 2,
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
|
backgroundColor: l?.color,
|
||||||
|
color: getTextColorFromBackgroundColor(l?.color),
|
||||||
}}
|
}}
|
||||||
startDecorator={getIconForLabel(label)}
|
|
||||||
|
// startDecorator={getIconForLabel(label)}
|
||||||
>
|
>
|
||||||
{label}
|
{l?.name}
|
||||||
</Chip>
|
</Chip>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -757,7 +765,7 @@ const ChoreCard = ({
|
||||||
</Typography>
|
</Typography>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
</Card>
|
</Card>
|
||||||
</>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,10 @@ import Fuse from 'fuse.js'
|
||||||
import { useContext, useEffect, useRef, useState } from 'react'
|
import { useContext, useEffect, useRef, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { UserContext } from '../../contexts/UserContext'
|
import { UserContext } from '../../contexts/UserContext'
|
||||||
import { GetAllUsers, GetChores, GetUserProfile } from '../../utils/Fetcher'
|
import { useChores } from '../../queries/ChoreQueries'
|
||||||
|
import { GetAllUsers, GetUserProfile } from '../../utils/Fetcher'
|
||||||
import LoadingComponent from '../components/Loading'
|
import LoadingComponent from '../components/Loading'
|
||||||
|
import { useLabels } from '../Labels/LabelQueries'
|
||||||
import ChoreCard from './ChoreCard'
|
import ChoreCard from './ChoreCard'
|
||||||
|
|
||||||
const MyChores = () => {
|
const MyChores = () => {
|
||||||
|
@ -38,6 +40,8 @@ const MyChores = () => {
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
const menuRef = useRef(null)
|
const menuRef = useRef(null)
|
||||||
const Navigate = useNavigate()
|
const Navigate = useNavigate()
|
||||||
|
const { data: userLabels, isLoading: userLabelsLoading } = useLabels()
|
||||||
|
const { data: choresData, isLoading: choresLoading } = useChores()
|
||||||
const choreSorter = (a, b) => {
|
const choreSorter = (a, b) => {
|
||||||
// 1. Handle null due dates (always last):
|
// 1. Handle null due dates (always last):
|
||||||
if (!a.nextDueDate && !b.nextDueDate) return 0 // Both null, no order
|
if (!a.nextDueDate && !b.nextDueDate) return 0 // Both null, no order
|
||||||
|
@ -74,14 +78,6 @@ const MyChores = () => {
|
||||||
setUserProfile(data.res)
|
setUserProfile(data.res)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
GetChores()
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
data.res.sort(choreSorter)
|
|
||||||
setChores(data.res)
|
|
||||||
|
|
||||||
setFilteredChores(data.res)
|
|
||||||
})
|
|
||||||
|
|
||||||
GetAllUsers()
|
GetAllUsers()
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
|
@ -94,6 +90,15 @@ const MyChores = () => {
|
||||||
setActiveUserId(currentUser.id)
|
setActiveUserId(currentUser.id)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (choresData) {
|
||||||
|
const sortedChores = choresData.res.sort(choreSorter)
|
||||||
|
setChores(sortedChores)
|
||||||
|
setFilteredChores(sortedChores)
|
||||||
|
}
|
||||||
|
}, [choresData, choresLoading])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('mousedown', handleMenuOutsideClick)
|
document.addEventListener('mousedown', handleMenuOutsideClick)
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -160,12 +165,21 @@ const MyChores = () => {
|
||||||
|
|
||||||
const searchOptions = {
|
const searchOptions = {
|
||||||
// keys to search in
|
// keys to search in
|
||||||
keys: ['name', 'labels'],
|
keys: ['name', 'raw_label'],
|
||||||
includeScore: true, // Optional: if you want to see how well each result matched the search term
|
includeScore: true, // Optional: if you want to see how well each result matched the search term
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
findAllMatches: true,
|
findAllMatches: true,
|
||||||
}
|
}
|
||||||
const fuse = new Fuse(chores, searchOptions)
|
|
||||||
|
const fuse = new Fuse(
|
||||||
|
chores.map(c => ({
|
||||||
|
...c,
|
||||||
|
raw_label: c.labelsV2
|
||||||
|
.map(l => userLabels.find(x => (x.id = l.id)).name)
|
||||||
|
.join(' '),
|
||||||
|
})),
|
||||||
|
searchOptions,
|
||||||
|
)
|
||||||
|
|
||||||
const handleSearchChange = e => {
|
const handleSearchChange = e => {
|
||||||
const search = e.target.value
|
const search = e.target.value
|
||||||
|
@ -180,7 +194,12 @@ const MyChores = () => {
|
||||||
setFilteredChores(fuse.search(term).map(result => result.item))
|
setFilteredChores(fuse.search(term).map(result => result.item))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userProfile === null) {
|
if (
|
||||||
|
userProfile === null ||
|
||||||
|
userLabelsLoading ||
|
||||||
|
performers.length === 0 ||
|
||||||
|
choresLoading
|
||||||
|
) {
|
||||||
return <LoadingComponent />
|
return <LoadingComponent />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,6 +345,7 @@ const MyChores = () => {
|
||||||
onChoreUpdate={handleChoreUpdated}
|
onChoreUpdate={handleChoreUpdated}
|
||||||
onChoreRemove={handleChoreDeleted}
|
onChoreRemove={handleChoreDeleted}
|
||||||
performers={performers}
|
performers={performers}
|
||||||
|
userLabels={userLabels}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|
8
src/views/Labels/LabelQueries.jsx
Normal file
8
src/views/Labels/LabelQueries.jsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { useQuery } from 'react-query'
|
||||||
|
import { GetLabels } from '../../utils/Fetcher'
|
||||||
|
|
||||||
|
export const useLabels = () => {
|
||||||
|
return useQuery('labels', GetLabels, {
|
||||||
|
initialData: [],
|
||||||
|
})
|
||||||
|
}
|
178
src/views/Labels/LabelView.jsx
Normal file
178
src/views/Labels/LabelView.jsx
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
import DeleteIcon from '@mui/icons-material/Delete'
|
||||||
|
import EditIcon from '@mui/icons-material/Edit'
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
CircularProgress,
|
||||||
|
Container,
|
||||||
|
IconButton,
|
||||||
|
Table,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/joy'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import LabelModal from '../Modals/Inputs/LabelModal'
|
||||||
|
import { useLabels } from './LabelQueries'
|
||||||
|
|
||||||
|
// import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||||
|
import { Add } from '@mui/icons-material'
|
||||||
|
import { useQueryClient } from 'react-query'
|
||||||
|
import { DeleteLabel } from '../../utils/Fetcher'
|
||||||
|
|
||||||
|
const LabelView = () => {
|
||||||
|
const { data: labels, isLabelsLoading, isError } = useLabels()
|
||||||
|
const [userLabels, setUserLabels] = useState([labels])
|
||||||
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
|
const [currentLabel, setCurrentLabel] = useState(null) // Label being edited or null for new label
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const handleAddLabel = () => {
|
||||||
|
setCurrentLabel(null) // Adding a new label
|
||||||
|
setModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEditLabel = label => {
|
||||||
|
setCurrentLabel(label) // Editing an existing label
|
||||||
|
setModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteLabel = id => {
|
||||||
|
DeleteLabel(id).then(res => {
|
||||||
|
// Invalidate and refetch labels after deleting a label
|
||||||
|
const updatedLabels = userLabels.filter(label => label.id !== id)
|
||||||
|
setUserLabels(updatedLabels)
|
||||||
|
|
||||||
|
queryClient.invalidateQueries('labels')
|
||||||
|
})
|
||||||
|
// Implement deletion logic here
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSaveLabel = newOrUpdatedLabel => {
|
||||||
|
queryClient.invalidateQueries('labels')
|
||||||
|
setModalOpen(false)
|
||||||
|
const updatedLabels = userLabels.map(label =>
|
||||||
|
label.id === newOrUpdatedLabel.id ? newOrUpdatedLabel : label,
|
||||||
|
)
|
||||||
|
setUserLabels(updatedLabels)
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
if (labels) {
|
||||||
|
setUserLabels(labels)
|
||||||
|
}
|
||||||
|
}, [labels])
|
||||||
|
|
||||||
|
if (isLabelsLoading) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
display='flex'
|
||||||
|
justifyContent='center'
|
||||||
|
alignItems='center'
|
||||||
|
height='100vh'
|
||||||
|
>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Typography color='danger' textAlign='center'>
|
||||||
|
Failed to load labels. Please try again.
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container maxWidth='md'>
|
||||||
|
<Table aria-label='Manage Labels' stickyHeader hoverRow>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{ textAlign: 'center' }}>Label</th>
|
||||||
|
<th style={{ textAlign: 'center' }}>Color</th>
|
||||||
|
<th style={{ textAlign: 'center' }}>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{userLabels.map(label => (
|
||||||
|
<tr key={label.id}>
|
||||||
|
<td>{label.name}</td>
|
||||||
|
<td
|
||||||
|
style={{
|
||||||
|
// center without display flex:
|
||||||
|
textAlign: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
borderRadius='50%'
|
||||||
|
sx={{
|
||||||
|
backgroundColor: label.color,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton onClick={() => handleEditLabel(label)}>
|
||||||
|
<EditIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => handleDeleteLabel(label.id)}
|
||||||
|
color='danger'
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
{userLabels.length === 0 && (
|
||||||
|
<Typography textAlign='center' mt={2}>
|
||||||
|
No labels available. Add a new label to get started.
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{modalOpen && (
|
||||||
|
<LabelModal
|
||||||
|
isOpen={modalOpen}
|
||||||
|
onClose={() => setModalOpen(false)}
|
||||||
|
onSave={handleSaveLabel}
|
||||||
|
label={currentLabel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: 0,
|
||||||
|
left: 10,
|
||||||
|
p: 2, // padding
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
gap: 2,
|
||||||
|
'z-index': 1000,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
color='primary'
|
||||||
|
variant='solid'
|
||||||
|
sx={{
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
}}
|
||||||
|
onClick={handleAddLabel}
|
||||||
|
>
|
||||||
|
<Add />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LabelView
|
0
src/views/Labels/Labels.jsx
Normal file
0
src/views/Labels/Labels.jsx
Normal file
175
src/views/Modals/Inputs/LabelModal.jsx
Normal file
175
src/views/Modals/Inputs/LabelModal.jsx
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalDialog,
|
||||||
|
Option,
|
||||||
|
Select,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/joy'
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { useQueryClient } from 'react-query'
|
||||||
|
import { CreateLabel, UpdateLabel } from '../../../utils/Fetcher'
|
||||||
|
import LABEL_COLORS from '../../../utils/LabelColors'
|
||||||
|
import { useLabels } from '../../Labels/LabelQueries'
|
||||||
|
|
||||||
|
function LabelModal({ isOpen, onClose, onSave, label }) {
|
||||||
|
const [labelName, setLabelName] = React.useState('')
|
||||||
|
const [color, setColor] = React.useState('')
|
||||||
|
const [error, setError] = React.useState('')
|
||||||
|
const { data: userLabels, isLoadingLabels } = useLabels()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
// Populate the form fields when editing
|
||||||
|
useEffect(() => {
|
||||||
|
if (label) {
|
||||||
|
setLabelName(label.name)
|
||||||
|
setColor(label.color)
|
||||||
|
} else {
|
||||||
|
setLabelName('')
|
||||||
|
setColor('')
|
||||||
|
}
|
||||||
|
setError('')
|
||||||
|
}, [label])
|
||||||
|
|
||||||
|
const validateLabel = () => {
|
||||||
|
if (!labelName || labelName.trim() === '') {
|
||||||
|
setError('Name cannot be empty')
|
||||||
|
return false
|
||||||
|
} else if (
|
||||||
|
!label ||
|
||||||
|
userLabels.some(
|
||||||
|
userLabel => userLabel.name === labelName && userLabel.id !== label.id,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
setError('Label with this name already exists')
|
||||||
|
return false
|
||||||
|
} else if (color === '') {
|
||||||
|
setError('Please select a color')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
if (!validateLabel()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveAction = label
|
||||||
|
? UpdateLabel({ id: label.id, name: labelName, color })
|
||||||
|
: CreateLabel({ name: labelName, color })
|
||||||
|
|
||||||
|
saveAction.then(res => {
|
||||||
|
if (res.error) {
|
||||||
|
console.log(res.error)
|
||||||
|
setError('Failed to save label. Please try again.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queryClient.invalidateQueries('labels').then(() => {
|
||||||
|
onSave({ id: label?.id, name: labelName, color })
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={isOpen} onClose={onClose}>
|
||||||
|
<ModalDialog>
|
||||||
|
<Typography level='title-md' mb={1}>
|
||||||
|
{label ? 'Edit Label' : 'Add Label'}
|
||||||
|
</Typography>
|
||||||
|
<FormControl>
|
||||||
|
<Typography level='body-sm' alignSelf={'start'}>
|
||||||
|
Name
|
||||||
|
</Typography>
|
||||||
|
<Input
|
||||||
|
margin='normal'
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
name='labelName'
|
||||||
|
type='text'
|
||||||
|
id='labelName'
|
||||||
|
value={labelName}
|
||||||
|
onChange={e => setLabelName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{/* Color Selection */}
|
||||||
|
<FormControl>
|
||||||
|
<Typography level='body-sm' alignSelf={'start'}>
|
||||||
|
Color:
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
label='Color'
|
||||||
|
value={color}
|
||||||
|
renderValue={selected => (
|
||||||
|
<Typography
|
||||||
|
key={selected.value}
|
||||||
|
startDecorator={
|
||||||
|
<Box
|
||||||
|
className='h-4 w-4'
|
||||||
|
borderRadius={10}
|
||||||
|
sx={{
|
||||||
|
background: selected.value,
|
||||||
|
shadow: { xs: 1 },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{selected.label}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
onChange={(e, value) => {
|
||||||
|
value && setColor(value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{LABEL_COLORS.map(val => (
|
||||||
|
<Option key={val.value} value={val.value}>
|
||||||
|
<Box className='flex items-center justify-between'>
|
||||||
|
<Box
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
borderRadius={10}
|
||||||
|
sx={{
|
||||||
|
background: val.value,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
ml: 1,
|
||||||
|
color: 'text.secondary',
|
||||||
|
}}
|
||||||
|
variant='caption'
|
||||||
|
>
|
||||||
|
{val.name}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
{error && (
|
||||||
|
<Typography color='warning' level='body-sm'>
|
||||||
|
{error}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
|
||||||
|
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
|
||||||
|
{label ? 'Save Changes' : 'Add Label'}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={onClose} variant='outlined'>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</ModalDialog>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LabelModal
|
|
@ -7,14 +7,18 @@ import * as React from 'react'
|
||||||
|
|
||||||
const filter = createFilterOptions()
|
const filter = createFilterOptions()
|
||||||
|
|
||||||
export default function FreeSoloCreateOption({ options, onSelectChange }) {
|
export default function FreeSoloCreateOption({
|
||||||
|
options,
|
||||||
|
onSelectChange,
|
||||||
|
selected,
|
||||||
|
}) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setValue(options)
|
setValue(options)
|
||||||
}, [options])
|
}, [options])
|
||||||
|
|
||||||
const [value, setValue] = React.useState([])
|
const [value, setValue] = React.useState([selected])
|
||||||
const [selectOptions, setSelectOptions] = React.useState(
|
const [selectOptions, setSelectOptions] = React.useState(
|
||||||
options ? options : [],
|
selected ? selected : [],
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<FormControl id='free-solo-with-text-demo'>
|
<FormControl id='free-solo-with-text-demo'>
|
||||||
|
@ -38,26 +42,27 @@ export default function FreeSoloCreateOption({ options, onSelectChange }) {
|
||||||
}
|
}
|
||||||
onSelectChange(newValue)
|
onSelectChange(newValue)
|
||||||
}}
|
}}
|
||||||
filterOptions={(options, params) => {
|
filterOptions={(selected, params) => {
|
||||||
const filtered = filter(options, params)
|
const filtered = filter(selected, params)
|
||||||
|
|
||||||
const { inputValue } = params
|
const { inputValue } = params
|
||||||
// Suggest the creation of a new value
|
// Suggest the creation of a new value
|
||||||
const isExisting = options.some(option => inputValue === option.title)
|
const isExisting = selected.some(
|
||||||
|
option => inputValue === option.title,
|
||||||
|
)
|
||||||
if (inputValue !== '' && !isExisting) {
|
if (inputValue !== '' && !isExisting) {
|
||||||
filtered.push({
|
filtered.push({
|
||||||
inputValue,
|
inputValue,
|
||||||
title: `Add "${inputValue}"`,
|
title: `Add "${inputValue}"`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return filtered
|
return filtered
|
||||||
}}
|
}}
|
||||||
selectOnFocus
|
selectOnFocus
|
||||||
clearOnBlur
|
clearOnBlur
|
||||||
handleHomeEndKeys
|
handleHomeEndKeys
|
||||||
// freeSolo
|
// freeSolo
|
||||||
options={selectOptions}
|
options={options}
|
||||||
getOptionLabel={option => {
|
getOptionLabel={option => {
|
||||||
// Value selected with enter, right from the input
|
// Value selected with enter, right from the input
|
||||||
if (typeof option === 'string') {
|
if (typeof option === 'string') {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Logo from '@/assets/logo.svg'
|
||||||
import {
|
import {
|
||||||
AccountBox,
|
AccountBox,
|
||||||
HomeOutlined,
|
HomeOutlined,
|
||||||
ListAltRounded,
|
ListAlt,
|
||||||
Logout,
|
Logout,
|
||||||
MenuRounded,
|
MenuRounded,
|
||||||
Message,
|
Message,
|
||||||
|
@ -30,6 +30,7 @@ const links = [
|
||||||
label: 'Home',
|
label: 'Home',
|
||||||
icon: <HomeOutlined />,
|
icon: <HomeOutlined />,
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// to: '/chores',
|
// to: '/chores',
|
||||||
// label: 'Desktop View',
|
// label: 'Desktop View',
|
||||||
|
@ -40,6 +41,11 @@ const links = [
|
||||||
label: 'Things',
|
label: 'Things',
|
||||||
icon: <Widgets />,
|
icon: <Widgets />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
to: 'labels',
|
||||||
|
label: 'Labels',
|
||||||
|
icon: <ListAlt />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
to: '/settings#sharing',
|
to: '/settings#sharing',
|
||||||
label: 'Sharing',
|
label: 'Sharing',
|
||||||
|
|
Loading…
Add table
Reference in a new issue