implement Application and MarketingApp components, enhance routing and user context management (#194)

This commit is contained in:
Mohamad Tarbin
2026-08-06 23:57:08 -04:00
committed by GitHub
parent 0b4b21bbb0
commit 5fb0b5b656
10 changed files with 135 additions and 45 deletions

10
src/Application.jsx Normal file
View 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
View 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

View File

@@ -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',

View File

@@ -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>,
)

View File

@@ -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,

View File

@@ -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}>

View File

@@ -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)

View File

@@ -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>
))}

View File

@@ -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&apos;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>

View File

@@ -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>