Support Sortinging in My Chore by Date
Show API Token and allow manual copy Show Error when sign up disabled Support Pushover
This commit is contained in:
commit
4fd959b277
7 changed files with 389 additions and 104 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "donetick",
|
"name": "donetick",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.80",
|
"version": "0.1.82",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
|
|
|
@ -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 = () => {
|
const GetSubscriptionSession = () => {
|
||||||
return Fetch(API_URL + `/payments/create-subscription`, {
|
return Fetch(API_URL + `/payments/create-subscription`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -373,6 +381,7 @@ export {
|
||||||
UpdateChoreHistory,
|
UpdateChoreHistory,
|
||||||
UpdateChorePriority,
|
UpdateChorePriority,
|
||||||
UpdateLabel,
|
UpdateLabel,
|
||||||
|
UpdateNotificationTarget,
|
||||||
UpdatePassword,
|
UpdatePassword,
|
||||||
UpdateThingState,
|
UpdateThingState,
|
||||||
UpdateUserDetails,
|
UpdateUserDetails,
|
||||||
|
|
|
@ -103,12 +103,13 @@ const SignupView = () => {
|
||||||
signUp(username, password, displayName, email).then(response => {
|
signUp(username, password, displayName, email).then(response => {
|
||||||
if (response.status === 201) {
|
if (response.status === 201) {
|
||||||
handleLogin(username, password)
|
handleLogin(username, password)
|
||||||
|
} else if (response.status === 403) {
|
||||||
|
setError('Signup disabled, please contact admin')
|
||||||
} else {
|
} else {
|
||||||
console.log('Signup failed')
|
console.log('Signup failed')
|
||||||
response.json().then(res => {
|
response.json().then(res => {
|
||||||
setError(res.error)
|
setError(res.error)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,7 +466,6 @@ const ChoreCard = ({
|
||||||
>
|
>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid
|
<Grid
|
||||||
item
|
|
||||||
xs={9}
|
xs={9}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/chores/${chore.id}`)
|
navigate(`/chores/${chore.id}`)
|
||||||
|
@ -556,7 +555,6 @@ const ChoreCard = ({
|
||||||
</Box> */}
|
</Box> */}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
item
|
|
||||||
xs={3}
|
xs={3}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
|
@ -2,15 +2,20 @@ import {
|
||||||
Add,
|
Add,
|
||||||
CancelRounded,
|
CancelRounded,
|
||||||
EditCalendar,
|
EditCalendar,
|
||||||
|
ExpandCircleDown,
|
||||||
FilterAlt,
|
FilterAlt,
|
||||||
PriorityHigh,
|
PriorityHigh,
|
||||||
Style,
|
Style,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionGroup,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Chip,
|
Chip,
|
||||||
Container,
|
Container,
|
||||||
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
List,
|
List,
|
||||||
|
@ -38,6 +43,8 @@ const MyChores = () => {
|
||||||
const [chores, setChores] = useState([])
|
const [chores, setChores] = useState([])
|
||||||
const [filteredChores, setFilteredChores] = useState([])
|
const [filteredChores, setFilteredChores] = useState([])
|
||||||
const [selectedFilter, setSelectedFilter] = useState('All')
|
const [selectedFilter, setSelectedFilter] = useState('All')
|
||||||
|
const [choreSections, setChoreSections] = useState([])
|
||||||
|
const [openChoreSections, setOpenChoreSections] = useState({})
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [activeUserId, setActiveUserId] = useState(0)
|
const [activeUserId, setActiveUserId] = useState(0)
|
||||||
const [performers, setPerformers] = useState([])
|
const [performers, setPerformers] = useState([])
|
||||||
|
@ -74,6 +81,108 @@ const MyChores = () => {
|
||||||
return aDueDate - bDueDate // Sort ascending by due date
|
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(() => {
|
useEffect(() => {
|
||||||
if (userProfile === null) {
|
if (userProfile === null) {
|
||||||
GetUserProfile()
|
GetUserProfile()
|
||||||
|
@ -100,6 +209,14 @@ const MyChores = () => {
|
||||||
const sortedChores = choresData.res.sort(choreSorter)
|
const sortedChores = choresData.res.sort(choreSorter)
|
||||||
setChores(sortedChores)
|
setChores(sortedChores)
|
||||||
setFilteredChores(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])
|
}, [choresData, choresLoading])
|
||||||
|
|
||||||
|
@ -231,11 +348,6 @@ const MyChores = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth='md'>
|
<Container maxWidth='md'>
|
||||||
{/* <Typography level='h3' mb={1.5}>
|
|
||||||
My Chores
|
|
||||||
</Typography> */}
|
|
||||||
{/* <Sheet> */}
|
|
||||||
{/* Search box to filter */}
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -253,7 +365,6 @@ const MyChores = () => {
|
||||||
mt: 1,
|
mt: 1,
|
||||||
mb: 1,
|
mb: 1,
|
||||||
borderRadius: 24,
|
borderRadius: 24,
|
||||||
// border: '1px solid',
|
|
||||||
height: 24,
|
height: 24,
|
||||||
borderColor: 'text.disabled',
|
borderColor: 'text.disabled',
|
||||||
padding: 1,
|
padding: 1,
|
||||||
|
@ -271,7 +382,6 @@ const MyChores = () => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IconButtonWithMenu
|
<IconButtonWithMenu
|
||||||
key={'icon-menu-labels-filter'}
|
|
||||||
icon={<PriorityHigh />}
|
icon={<PriorityHigh />}
|
||||||
options={Priorities}
|
options={Priorities}
|
||||||
selectedItem={selectedFilter}
|
selectedItem={selectedFilter}
|
||||||
|
@ -282,7 +392,6 @@ const MyChores = () => {
|
||||||
isActive={selectedFilter.startsWith('Priority: ')}
|
isActive={selectedFilter.startsWith('Priority: ')}
|
||||||
/>
|
/>
|
||||||
<IconButtonWithMenu
|
<IconButtonWithMenu
|
||||||
key={'icon-menu-labels-filter'}
|
|
||||||
icon={<Style />}
|
icon={<Style />}
|
||||||
// TODO : this need simplification we want to display both user labels and chore labels
|
// TODO : this need simplification we want to display both user labels and chore labels
|
||||||
// that why we are merging them here.
|
// that why we are merging them here.
|
||||||
|
@ -395,7 +504,6 @@ const MyChores = () => {
|
||||||
Current Filter: {selectedFilter}
|
Current Filter: {selectedFilter}
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
{/* </Sheet> */}
|
|
||||||
{filteredChores.length === 0 && (
|
{filteredChores.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -419,7 +527,10 @@ const MyChores = () => {
|
||||||
{chores.length > 0 && (
|
{chores.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setFilteredChores(chores)}
|
onClick={() => {
|
||||||
|
setFilteredChores(chores)
|
||||||
|
setSearchTerm('')
|
||||||
|
}}
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
color='neutral'
|
color='neutral'
|
||||||
>
|
>
|
||||||
|
@ -429,8 +540,76 @@ const MyChores = () => {
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
{(searchTerm?.length > 0 || selectedFilter !== 'All') &&
|
||||||
{filteredChores.map(chore => (
|
filteredChores.map(chore => (
|
||||||
|
<ChoreCard
|
||||||
|
key={`filtered-${chore.id} `}
|
||||||
|
chore={chore}
|
||||||
|
onChoreUpdate={handleChoreUpdated}
|
||||||
|
onChoreRemove={handleChoreDeleted}
|
||||||
|
performers={performers}
|
||||||
|
userLabels={userLabels}
|
||||||
|
onChipClick={handleLabelFiltering}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{searchTerm.length === 0 && selectedFilter === 'All' && (
|
||||||
|
<AccordionGroup transition='0.2s ease' disableDivider>
|
||||||
|
{choreSections.map((section, index) => {
|
||||||
|
if (section.content.length === 0) return null
|
||||||
|
return (
|
||||||
|
<Accordion
|
||||||
|
title={section.name}
|
||||||
|
key={section.name + index}
|
||||||
|
sx={{
|
||||||
|
my: 0,
|
||||||
|
}}
|
||||||
|
expanded={Boolean(openChoreSections[index])}
|
||||||
|
>
|
||||||
|
<Divider orientation='horizontal'>
|
||||||
|
<Chip
|
||||||
|
variant='soft'
|
||||||
|
color='neutral'
|
||||||
|
size='md'
|
||||||
|
onClick={() => {
|
||||||
|
if (openChoreSections[index]) {
|
||||||
|
const newOpenChoreSections = { ...openChoreSections }
|
||||||
|
delete newOpenChoreSections[index]
|
||||||
|
setOpenChoreSections(newOpenChoreSections)
|
||||||
|
} else {
|
||||||
|
setOpenChoreSections({
|
||||||
|
...openChoreSections,
|
||||||
|
[index]: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
endDecorator={
|
||||||
|
openChoreSections[index] ? (
|
||||||
|
<ExpandCircleDown
|
||||||
|
color='primary'
|
||||||
|
sx={{ transform: 'rotate(180deg)' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ExpandCircleDown color='primary' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
startDecorator={
|
||||||
|
<>
|
||||||
|
<Chip color='primary' size='sm' variant='soft'>
|
||||||
|
{section?.content?.length}
|
||||||
|
</Chip>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{section.name}
|
||||||
|
</Chip>
|
||||||
|
</Divider>
|
||||||
|
<AccordionDetails
|
||||||
|
sx={{
|
||||||
|
flexDirection: 'column',
|
||||||
|
my: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{section.content?.map(chore => (
|
||||||
<ChoreCard
|
<ChoreCard
|
||||||
key={chore.id}
|
key={chore.id}
|
||||||
chore={chore}
|
chore={chore}
|
||||||
|
@ -441,7 +620,12 @@ const MyChores = () => {
|
||||||
onChipClick={handleLabelFiltering}
|
onChipClick={handleLabelFiltering}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</AccordionGroup>
|
||||||
|
)}
|
||||||
<Box
|
<Box
|
||||||
// variant='outlined'
|
// variant='outlined'
|
||||||
sx={{
|
sx={{
|
||||||
|
|
|
@ -1,4 +1,14 @@
|
||||||
import { Box, Button, Card, Chip, Divider, Typography } from '@mui/joy'
|
import { CopyAll } from '@mui/icons-material'
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Chip,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/joy'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useContext, useEffect, useState } from 'react'
|
import { useContext, useEffect, useState } from 'react'
|
||||||
import { UserContext } from '../../contexts/UserContext'
|
import { UserContext } from '../../contexts/UserContext'
|
||||||
|
@ -13,6 +23,7 @@ import TextModal from '../Modals/Inputs/TextModal'
|
||||||
const APITokenSettings = () => {
|
const APITokenSettings = () => {
|
||||||
const [tokens, setTokens] = useState([])
|
const [tokens, setTokens] = useState([])
|
||||||
const [isGetTokenNameModalOpen, setIsGetTokenNameModalOpen] = useState(false)
|
const [isGetTokenNameModalOpen, setIsGetTokenNameModalOpen] = useState(false)
|
||||||
|
const [showTokenId, setShowTokenId] = useState(null)
|
||||||
const { userProfile, setUserProfile } = useContext(UserContext)
|
const { userProfile, setUserProfile } = useContext(UserContext)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
GetLongLiveTokens().then(resp => {
|
GetLongLiveTokens().then(resp => {
|
||||||
|
@ -61,19 +72,21 @@ const APITokenSettings = () => {
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
{token.token && (
|
|
||||||
<Button
|
<Button
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
color='primary'
|
color='primary'
|
||||||
sx={{ mr: 1 }}
|
sx={{ mr: 1 }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigator.clipboard.writeText(token.token)
|
if (showTokenId === token.id) {
|
||||||
alert('Token copied to clipboard')
|
setShowTokenId(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowTokenId(token.id)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Copy Token
|
{showTokenId === token?.id ? 'Hide' : 'Show'} Token
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
|
@ -97,6 +110,28 @@ const APITokenSettings = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
{showTokenId === token?.id && (
|
||||||
|
<Box>
|
||||||
|
<Input
|
||||||
|
value={token.token}
|
||||||
|
sx={{ width: '100%', mt: 2 }}
|
||||||
|
readOnly
|
||||||
|
endDecorator={
|
||||||
|
<IconButton
|
||||||
|
variant='outlined'
|
||||||
|
color='primary'
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(token.token)
|
||||||
|
alert('Token copied to clipboard')
|
||||||
|
setShowTokenId(null)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CopyAll />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,77 @@
|
||||||
import { Button, Divider, Input, Option, Select, Typography } from '@mui/joy'
|
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 { UserContext } from '../../contexts/UserContext'
|
||||||
import { GetUserProfile, UpdateUserDetails } from '../../utils/Fetcher'
|
import { UpdateNotificationTarget } from '../../utils/Fetcher'
|
||||||
|
|
||||||
const NotificationSetting = () => {
|
const NotificationSetting = () => {
|
||||||
const { userProfile, setUserProfile } = useContext(UserContext)
|
const { userProfile, setUserProfile } = useContext(UserContext)
|
||||||
useEffect(() => {
|
const [notificationTarget, setNotificationTarget] = useState(
|
||||||
if (!userProfile) {
|
userProfile?.notification_target
|
||||||
GetUserProfile().then(resp => {
|
? String(userProfile.notification_target.type)
|
||||||
resp.json().then(data => {
|
: '0',
|
||||||
setUserProfile(data.res)
|
)
|
||||||
setChatID(data.res.chatID)
|
|
||||||
|
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),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [])
|
|
||||||
const [chatID, setChatID] = useState(userProfile?.chatID)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='grid gap-4 py-4' id='notifications'>
|
<div className='grid gap-4 py-4' id='notifications'>
|
||||||
<Typography level='h3'>Notification Settings</Typography>
|
<Typography level='h3'>Notification Settings</Typography>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Typography level='body-md'>Manage your notification settings</Typography>
|
<Typography level='body-md'>Manage your notification settings</Typography>
|
||||||
|
|
||||||
<Select defaultValue='telegram' sx={{ maxWidth: '200px' }} disabled>
|
<Select
|
||||||
<Option value='telegram'>Telegram</Option>
|
value={notificationTarget}
|
||||||
<Option value='discord'>Discord</Option>
|
sx={{ maxWidth: '200px' }}
|
||||||
|
onChange={(e, selected) => setNotificationTarget(selected)}
|
||||||
|
>
|
||||||
|
<Option value='0'>None</Option>
|
||||||
|
<Option value='1'>Telegram</Option>
|
||||||
|
<Option value='2'>Pushover</Option>
|
||||||
</Select>
|
</Select>
|
||||||
|
{notificationTarget === '1' && (
|
||||||
|
<>
|
||||||
<Typography level='body-xs'>
|
<Typography level='body-xs'>
|
||||||
You need to initiate a message to the bot in order for the Telegram
|
You need to initiate a message to the bot in order for the Telegram
|
||||||
notification to work{' '}
|
notification to work{' '}
|
||||||
|
@ -43,6 +87,8 @@ const NotificationSetting = () => {
|
||||||
to start a chat
|
to start a chat
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<Typography level='body-sm'>Chat ID</Typography>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
value={chatID}
|
value={chatID}
|
||||||
onChange={e => setChatID(e.target.value)}
|
onChange={e => setChatID(e.target.value)}
|
||||||
|
@ -52,8 +98,8 @@ const NotificationSetting = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography mt={0} level='body-xs'>
|
<Typography mt={0} level='body-xs'>
|
||||||
If you don't know your Chat ID, start chat with userinfobot and it will
|
If you don't know your Chat ID, start chat with userinfobot and it
|
||||||
send you your Chat ID.{' '}
|
will send you your Chat ID.{' '}
|
||||||
<a
|
<a
|
||||||
style={{
|
style={{
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
|
@ -65,21 +111,33 @@ const NotificationSetting = () => {
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
to start chat with userinfobot{' '}
|
to start chat with userinfobot{' '}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{notificationTarget === '2' && (
|
||||||
|
<>
|
||||||
|
<Typography level='body-sm'>User key</Typography>
|
||||||
|
<Input
|
||||||
|
value={chatID}
|
||||||
|
onChange={e => setChatID(e.target.value)}
|
||||||
|
placeholder='User ID'
|
||||||
|
sx={{
|
||||||
|
width: '200px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{error && (
|
||||||
|
<Typography color='warning' level='body-sm'>
|
||||||
|
{error}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
width: '110px',
|
width: '110px',
|
||||||
mb: 1,
|
mb: 1,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={handleSave}
|
||||||
UpdateUserDetails({
|
|
||||||
chatID: Number(chatID),
|
|
||||||
}).then(resp => {
|
|
||||||
resp.json().then(data => {
|
|
||||||
setUserProfile(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
|
|
Loading…
Add table
Reference in a new issue