aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.env3
-rw-r--r--package-lock.json50
-rw-r--r--package.json3
-rw-r--r--src/App.jsx10
-rw-r--r--src/utils/Fetcher.jsx10
-rw-r--r--src/views/Authorization/Signup.jsx17
-rw-r--r--src/views/ChoreEdit/ChoreView.jsx62
-rw-r--r--src/views/Chores/ChoreCard.jsx49
-rw-r--r--src/views/Chores/MyChores.jsx169
-rw-r--r--src/views/Landing/CookiePermissionSnackbar.jsx40
-rw-r--r--src/views/Landing/FeaturesSection.jsx12
-rw-r--r--src/views/Landing/Footer.jsx30
-rw-r--r--src/views/Landing/GettingStarted.jsx171
-rw-r--r--src/views/Landing/HomeHero.jsx13
-rw-r--r--src/views/Landing/Landing.jsx6
-rw-r--r--src/views/Modals/Inputs/SelectModal.jsx4
-rw-r--r--src/views/components/NavBar.jsx21
-rw-r--r--vite.config.js9
18 files changed, 506 insertions, 173 deletions
diff --git a/.env b/.env
index b7e8b90..29ce2e6 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,3 @@
VITE_APP_API_URL=http://localhost:2021
-VITE_IS_LANDING_DEFAULT=false \ No newline at end of file
+VITE_IS_LANDING_DEFAULT=false
+VITE_OPENREPLAY_PROJECT_KEY=
diff --git a/package-lock.json b/package-lock.json
index 21700ff..efde8e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,19 @@
{
- "name": "fe-template",
- "version": "0.1.61",
+ "name": "donetick",
+ "version": "0.1.72",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "fe-template",
- "version": "0.1.61",
+ "name": "donetick",
+ "version": "0.1.72",
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.2",
"@mui/joy": "^5.0.0-beta.20",
"@mui/material": "^5.15.2",
+ "@openreplay/tracker": "^14.0.4",
"@tanstack/react-query": "^5.17.0",
"aos": "^2.3.4",
"dotenv": "^16.4.5",
@@ -2481,6 +2482,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@medv/finder": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@medv/finder/-/finder-3.2.0.tgz",
+ "integrity": "sha512-JmU7JIBwyL8RAzefvzALT4sP2M0biGk8i2invAgpQmma/QgfsaqoHIvJ7S0YC8n9hUVG8X3Leul2nGa06PvhbQ==",
+ "license": "MIT"
+ },
"node_modules/@mui/base": {
"version": "5.0.0-beta.29",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.29.tgz",
@@ -2839,6 +2846,20 @@
"node": ">= 8"
}
},
+ "node_modules/@openreplay/tracker": {
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@openreplay/tracker/-/tracker-14.0.4.tgz",
+ "integrity": "sha512-Mz+MPw9EYbH6tpZ3d1u2yLJsY+MaoBmJX0gQt4HtvGlwJnd7xJvF37xHY8/e3N1Tc7zENsrf7xcpiZXabNKoVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@medv/finder": "^3.2.0",
+ "error-stack-parser": "^2.0.6",
+ "fflate": "^0.8.2"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -4747,6 +4768,15 @@
"is-arrayish": "^0.2.1"
}
},
+ "node_modules/error-stack-parser": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
+ "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "stackframe": "^1.3.4"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.22.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
@@ -5327,6 +5357,12 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "license": "MIT"
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -8064,6 +8100,12 @@
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
},
+ "node_modules/stackframe": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
+ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
+ "license": "MIT"
+ },
"node_modules/streamx": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz",
diff --git a/package.json b/package.json
index 83fd033..ea12bfe 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "donetick",
"private": true,
- "version": "0.1.65",
+ "version": "0.1.74",
"type": "module",
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
@@ -25,6 +25,7 @@
"@mui/icons-material": "^5.15.2",
"@mui/joy": "^5.0.0-beta.20",
"@mui/material": "^5.15.2",
+ "@openreplay/tracker": "^14.0.4",
"@tanstack/react-query": "^5.17.0",
"aos": "^2.3.4",
"dotenv": "^16.4.5",
diff --git a/src/App.jsx b/src/App.jsx
index e436ecc..de8a3dd 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,5 +1,6 @@
import NavBar from '@/views/components/NavBar'
import { Button, Snackbar, Typography, useColorScheme } from '@mui/joy'
+import Tracker from '@openreplay/tracker'
import { useEffect, useState } from 'react'
import { Outlet } from 'react-router-dom'
import { useRegisterSW } from 'virtual:pwa-register/react'
@@ -19,6 +20,8 @@ const remove = className => {
const intervalMS = 5 * 60 * 1000 // 5 minutes
function App() {
+ startOpenReplay()
+
const { mode, systemMode } = useColorScheme()
const [userProfile, setUserProfile] = useState(null)
const [showUpdateSnackbar, setShowUpdateSnackbar] = useState(true)
@@ -115,4 +118,11 @@ function App() {
)
}
+const startOpenReplay = () => {
+ if (!import.meta.env.VITE_OPENREPLAY_PROJECT_KEY) return
+ const tracker = new Tracker({
+ projectKey: import.meta.env.VITE_OPENREPLAY_PROJECT_KEY,
+ })
+ tracker.start()
+}
export default App
diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx
index 17b6cd9..5b3bbd9 100644
--- a/src/utils/Fetcher.jsx
+++ b/src/utils/Fetcher.jsx
@@ -83,6 +83,15 @@ const SkipChore = id => {
body: JSON.stringify({}),
})
}
+
+const UpdateChoreAssignee = (id, assignee) => {
+ return Fetch(`${API_URL}/chores/${id}/assignee`, {
+ method: 'PUT',
+ headers: HEADERS(),
+ body: JSON.stringify({ assignee:Number(assignee) }),
+ })
+}
+
const CreateChore = chore => {
return Fetch(`${API_URL}/chores/`, {
method: 'POST',
@@ -306,4 +315,5 @@ export {
UpdateChoreHistory,
UpdateThingState,
UpdateUserDetails,
+ UpdateChoreAssignee,
}
diff --git a/src/views/Authorization/Signup.jsx b/src/views/Authorization/Signup.jsx
index a1e312c..9e6cc70 100644
--- a/src/views/Authorization/Signup.jsx
+++ b/src/views/Authorization/Signup.jsx
@@ -7,6 +7,7 @@ import {
FormHelperText,
Input,
Sheet,
+ Snackbar,
Typography,
} from '@mui/joy'
import React from 'react'
@@ -25,6 +26,8 @@ const SignupView = () => {
const [emailError, setEmailError] = React.useState('')
const [displayNameError, setDisplayNameError] = React.useState('')
const [error, setError] = React.useState(null)
+ const [snackbarOpen, setSnackbarOpen] = React.useState(false)
+ const [snackbarMessage, setSnackbarMessage] = React.useState('')
const handleLogin = (username, password) => {
login(username, password).then(response => {
if (response.status === 200) {
@@ -39,6 +42,7 @@ const SignupView = () => {
})
} else {
console.log('Login failed', response)
+
// Navigate('/login')
}
})
@@ -101,7 +105,10 @@ const SignupView = () => {
handleLogin(username, password)
} else {
console.log('Signup failed')
- setError('Signup failed')
+ response.json().then(res => {
+ setError(res.error)
+ }
+ )
}
})
}
@@ -256,6 +263,14 @@ const SignupView = () => {
</Button>
</Sheet>
</Box>
+ <Snackbar
+ open={error !== null}
+ onClose={() => setError(null)}
+ autoHideDuration={5000}
+ message={error}
+ >
+ {error}
+ </Snackbar>
</Container>
)
}
diff --git a/src/views/ChoreEdit/ChoreView.jsx b/src/views/ChoreEdit/ChoreView.jsx
index 172ad9b..b77b511 100644
--- a/src/views/ChoreEdit/ChoreView.jsx
+++ b/src/views/ChoreEdit/ChoreView.jsx
@@ -3,6 +3,7 @@ import {
CancelScheduleSend,
Check,
Checklist,
+ Edit,
History,
PeopleAlt,
Person,
@@ -258,7 +259,7 @@ const ChoreView = () => {
>
<Grid container spacing={1}>
{infoCards.map((detail, index) => (
- <Grid item xs={4} key={index}>
+ <Grid item xs={6} key={index}>
{/* divider between the list items: */}
<ListItem key={index}>
@@ -411,7 +412,15 @@ const ChoreView = () => {
}}
/>
)}
-
+ <Box
+ sx={{
+ display: 'flex',
+ flexDirection: 'row',
+ gap: 1,
+ alignContent: 'center',
+ justifyContent: 'center',
+ }}
+ >
<Button
fullWidth
size='lg'
@@ -419,20 +428,15 @@ const ChoreView = () => {
disabled={isPendingCompletion}
color={isPendingCompletion ? 'danger' : 'success'}
startDecorator={<Check />}
+ sx={
+ {
+ flex: 4,
+ }
+ }
>
<Box>Mark as done</Box>
</Button>
- <Divider sx={{ my: 0.5 }}>or</Divider>
-
- <Box
- sx={{
- display: 'flex',
- flexDirection: 'row',
- gap: 1,
- alignContent: 'center',
- justifyContent: 'center',
- }}
- >
+
<Button
fullWidth
size='lg'
@@ -454,9 +458,26 @@ const ChoreView = () => {
})
}}
startDecorator={<SwitchAccessShortcut />}
+ sx={
+ {
+ flex: 1,
+ }
+ }
>
<Box>Skip</Box>
</Button>
+ </Box>
+ <Divider sx={{ my: 0.5 }}>More</Divider>
+
+ <Box
+ sx={{
+ display: 'flex',
+ flexDirection: 'row',
+ gap: 1,
+ alignContent: 'center',
+ justifyContent: 'center',
+ }}
+ >
<Button
startDecorator={<History />}
size='lg'
@@ -469,8 +490,21 @@ const ChoreView = () => {
>
History
</Button>
- </Box>
+ <Button
+ startDecorator={<Edit />}
+ size='lg'
+ color='primary'
+ variant='outlined'
+ fullWidth
+ onClick={() => {
+ navigate(`/chores/${choreId}/edit`)
+ }}
+ >
+ Edit
+ </Button>
+
+</Box>
<Snackbar
open={isPendingCompletion}
endDecorator={
diff --git a/src/views/Chores/ChoreCard.jsx b/src/views/Chores/ChoreCard.jsx
index 97e407a..29ee933 100644
--- a/src/views/Chores/ChoreCard.jsx
+++ b/src/views/Chores/ChoreCard.jsx
@@ -3,7 +3,6 @@ import {
Check,
Delete,
Edit,
- HowToReg,
KeyboardDoubleArrowUp,
LocalOffer,
ManageSearch,
@@ -39,7 +38,11 @@ import moment from 'moment'
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { API_URL } from '../../Config'
-import { MarkChoreComplete, SkipChore } from '../../utils/Fetcher'
+import {
+ MarkChoreComplete,
+ SkipChore,
+ UpdateChoreAssignee,
+} from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
import DateModal from '../Modals/Inputs/DateModal'
@@ -219,7 +222,14 @@ const ChoreCard = ({
})
}
const handleAssigneChange = assigneeId => {
- // TODO: Implement assignee change
+ UpdateChoreAssignee(chore.id, assigneeId).then(response => {
+ if (response.ok) {
+ response.json().then(data => {
+ const newChore = data.res
+ onChoreUpdate(newChore, 'assigned')
+ })
+ }
+ })
}
const handleCompleteWithNote = note => {
Fetch(`${API_URL}/chores/${chore.id}/do`, {
@@ -305,8 +315,28 @@ const ChoreCard = ({
return 'Yearly'
} else if (chore.frequencyType === 'days_of_the_week') {
let days = JSON.parse(chore.frequencyMetadata).days
- days = days.map(d => moment().day(d).format('ddd'))
- return days.join(', ')
+ if (days.length > 4) {
+ const allDays = [
+ 'Sunday',
+ 'Monday',
+ 'Tuesday',
+ 'Wednesday',
+ 'Thursday',
+ 'Friday',
+ 'Saturday',
+ ]
+ const selectedDays = days.map(d => moment().day(d).format('dddd'))
+ const notSelectedDay = allDays.filter(
+ day => !selectedDays.includes(day),
+ )
+ const notSelectedShortdays = notSelectedDay.map(d =>
+ moment().day(d).format('ddd'),
+ )
+ return `Daily except ${notSelectedShortdays.join(', ')}`
+ } else {
+ days = days.map(d => moment().day(d).format('ddd'))
+ return days.join(', ')
+ }
} else if (chore.frequencyType === 'day_of_the_month') {
let freqData = JSON.parse(chore.frequencyMetadata)
const months = freqData.months.map(m => moment().month(m).format('MMM'))
@@ -547,10 +577,6 @@ const ChoreCard = ({
<RecordVoiceOver />
Delegate to someone else
</MenuItem>
- <MenuItem>
- <HowToReg />
- Complete as someone else
- </MenuItem>
<Divider />
<MenuItem
onClick={() => {
@@ -620,10 +646,13 @@ const ChoreCard = ({
options={performers}
displayKey='displayName'
title={`Delegate to someone else`}
+ placeholder={'Select a performer'}
onClose={() => {
setIsChangeAssigneeModalOpen(false)
}}
- onSave={handleAssigneChange}
+ onSave={selected => {
+ handleAssigneChange(selected.id)
+ }}
/>
<ConfirmationModal config={confirmModelConfig} />
<TextModal
diff --git a/src/views/Chores/MyChores.jsx b/src/views/Chores/MyChores.jsx
index d4eed5a..f7b27da 100644
--- a/src/views/Chores/MyChores.jsx
+++ b/src/views/Chores/MyChores.jsx
@@ -1,13 +1,17 @@
-import { Add, CancelRounded, EditCalendar } from '@mui/icons-material'
import {
- Badge,
+ Add,
+ CancelRounded,
+ EditCalendar,
+ FilterAlt,
+ FilterAltOff,
+} from '@mui/icons-material'
+import {
Box,
- Checkbox,
+ Chip,
Container,
IconButton,
Input,
List,
- ListItem,
Menu,
MenuItem,
Snackbar,
@@ -192,95 +196,27 @@ const MyChores = () => {
My Chores
</Typography> */}
{/* <Sheet> */}
- <List
- orientation='horizontal'
- wrap
+
+ {/* Search box to filter */}
+ <Box
sx={{
- '--List-gap': '8px',
- '--ListItem-radius': '20px',
- '--ListItem-minHeight': '32px',
- '--ListItem-gap': '4px',
- mt: 0.2,
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignContent: 'center',
+ alignItems: 'center',
+ gap: 1,
}}
>
- {['All', 'Overdue', 'Due today', 'Due in week'].map(filter => (
- <Badge
- key={filter}
- anchorOrigin={{
- vertical: 'top',
- horizontal: 'right',
- }}
- variant='outlined'
- color={selectedFilter === filter ? 'primary' : 'neutral'}
- badgeContent={FILTERS[filter](chores).length}
- badgeInset={'5px'}
- >
- <ListItem key={filter}>
- <Checkbox
- key={'checkbox' + filter}
- label={filter}
- onClick={() => handleSelectedFilter(filter)}
- checked={filter === selectedFilter}
- disableIcon
- overlay
- size='sm'
- />
- </ListItem>
- </Badge>
- ))}
-
- <ListItem onClick={handleFilterMenuOpen}>
- <Checkbox key='checkboxAll' label='⋮' disableIcon overlay size='lg' />
- </ListItem>
- <Menu
- ref={menuRef}
- anchorEl={anchorEl}
- open={Boolean(anchorEl)}
- onClose={handleFilterMenuClose}
- >
- <MenuItem
- onClick={() => {
- setFilteredChores(
- FILTERS['Assigned To Me'](chores, userProfile.id),
- )
- setSelectedFilter('Assigned To Me')
- handleFilterMenuClose()
- }}
- >
- Assigned to me
- </MenuItem>
- <MenuItem
- onClick={() => {
- setFilteredChores(
- FILTERS['Created By Me'](chores, userProfile.id),
- )
- setSelectedFilter('Created By Me')
- handleFilterMenuClose()
- }}
- >
- Created by me
- </MenuItem>
- <MenuItem
- onClick={() => {
- setFilteredChores(FILTERS['No Due Date'](chores, userProfile.id))
- setSelectedFilter('No Due Date')
- handleFilterMenuClose()
- }}
- >
- No Due Date
- </MenuItem>
- </Menu>
- </List>
- {/* Search box to filter */}
- <Box>
<Input
placeholder='Search'
value={searchTerm}
+ fullWidth
sx={{
mt: 1,
mb: 1,
- borderRadius: 20,
+ borderRadius: 24,
// border: '1px solid',
+ height: 24,
borderColor: 'text.disabled',
padding: 1,
}}
@@ -296,6 +232,73 @@ const MyChores = () => {
)
}
/>
+ <IconButton
+ onClick={handleFilterMenuOpen}
+ variant='outlined'
+ color={
+ selectedFilter && selectedFilter != 'All' ? 'primary' : 'neutral'
+ }
+ size='sm'
+ sx={{
+ height: 24,
+ borderRadius: 24,
+ }}
+ >
+ {selectedFilter && selectedFilter != 'All' ? (
+ <FilterAltOff />
+ ) : (
+ <FilterAlt />
+ )}
+ </IconButton>
+ <List
+ orientation='horizontal'
+ wrap
+ sx={{
+ // '--List-gap': '8px',
+ // '--ListItem-radius': '20px',
+ // '--ListItem-minHeight': '32px',
+ // '--ListItem-gap': '4px',
+ mt: 0.2,
+ }}
+ >
+ {/* <Checkbox
+ key='checkboxAll'
+ label=''
+ disableIcon
+ overlay
+ size='sm'
+ /> */}
+
+ <Menu
+ ref={menuRef}
+ anchorEl={anchorEl}
+ open={Boolean(anchorEl)}
+ onClose={handleFilterMenuClose}
+ >
+ {Object.keys(FILTERS).map(filter => (
+ <MenuItem
+ key={filter}
+ onClick={() => {
+ const filterFunction = FILTERS[filter]
+ const filteredChores =
+ filterFunction.length === 2
+ ? filterFunction(chores, userProfile.id)
+ : filterFunction(chores)
+ setFilteredChores(filteredChores)
+ setSelectedFilter(filter)
+ handleFilterMenuClose()
+ }}
+ >
+ {filter}
+ <Chip color={selectedFilter === filter ? 'primary' : 'neutral'}>
+ {FILTERS[filter].length === 2
+ ? FILTERS[filter](chores, userProfile.id).length
+ : FILTERS[filter](chores).length}
+ </Chip>
+ </MenuItem>
+ ))}
+ </Menu>
+ </List>
</Box>
{/* </Sheet> */}
diff --git a/src/views/Landing/CookiePermissionSnackbar.jsx b/src/views/Landing/CookiePermissionSnackbar.jsx
new file mode 100644
index 0000000..7fe3724
--- /dev/null
+++ b/src/views/Landing/CookiePermissionSnackbar.jsx
@@ -0,0 +1,40 @@
+import { Button, Snackbar } from '@mui/joy'
+import Cookies from 'js-cookie'
+import { useEffect, useState } from 'react'
+
+const CookiePermissionSnackbar = () => {
+ useEffect(() => {
+ const cookiePermission = Cookies.get('cookies_permission')
+
+ if (cookiePermission !== 'true') {
+ setOpen(true)
+ }
+ }, [])
+
+ const [open, setOpen] = useState(false)
+ const handleClose = () => {
+ Cookies.set('cookies_permission', 'true')
+ setOpen(false)
+ }
+
+ return (
+ <Snackbar
+ open={open}
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
+ onClose={(event, reason) => {
+ if (reason === 'clickaway') {
+ return
+ }
+ // Cookies.set('cookies_permission', 'true')
+ handleClose()
+ }}
+ >
+ We use cookies to ensure you get the best experience on our website.
+ <Button variant='soft' onClick={handleClose}>
+ Accept
+ </Button>
+ </Snackbar>
+ )
+}
+
+export default CookiePermissionSnackbar
diff --git a/src/views/Landing/FeaturesSection.jsx b/src/views/Landing/FeaturesSection.jsx
index 32dcbc8..dbabd43 100644
--- a/src/views/Landing/FeaturesSection.jsx
+++ b/src/views/Landing/FeaturesSection.jsx
@@ -111,18 +111,6 @@ function FeaturesSection() {
return (
<Container sx={{ textAlign: 'center' }}>
<Typography level='h4' mt={2} mb={4}>
- Donetick is under development
- </Typography>
-
- <Container maxWidth={'lg'} sx={{ mb: 8 }}>
- <Typography level='body-md' color='neutral'>
- Donetick is beta software. and is still under development. thing may
- change, break, or disappear at any time. Please use it at your own
- risk and discretion. I will do my best to keep the service running
- </Typography>
- </Container>
-
- <Typography level='h4' mt={2} mb={4}>
Why Donetick?
</Typography>
diff --git a/src/views/Landing/Footer.jsx b/src/views/Landing/Footer.jsx
index 11a011a..d8cd4a4 100644
--- a/src/views/Landing/Footer.jsx
+++ b/src/views/Landing/Footer.jsx
@@ -4,6 +4,7 @@ import Box from '@mui/joy/Box'
import Link from '@mui/joy/Link'
import Typography from '@mui/joy/Typography'
import * as React from 'react'
+import { version } from '../../../package.json'
function Footer() {
return (
@@ -47,17 +48,6 @@ function Footer() {
tick✓
</span>
</Typography>
- <span
- style={{
- fontSize: 12,
- fontWeight: 700,
- position: 'relative',
- top: 12,
- right: 45,
- }}
- >
- Beta
- </span>
</Box>
</Box>
<Box>
@@ -65,11 +55,11 @@ function Footer() {
Github
</Typography>
<Link
- href='https://github.com/donetick/core'
+ href='https://github.com/donetick/donetick'
level='body2'
sx={{ display: 'block' }}
>
- Core(Backend)
+ Donetick
</Link>
<Link
href='https://github.com/donetick/frontend'
@@ -86,11 +76,18 @@ function Footer() {
Home Assistant Addon
</Link>
<Link
- href='https://github.com/orgs/Donetick/packages'
+ href='https://github.com/orgs/donetick/packages'
+ level='body2'
+ sx={{ display: 'block' }}
+ >
+ Docker Images
+ </Link>
+ <Link
+ href='https://github.com/donetick/donetick/releases'
level='body2'
sx={{ display: 'block' }}
>
- Packages
+ Releases
</Link>
</Box>
<Box>
@@ -107,6 +104,9 @@ function Footer() {
<Link disabled={true} level='body2' sx={{ display: 'block' }}>
Changelog(soon)
</Link>
+ <Link disabled={true} level='body2' sx={{ display: 'block' }}>
+ V{version}
+ </Link>
</Box>
{/* <Box>
<Typography level='body2' fontWeight='bold' mb={1}>
diff --git a/src/views/Landing/GettingStarted.jsx b/src/views/Landing/GettingStarted.jsx
new file mode 100644
index 0000000..ede365d
--- /dev/null
+++ b/src/views/Landing/GettingStarted.jsx
@@ -0,0 +1,171 @@
+import {
+ AddHome,
+ AutoAwesome,
+ Cloud,
+ GitHub,
+ InstallMobile,
+ Storage,
+} from '@mui/icons-material'
+import { Box, Button, Card, Grid, styled, Typography } from '@mui/joy'
+import { useNavigate } from 'react-router-dom'
+const IconContainer = styled('div')({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: '50%',
+ minWidth: '60px',
+ height: '60px',
+ marginRight: '16px',
+})
+
+const ButtonContainer = styled('div')({
+ display: 'flex',
+ justifyContent: 'center',
+ marginTop: 'auto',
+})
+
+function StartOptionCard({ icon: Icon, title, description, button, index }) {
+ return (
+ <Card
+ variant='plain'
+ sx={{
+ p: 2,
+ display: 'flex',
+ minHeight: '300px',
+ py: 4,
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ }}
+ data-aos-delay={100 * index}
+ data-aos-anchor='[data-aos-id-getting-started-container]'
+ data-aos='fade-up'
+ >
+ {/* Changes are within this div */}
+ <Box
+ sx={{
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ }}
+ >
+ <IconContainer>{Icon}</IconContainer>
+
+ <Typography level='h4' textAlign={'center'}>
+ {title}
+ </Typography>
+ </Box>
+
+ <Typography level='body-md' color='neutral' lineHeight={1.6}>
+ {description}
+ </Typography>
+
+ <ButtonContainer>{button}</ButtonContainer>
+ </Card>
+ )
+}
+
+const GettingStarted = () => {
+ const navigate = useNavigate()
+ const information = [
+ {
+ title: 'Donetick Web',
+ icon: <Cloud style={{ fontSize: '48px' }} />,
+ description:
+ 'The easiest way! just create account and start using DoneTick',
+ button: (
+ <Button
+ size='lg'
+ fullWidth
+ startDecorator={<AutoAwesome />}
+ onClick={() => {
+ navigate('/my/chores')
+ }}
+ >
+ Start Now!
+ </Button>
+ ),
+ },
+ {
+ title: 'Selfhosted',
+ icon: <Storage style={{ fontSize: '48px' }} />,
+ description: 'Download the binary and manage your own DoneTick instance',
+ button: (
+ <Button
+ size='lg'
+ fullWidth
+ startDecorator={<GitHub />}
+ onClick={() => {
+ window.open(
+ 'https://github.com/donetick/donetick/releases',
+ '_blank',
+ )
+ }}
+ >
+ Github Releases
+ </Button>
+ ),
+ },
+ {
+ title: 'Hassio Addon',
+ icon: <AddHome style={{ fontSize: '48px' }} />,
+ description:
+ 'have Home Assistant? install DoneTick as a Home Assistant Addon with single click',
+ button: (
+ <Button
+ size='lg'
+ fullWidth
+ startDecorator={<InstallMobile />}
+ onClick={() => {
+ window.open(
+ 'https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fdonetick%2Fhassio-addons',
+ )
+ }}
+ >
+ Add Addon
+ </Button>
+ ),
+ },
+ ]
+ return (
+ <Box
+ sx={{
+ alignContent: 'center',
+ textAlign: 'center',
+ display: 'flex',
+ flexDirection: 'column',
+ mt: 2,
+ }}
+ >
+ <Typography level='h4' mt={2} mb={4}>
+ Getting Started
+ </Typography>
+
+ <Box maxWidth={'lg'} sx={{ mb: 8 }}>
+ <Typography level='body-md' color='neutral'>
+ you can start using DoneTick in multiple ways, easiest way is to use
+ Donetick Web and you can start in seconds, or if you are into
+ selfhosting you can download the binary and run it on your own server,
+ or if you are using Home Assistant you can install DoneTick as a Home
+ Assistant Addon
+ </Typography>
+ <div data-aos-id-getting-started-container>
+ <Grid container spacing={4} mt={4}>
+ {information.map((info, index) => (
+ <Grid item xs={12} md={4} key={index}>
+ <StartOptionCard
+ icon={info.icon}
+ title={info.title}
+ description={info.description}
+ button={info.button}
+ />
+ </Grid>
+ ))}
+ </Grid>
+ </div>
+ </Box>
+ </Box>
+ )
+}
+
+export default GettingStarted
diff --git a/src/views/Landing/HomeHero.jsx b/src/views/Landing/HomeHero.jsx
index 644c9cc..35286ec 100644
--- a/src/views/Landing/HomeHero.jsx
+++ b/src/views/Landing/HomeHero.jsx
@@ -61,17 +61,6 @@ const HomeHero = () => {
>
tick
</span>
- <span
- style={{
- fontSize: 20,
- fontWeight: 700,
- position: 'relative',
- top: 12,
- right: 45,
- }}
- >
- Beta
- </span>
</Typography>
</Box>
)
@@ -167,7 +156,7 @@ const HomeHero = () => {
className='hover:scale-105'
onClick={() => {
// new window open to https://github.com/Donetick:
- window.open('https://github.com/donetick', '_blank')
+ window.open('https://github.com/donetick/donetick', '_blank')
}}
startDecorator={<GitHub />}
>
diff --git a/src/views/Landing/Landing.jsx b/src/views/Landing/Landing.jsx
index 4ca1b60..a323ecc 100644
--- a/src/views/Landing/Landing.jsx
+++ b/src/views/Landing/Landing.jsx
@@ -3,12 +3,14 @@ import AOS from 'aos'
import 'aos/dist/aos.css'
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
+import CookiePermissionSnackbar from './CookiePermissionSnackbar'
import DemoAssignee from './DemoAssignee'
import DemoHistory from './DemoHistory'
import DemoMyChore from './DemoMyChore'
import DemoScheduler from './DemoScheduler'
import FeaturesSection from './FeaturesSection'
import Footer from './Footer'
+import GettingStarted from './GettingStarted'
import HomeHero from './HomeHero'
const Landing = () => {
const Navigate = useNavigate()
@@ -39,6 +41,8 @@ const Landing = () => {
<DemoHistory />
</Grid>
<FeaturesSection />
+ <GettingStarted />
+
{/* <PricingSection /> */}
<Box
sx={{
@@ -49,7 +53,7 @@ const Landing = () => {
mb: 5,
}}
></Box>
-
+ <CookiePermissionSnackbar />
<Footer />
</Container>
)
diff --git a/src/views/Modals/Inputs/SelectModal.jsx b/src/views/Modals/Inputs/SelectModal.jsx
index 61e7ae9..f879bf0 100644
--- a/src/views/Modals/Inputs/SelectModal.jsx
+++ b/src/views/Modals/Inputs/SelectModal.jsx
@@ -9,7 +9,7 @@ import {
} from '@mui/joy'
import React from 'react'
-function SelectModal({ isOpen, onClose, onSave, options, title, displayKey }) {
+function SelectModal({ isOpen, onClose, onSave, options, title, displayKey,placeholder }) {
const [selected, setSelected] = React.useState(null)
const handleSave = () => {
onSave(options.find(item => item.id === selected))
@@ -20,7 +20,7 @@ function SelectModal({ isOpen, onClose, onSave, options, title, displayKey }) {
<Modal open={isOpen} onClose={onClose}>
<ModalDialog>
<Typography variant='h4'>{title}</Typography>
- <Select>
+ <Select placeholder={placeholder}>
{options.map((item, index) => (
<Option
value={item.id}
diff --git a/src/views/components/NavBar.jsx b/src/views/components/NavBar.jsx
index d3708ae..5537246 100644
--- a/src/views/components/NavBar.jsx
+++ b/src/views/components/NavBar.jsx
@@ -30,11 +30,11 @@ const links = [
label: 'Home',
icon: <HomeOutlined />,
},
- {
- to: '/chores',
- label: 'Desktop View',
- icon: <ListAltRounded />,
- },
+ // {
+ // to: '/chores',
+ // label: 'Desktop View',
+ // icon: <ListAltRounded />,
+ // },
{
to: '/things',
label: 'Things',
@@ -114,17 +114,6 @@ const NavBar = () => {
tick✓
</span>
</Typography>
- <span
- style={{
- fontSize: 12,
- fontWeight: 700,
- position: 'relative',
- top: 12,
- right: 45,
- }}
- >
- Beta
- </span>
</Box>
<Drawer
open={drawerOpen}
diff --git a/vite.config.js b/vite.config.js
index 5af4b6f..f8e9fde 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -14,7 +14,10 @@ export default defineConfig({
'safari-pinned-tab.svg',
'mstile-150x150.png',
],
- injectManifest: true,
+ injectManifest: {
+ globPatterns: ['**/*.{js,css,html,png,svg}'],
+ globIgnores: ['index.html'],
+ },
manifest: {
name: 'Donetick: Simplify Tasks & Chores, Together.',
short_name: 'Donetick',
@@ -55,6 +58,10 @@ export default defineConfig({
background_color: '#ffffff',
display: 'standalone',
},
+ workbox: {
+ skipWaiting: true, // Force the waiting service worker to become the active service worker
+ clientsClaim: true, // Take control of uncontrolled clients as soon as the service worker becomes active
+ },
}),
],