diff options
-rw-r--r-- | .env | 3 | ||||
-rw-r--r-- | package-lock.json | 50 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | src/App.jsx | 10 | ||||
-rw-r--r-- | src/utils/Fetcher.jsx | 10 | ||||
-rw-r--r-- | src/views/Authorization/Signup.jsx | 17 | ||||
-rw-r--r-- | src/views/ChoreEdit/ChoreView.jsx | 62 | ||||
-rw-r--r-- | src/views/Chores/ChoreCard.jsx | 49 | ||||
-rw-r--r-- | src/views/Chores/MyChores.jsx | 169 | ||||
-rw-r--r-- | src/views/Landing/CookiePermissionSnackbar.jsx | 40 | ||||
-rw-r--r-- | src/views/Landing/FeaturesSection.jsx | 12 | ||||
-rw-r--r-- | src/views/Landing/Footer.jsx | 30 | ||||
-rw-r--r-- | src/views/Landing/GettingStarted.jsx | 171 | ||||
-rw-r--r-- | src/views/Landing/HomeHero.jsx | 13 | ||||
-rw-r--r-- | src/views/Landing/Landing.jsx | 6 | ||||
-rw-r--r-- | src/views/Modals/Inputs/SelectModal.jsx | 4 | ||||
-rw-r--r-- | src/views/components/NavBar.jsx | 21 | ||||
-rw-r--r-- | vite.config.js | 9 |
18 files changed, 506 insertions, 173 deletions
@@ -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 + }, }), ], |