{dateNumber}
+{date.day}
Week Calendar
-- Weekly calendar is only available when navigating from the month - calendar for now -
-
Week {getWeek(startOfTheWeek)} of {getISOWeekYear(startOfTheWeek)}
@@ -113,10 +86,15 @@ const WeekCalendar = () => {
const weekDayName = WEEK_DAYS[getDay(day) - 1] || WEEK_DAYS[6];
return (
-
-
- {weekDayName}
-
+
+
+
+ {weekDayName}
+
+
+ {day.getDate()}
+
+
{eachMinuteOfInterval(
{
@@ -133,11 +111,11 @@ const WeekCalendar = () => {
className="group/minutes-cell relative flex gap-4"
>
{dayIndex === 0 && (
-
+
{hour !== 0 && hour}
)}
-
+
{groupOccurrences(dayIndex, hour).map(
([habitId, habitOccurrences]) => {
if (!habitOccurrences) {
diff --git a/src/components/habit/habits-page/HabitsPage.test.tsx b/src/components/habit/habits-page/HabitsPage.test.tsx
index ce0859d..4285b9b 100644
--- a/src/components/habit/habits-page/HabitsPage.test.tsx
+++ b/src/components/habit/habits-page/HabitsPage.test.tsx
@@ -40,7 +40,11 @@ jest.mock('@hooks', () => ({
.fn()
.mockReturnValue(['', jest.fn(), jest.fn(), jest.fn()]),
useFileField: jest.fn().mockReturnValue([null, jest.fn(), jest.fn()]),
- useScreenSize: jest.fn(),
+ useScreenWidth: jest.fn().mockReturnValue({
+ screenWidth: 1400,
+ isMobile: false,
+ isDesktop: true,
+ }),
}));
describe(HabitsPage.name, () => {
diff --git a/src/components/habit/habits-page/HabitsPage.tsx b/src/components/habit/habits-page/HabitsPage.tsx
index d300d75..df031c1 100644
--- a/src/components/habit/habits-page/HabitsPage.tsx
+++ b/src/components/habit/habits-page/HabitsPage.tsx
@@ -4,7 +4,7 @@ import {
EditHabitDialog,
TraitChip,
} from '@components';
-import { useDocumentTitle, useScreenSize } from '@hooks';
+import { useDocumentTitle, useScreenWidth } from '@hooks';
import { type Habit } from '@models';
import {
Button,
@@ -69,7 +69,7 @@ const HabitsPage = () => {
const { removeOccurrencesByHabitId } = useOccurrencesStore();
const [habitToEdit, setHabitToEdit] = React.useState(null);
const [habitToRemove, setHabitToRemove] = React.useState(null);
- const screenSize = useScreenSize();
+ const { isMobile } = useScreenWidth();
useDocumentTitle('My Habits | Habitrack');
@@ -99,8 +99,6 @@ const HabitsPage = () => {
setHabitToEdit(null);
};
- const isMobile = screenSize < 768;
-
return (
<>
diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx
index 47f1022..0a7f8b5 100644
--- a/src/components/header/Header.tsx
+++ b/src/components/header/Header.tsx
@@ -1,5 +1,5 @@
import { AuthModalButton } from '@components';
-import { useScreenSize, useFetchOnAuth } from '@hooks';
+import { useScreenWidth, useFetchOnAuth } from '@hooks';
import { Button, Tooltip, Spinner } from '@nextui-org/react';
import { GithubLogo } from '@phosphor-icons/react';
import { useSessionContext } from '@supabase/auth-helpers-react';
@@ -11,7 +11,7 @@ import ThemeToggle from './ThemeToggle';
const Header = () => {
useFetchOnAuth();
- const screenSize = useScreenSize();
+ const { screenWidth, isMobile, isDesktop } = useScreenWidth();
const session = useSessionContext();
return (
@@ -36,8 +36,8 @@ const Header = () => {
color="primary"
as={Link}
to="/calendar/month"
- size={screenSize > 1024 ? 'md' : 'sm'}
- className={screenSize < 339 ? 'min-w-fit px-2' : ''}
+ size={isDesktop ? 'md' : 'sm'}
+ className={screenWidth < 339 ? 'min-w-fit px-2' : ''}
>
Calendar
@@ -46,16 +46,16 @@ const Header = () => {
variant="flat"
as={Link}
to="/habits"
- size={screenSize > 1024 ? 'md' : 'sm'}
+ size={isDesktop ? 'md' : 'sm'}
className={clsx(
'dark:text-secondary-800',
- screenSize < 339 && 'min-w-fit px-2'
+ screenWidth < 339 && 'min-w-fit px-2'
)}
>
Habits
- {screenSize > 768 && (
+ {!isMobile && (
{
as={Link}
to="https://github.com/domhhv/habitrack"
target="_blank"
- size={screenSize > 1024 ? 'md' : 'sm'}
+ size={isDesktop ? 'md' : 'sm'}
>
- 1024 ? 20 : 16} />
+
)}
diff --git a/src/components/header/ThemeToggle.tsx b/src/components/header/ThemeToggle.tsx
index b6860b0..ee552cd 100644
--- a/src/components/header/ThemeToggle.tsx
+++ b/src/components/header/ThemeToggle.tsx
@@ -1,4 +1,4 @@
-import { ThemeMode, useScreenSize, useThemeMode } from '@hooks';
+import { ThemeMode, useScreenWidth, useThemeMode } from '@hooks';
import { ButtonGroup, Button } from '@nextui-org/react';
import {
SunDim as SunIcon,
@@ -17,7 +17,7 @@ const modesToIcons = {
const ThemeToggle = () => {
const { themeMode, setThemeMode } = useThemeMode();
- const screenSize = useScreenSize();
+ const { screenWidth } = useScreenWidth();
const handleThemeChange = (newThemeMode: ThemeMode) => () => {
setThemeMode(newThemeMode);
@@ -42,7 +42,7 @@ const ThemeToggle = () => {
isIconOnly
variant="flat"
color="secondary"
- size={screenSize > 1024 ? 'md' : 'sm'}
+ size={screenWidth > 1024 ? 'md' : 'sm'}
>
{
const user = useUser();
- const screenSize = useScreenSize();
+ const { screenWidth } = useScreenWidth();
const { showSnackbar } = useSnackbarsStore();
const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
const [authenticating, setAuthenticating] = React.useState(false);
@@ -114,16 +114,16 @@ const AuthModalButton = () => {
return (
<>
{user?.id ? (
- 1024 ? 'md' : 'sm'}>
+ 1024 ? 'md' : 'sm'}>
}
>
- {screenSize > 1024 && 'Account'}
+ {screenWidth > 1024 && 'Account'}
{
- it('should return a number', () => {
- const { result } = renderHook(() => useScreenSize());
- expect(typeof result.current).toBe('number');
- });
-
- it('should return the window innerWidth', () => {
- (window.innerWidth as number) = 1000;
- const { result } = renderHook(() => useScreenSize());
- expect(result.current).toEqual(1000);
- });
-
- it('should update the screen size when the window is resized', () => {
- (window.innerWidth as number) = 1024;
- const { result } = renderHook(() => useScreenSize());
- expect(result.current).toEqual(1024);
- fireEvent.resize(window, { target: { innerWidth: 1000 } });
- expect(result.current).toEqual(1000);
- });
-});
diff --git a/src/hooks/useScreenSize.ts b/src/hooks/useScreenSize.ts
deleted file mode 100644
index e396f7a..0000000
--- a/src/hooks/useScreenSize.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-
-const useScreenSize = () => {
- const [screenSize, setScreenSize] = React.useState(() => {
- return window.innerWidth;
- });
-
- React.useEffect(() => {
- const handleResize = (event: UIEvent) => {
- setScreenSize((event.target as typeof window)?.innerWidth);
- };
-
- window.addEventListener('resize', handleResize);
-
- return () => {
- window.removeEventListener('resize', handleResize);
- };
- }, []);
-
- return screenSize;
-};
-
-export default useScreenSize;
diff --git a/src/hooks/useScreenWidth.test.ts b/src/hooks/useScreenWidth.test.ts
new file mode 100644
index 0000000..8ffc10f
--- /dev/null
+++ b/src/hooks/useScreenWidth.test.ts
@@ -0,0 +1,24 @@
+import { fireEvent, renderHook } from '@testing-library/react';
+
+import useScreenWidth from './useScreenWidth';
+
+describe(useScreenWidth.name, () => {
+ it('should return screenWidth as a number', () => {
+ const { result } = renderHook(() => useScreenWidth());
+ expect(typeof result.current.screenWidth).toBe('number');
+ });
+
+ it('should return the window innerWidth', () => {
+ (window.innerWidth as number) = 1000;
+ const { result } = renderHook(() => useScreenWidth());
+ expect(result.current.screenWidth).toEqual(1000);
+ });
+
+ it('should update the screen size when the window is resized', () => {
+ (window.innerWidth as number) = 1024;
+ const { result } = renderHook(() => useScreenWidth());
+ expect(result.current.screenWidth).toEqual(1024);
+ fireEvent.resize(window, { target: { innerWidth: 1000 } });
+ expect(result.current.screenWidth).toEqual(1000);
+ });
+});
diff --git a/src/hooks/useScreenWidth.ts b/src/hooks/useScreenWidth.ts
new file mode 100644
index 0000000..9f7f0cd
--- /dev/null
+++ b/src/hooks/useScreenWidth.ts
@@ -0,0 +1,29 @@
+import { tailwindConfig } from '@helpers';
+import React from 'react';
+
+const useScreenWidth = () => {
+ const [screenWidth, setScreenWidth] = React.useState(() => {
+ return window.innerWidth;
+ });
+
+ React.useEffect(() => {
+ const handleResize = (event: UIEvent) => {
+ setScreenWidth((event.target as typeof window)?.innerWidth);
+ };
+
+ window.addEventListener('resize', handleResize);
+
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ const { md, lg } = tailwindConfig.theme.screens;
+
+ const isMobile = screenWidth < Number(md.slice(0, -2));
+ const isDesktop = screenWidth >= Number(lg.slice(0, -2));
+
+ return { screenWidth, isMobile, isDesktop };
+};
+
+export default useScreenWidth;
diff --git a/src/stores/occurrences.store.ts b/src/stores/occurrences.store.ts
index b52eb76..95a5af9 100644
--- a/src/stores/occurrences.store.ts
+++ b/src/stores/occurrences.store.ts
@@ -159,7 +159,7 @@ const useOccurrencesStore = create((set, get) => {
),
}));
},
- onRangeChange: (range: [number, number]) => set({ range }),
+ onRangeChange: (range: [number, number]) => set(() => ({ range })),
updateOccurrences: (
allOccurrences: Occurrence[],
filteredBy: OccurrenceFilters
diff --git a/tests/jest.config.js b/tests/jest.config.js
index 3725769..cbd67a7 100644
--- a/tests/jest.config.js
+++ b/tests/jest.config.js
@@ -14,6 +14,7 @@ export default {
'@models': '/src/models',
'@stores': '/src/stores',
'@tests': '/tests',
+ '@root/tailwind.config': '/tailwind.config.ts',
},
transform: {
'\\.[jt]sx?$': 'babel-jest',
diff --git a/tsconfig.json b/tsconfig.json
index 08f251f..96e68f6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -26,7 +26,8 @@
"@helpers": ["helpers"],
"@models": ["models"],
"@stores": ["stores"],
- "@tests": ["../tests"]
+ "@tests": ["../tests"],
+ "@root/*": ["../*"]
}
},
"include": ["src", "tests", "./supabase/database.types.ts"]
diff --git a/vite.config.ts b/vite.config.ts
index cbebaa8..5f163b7 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -32,6 +32,7 @@ export default defineConfig(({ mode }) => {
'@models': resolve(__dirname, './src/models'),
'@stores': resolve(__dirname, './src/stores'),
'@tests': resolve(__dirname, './tests'),
+ '@root': resolve(__dirname, './'),
},
},
build: {
- {weekDayName} -
++ {weekDayName} +
++ {day.getDate()} +
++
{hour !== 0 && hour}
)} -