implement Application and MarketingApp components, enhance routing and user context management (#194)
This commit is contained in:
10
src/Application.jsx
Normal file
10
src/Application.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import App from './App.jsx'
|
||||
import Contexts from './contexts/Contexts.jsx'
|
||||
|
||||
const Application = () => (
|
||||
<Contexts>
|
||||
<App />
|
||||
</Contexts>
|
||||
)
|
||||
|
||||
export default Application
|
||||
66
src/MarketingApp.jsx
Normal file
66
src/MarketingApp.jsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useColorScheme } from '@mui/joy'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { useEffect } from 'react'
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
||||
|
||||
import { ImpersonateUserProvider } from './contexts/ImpersonateUserContext'
|
||||
import { LocalizationProvider } from './contexts/LocalizationContext'
|
||||
import ThemeContext from './contexts/ThemeContext'
|
||||
import Landing from './views/Landing/Landing'
|
||||
import PrivacyPolicyView from './views/PrivacyPolicy/PrivacyPolicyView'
|
||||
import TermsView from './views/Terms/TermsView'
|
||||
|
||||
const AppRedirect = () => {
|
||||
useEffect(() => {
|
||||
const { hash, pathname, search } = window.location
|
||||
window.location.replace(
|
||||
`https://app.donetick.com${pathname}${search}${hash}`,
|
||||
)
|
||||
}, [])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{ path: '/', element: <Landing /> },
|
||||
{ path: '/privacy', element: <PrivacyPolicyView /> },
|
||||
{ path: '/terms', element: <TermsView /> },
|
||||
{ path: '*', element: <AppRedirect /> },
|
||||
])
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { enabled: false, refetchOnWindowFocus: false, retry: false },
|
||||
},
|
||||
})
|
||||
|
||||
const ThemeClass = () => {
|
||||
const { mode, systemMode } = useColorScheme()
|
||||
|
||||
useEffect(() => {
|
||||
const storedMode = JSON.parse(localStorage.getItem('themeMode') || 'null')
|
||||
const selectedMode = storedMode || mode
|
||||
const isDark =
|
||||
selectedMode === 'dark' ||
|
||||
(selectedMode === 'system' && systemMode === 'dark')
|
||||
|
||||
document.getElementById('root').classList.toggle('dark', isDark)
|
||||
}, [mode, systemMode])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const MarketingApp = () => (
|
||||
<ThemeContext>
|
||||
<ThemeClass />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<LocalizationProvider>
|
||||
<ImpersonateUserProvider>
|
||||
<RouterProvider router={router} />
|
||||
</ImpersonateUserProvider>
|
||||
</LocalizationProvider>
|
||||
</QueryClientProvider>
|
||||
</ThemeContext>
|
||||
)
|
||||
|
||||
export default MarketingApp
|
||||
@@ -1,3 +1,5 @@
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
||||
|
||||
import App from '@/App'
|
||||
import ChoreEdit from '@/views/ChoreEdit/ChoreEdit'
|
||||
import Error from '@/views/Error'
|
||||
@@ -10,7 +12,7 @@ import Settings from '@/views/Settings/Settings'
|
||||
import SettingsOverview from '@/views/Settings/SettingsOverview'
|
||||
import SettingsRoutes from '@/views/Settings/SettingsRoutes'
|
||||
import ThemeSettings from '@/views/Settings/ThemeSettings'
|
||||
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
|
||||
|
||||
import AuthenticationLoading from '../views/Authorization/Authenticating'
|
||||
import ForgotPasswordView from '../views/Authorization/ForgotPasswordView'
|
||||
import LoginSettings from '../views/Authorization/LoginSettings'
|
||||
@@ -49,16 +51,6 @@ import ThingsView from '../views/Things/ThingsView'
|
||||
import TimerDetails from '../views/Timer/TimerDetails'
|
||||
import UserActivities from '../views/User/UserActivities'
|
||||
import UserPoints from '../views/User/UserPoints'
|
||||
const getMainRoute = () => {
|
||||
if (
|
||||
// if domain is www.donetick.com or donetick.com then show landing page:
|
||||
window.location.hostname === 'www.donetick.com' ||
|
||||
window.location.hostname === 'donetick.com'
|
||||
) {
|
||||
return <Landing />
|
||||
}
|
||||
return <MyChores />
|
||||
}
|
||||
const Router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
@@ -67,7 +59,7 @@ const Router = createBrowserRouter([
|
||||
children: [
|
||||
{
|
||||
path: '/',
|
||||
element: getMainRoute(),
|
||||
element: <MyChores />,
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
|
||||
23
src/main.jsx
23
src/main.jsx
@@ -1,14 +1,23 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import Contexts from './contexts/Contexts.jsx'
|
||||
import './i18n/config'
|
||||
import './index.css'
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
const marketingHosts = new Set(['donetick.com', 'www.donetick.com'])
|
||||
const isMarketingSite =
|
||||
marketingHosts.has(window.location.hostname) ||
|
||||
(import.meta.env.DEV &&
|
||||
new URLSearchParams(window.location.search).get('site') === 'marketing')
|
||||
|
||||
export const Site = React.lazy(() =>
|
||||
isMarketingSite ? import('./MarketingApp.jsx') : import('./Application.jsx'),
|
||||
)
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<Contexts>
|
||||
<App />
|
||||
</Contexts>
|
||||
<React.Suspense fallback={null}>
|
||||
<Site />
|
||||
</React.Suspense>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
import {
|
||||
GetAllCircleMembers,
|
||||
GetAllUsers,
|
||||
@@ -54,7 +55,7 @@ export const useCircleMembers = () => {
|
||||
return { data, error, isLoading, handleRefetch }
|
||||
}
|
||||
|
||||
export const useUserProfile = () => {
|
||||
export const useUserProfile = ({ enabled = true } = {}) => {
|
||||
const queryClient = useQueryClient()
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
@@ -80,7 +81,7 @@ export const useUserProfile = () => {
|
||||
},
|
||||
staleTime: 30 * 60 * 1000,
|
||||
gcTime: 30 * 60 * 1000,
|
||||
enabled: !!token,
|
||||
enabled: enabled && !!token,
|
||||
})
|
||||
return {
|
||||
data,
|
||||
|
||||
@@ -84,9 +84,7 @@ const generateSchedulePreview = (metadata, formatTimeFn) => {
|
||||
.map(day => day.charAt(0).toUpperCase() + day.slice(1, 3))
|
||||
.join(', ')
|
||||
|
||||
const timeStr = metadata.time
|
||||
? formatTimeFn(metadata.time)
|
||||
: '6:00 PM'
|
||||
const timeStr = metadata.time ? formatTimeFn(metadata.time) : '6:00 PM'
|
||||
|
||||
if (metadata.weekPattern === 'every_week' || !metadata.weekPattern) {
|
||||
return `Every ${dayNames} at ${timeStr}`
|
||||
@@ -109,11 +107,11 @@ const generateSchedulePreview = (metadata, formatTimeFn) => {
|
||||
}
|
||||
|
||||
export const RepeatOnSections = ({
|
||||
frequencyType,
|
||||
frequency,
|
||||
onFrequencyUpdate,
|
||||
frequencyMetadata,
|
||||
frequencyType,
|
||||
onFrequencyMetadataUpdate,
|
||||
onFrequencyUpdate,
|
||||
}) => {
|
||||
const { fmt } = useLocalization()
|
||||
// if time on frequencyMetadata is not set, try to set it to the nextDueDate if available,
|
||||
@@ -527,20 +525,21 @@ export const RepeatOnSections = ({
|
||||
}
|
||||
|
||||
const RepeatSection = ({
|
||||
frequencyType,
|
||||
frequency,
|
||||
onFrequencyUpdate,
|
||||
onFrequencyTypeUpdate,
|
||||
frequencyMetadata,
|
||||
onFrequencyMetadataUpdate,
|
||||
frequencyError,
|
||||
allUserThings,
|
||||
onTriggerUpdate,
|
||||
OnTriggerValidate,
|
||||
allUserThings,
|
||||
frequency,
|
||||
frequencyError,
|
||||
frequencyMetadata,
|
||||
frequencyType,
|
||||
isAttemptToSave,
|
||||
onFrequencyMetadataUpdate,
|
||||
onFrequencyTypeUpdate,
|
||||
onFrequencyUpdate,
|
||||
onTriggerUpdate,
|
||||
selectedThing,
|
||||
viewOnly = false,
|
||||
}) => {
|
||||
const { data: userProfile } = useUserProfile()
|
||||
const { data: userProfile } = useUserProfile({ enabled: !viewOnly })
|
||||
|
||||
return (
|
||||
<Box mt={2}>
|
||||
|
||||
@@ -49,7 +49,7 @@ const ChoreCard = ({
|
||||
sx,
|
||||
viewOnly,
|
||||
}) => {
|
||||
const { data: userProfile } = useUserProfile()
|
||||
const { data: userProfile } = useUserProfile({ enabled: !viewOnly })
|
||||
const { timeFormat } = useLocalization()
|
||||
const { data: pendingCmds } = usePendingCommands(chore.id)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box, Card, Grid, List, Typography } from '@mui/joy'
|
||||
import moment from 'moment'
|
||||
|
||||
import HistoryCard from '../History/HistoryCard'
|
||||
|
||||
const DemoHistory = () => {
|
||||
@@ -7,29 +8,32 @@ const DemoHistory = () => {
|
||||
{
|
||||
id: 32,
|
||||
choreId: 12,
|
||||
completedAt: moment().hour(4).format(),
|
||||
performedAt: moment().hour(4).format(),
|
||||
completedBy: 1,
|
||||
assignedTo: 1,
|
||||
notes: null,
|
||||
dueDate: moment().format(),
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
choreId: 12,
|
||||
completedAt: moment().day(-1).format(),
|
||||
performedAt: moment().day(-1).format(),
|
||||
completedBy: 1,
|
||||
assignedTo: 1,
|
||||
notes: 'Need to be replaced with a new one',
|
||||
dueDate: moment().day(-2).format(),
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
choreId: 12,
|
||||
completedAt: moment().day(-10).hour(1).format(),
|
||||
performedAt: moment().day(-10).hour(1).format(),
|
||||
completedBy: 2,
|
||||
assignedTo: 1,
|
||||
notes: null,
|
||||
dueDate: moment().day(-10).format(),
|
||||
status: 1,
|
||||
},
|
||||
]
|
||||
const performers = [
|
||||
@@ -61,6 +65,7 @@ const DemoHistory = () => {
|
||||
key={index}
|
||||
index={index}
|
||||
performers={performers}
|
||||
pendingCommands={[]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Card, Grid, Typography } from '@mui/joy'
|
||||
import moment from 'moment'
|
||||
import { useState } from 'react'
|
||||
|
||||
import ChoreCard from '../Chores/ChoreCard'
|
||||
|
||||
const DemoMyChore = () => {
|
||||
@@ -121,7 +122,12 @@ const DemoMyChore = () => {
|
||||
data-aos-anchor='[data-aos-first-tasks-list]'
|
||||
data-aos='fade-up'
|
||||
>
|
||||
<ChoreCard chore={card} performers={users} viewOnly={true} />
|
||||
<ChoreCard
|
||||
chore={card}
|
||||
performers={users}
|
||||
showActions={false}
|
||||
viewOnly
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Grid>
|
||||
@@ -141,10 +147,10 @@ const DemoMyChore = () => {
|
||||
<Typography level='body-lg' textAlign='center' sx={{ mb: 4 }}>
|
||||
Main view prioritize tasks due today, followed by overdue ones, and
|
||||
finally, future tasks or those without due dates. With Donetick, you
|
||||
can view all the tasks you've created (whether assigned to you or
|
||||
not) as well as tasks assigned to you by others. Quickly mark them
|
||||
as done with just one click, ensuring a smooth and efficient task
|
||||
management experience.
|
||||
can view all the tasks you've created (whether assigned to you
|
||||
or not) as well as tasks assigned to you by others. Quickly mark
|
||||
them as done with just one click, ensuring a smooth and efficient
|
||||
task management experience.
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box, Card, Grid, Typography } from '@mui/joy'
|
||||
import { useState } from 'react'
|
||||
|
||||
import RepeatSection from '../ChoreEdit/RepeatSection'
|
||||
|
||||
const DemoScheduler = () => {
|
||||
@@ -32,6 +33,7 @@ const DemoScheduler = () => {
|
||||
OnTriggerValidate={() => {}}
|
||||
isAttemptToSave={false}
|
||||
selectedThing={null}
|
||||
viewOnly
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
Reference in New Issue
Block a user