Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,809 changes: 1,308 additions & 1,501 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
"react-router-dom": "^6.27.0",
"react-scripts": "5.0.1",
"react-scripts": "^5.0.1",
"workbox-background-sync": "^6.6.0",
"workbox-broadcast-update": "^6.6.0",
"workbox-cacheable-response": "^6.6.0",
Expand Down Expand Up @@ -53,4 +53,4 @@
"last 1 safari version"
]
}
}
}
93 changes: 40 additions & 53 deletions src/App.js

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WeeklySummary and HeatMap belong in MainPage, not here in App routes. Please move them there. Also, if you start the app from scratch with no habits, your components appear on top of the "create first habit" banner. Please fix this bug.

ps: Please don't change formatting (tabs and empty lines) because it adds unnecessary noise to the PR.

Original file line number Diff line number Diff line change
@@ -1,69 +1,56 @@
// src/App.js
import './App.css';

// router
import { Routes, Route, useLocation, Navigate } from 'react-router-dom';

// framer
import { AnimatePresence } from 'framer-motion';

// stores
import { useDialogStore } from './stores/dialogStore';

// main components
import MainPage from './components/MainPage';
import Modal from './components/Modal';
import Dialog from './components/Containment/Dialog';

// hooks
import useColorScheme from './hooks/useColorScheme';
import useAchievementsCheck from './hooks/useAchievementsCheck';

// db
import dbModalRoutes from './db/dbModalRoutes';

// NEW COMPONENTS
import WeeklySummary from './components/WeeklySummary';
import HeatMap from './components/HeatMap';

const PUBLIC_URL = process.env.PUBLIC_URL;

function App() {

const location = useLocation();
const isDialogVisible = useDialogStore((s) => s.isVisible);

// Get colors from database based on settings or system theme
useColorScheme();

// Check achievements when dependencies change
useAchievementsCheck();

return (
<main className="App">
<AnimatePresence initial={false}>
<Routes location={location} key={location.pathname}>
<Route
path='*'
element={<Navigate to={PUBLIC_URL} />}
/>

<Route
path={PUBLIC_URL}
element={<MainPage />}
/>

<Route
path={`${PUBLIC_URL}/modal`}
element={<Modal />}
>
{dbModalRoutes.map((r) => (
<Route key={r.path} path={r.path} element={r.element} />
))}
</Route>
</Routes>

{isDialogVisible && (
<Dialog key="dialog" />
)}
</AnimatePresence>
</main>
);
const location = useLocation();
const isDialogVisible = useDialogStore((s) => s.isVisible);

useColorScheme();
useAchievementsCheck();

return (
<main className="App">
<AnimatePresence initial={false}>
<Routes location={location} key={location.pathname}>
<Route path="*" element={<Navigate to={PUBLIC_URL} />} />

<Route
path={PUBLIC_URL}
element={
<>
<WeeklySummary />
<HeatMap />
<MainPage />
</>
}
/>

<Route path={`${PUBLIC_URL}/modal`} element={<Modal />}>
{dbModalRoutes.map((r) => (
<Route key={r.path} path={r.path} element={r.element} />
))}
</Route>
</Routes>

{isDialogVisible && <Dialog key="dialog" />}
</AnimatePresence>
</main>
);
}

export default App;
export default App;
185 changes: 185 additions & 0 deletions src/components/HeatMap.css

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please redo the styles to match our Habit component and ensure proper Dark Mode support. Update the selectors to use the new .level-x classes and fix the layout to be a horizontal grid instead of a vertical list. Avoid unnecessary borders and use tabs for indentation.

Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/* src/components/Heatmap.css */
.hm-wrapper {
border: 1px solid #e6e6e6;
padding: 12px;
border-radius: 8px;
background: #fff;
max-width: 720px;
margin-bottom: 12px;
}/* src/components/HeatMap.css */
.hm-wrapper {
border: 1px solid #e6e6e6;
padding: 12px;
border-radius: 8px;
background: #fff;
max-width: 720px;
margin-bottom: 12px;
}

.hm-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}

.hm-header h4 {
margin: 0;
font-size: 14px;
}

.hm-legend {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: #444;
}

.legend-dot {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 2px;
margin-right: 4px;
vertical-align: middle;
}
.legend-dot.none {
background: #f0f0f0;
border: 1px solid #e0e0e0;
}
.legend-dot.light {
background: #d6f5d6;
}
.legend-dot.medium {
background: #66d966;
}
.legend-dot.strong {
background: #238823;
}

.hm-grid {
display: flex;
gap: 6px;
align-items: stretch;
overflow-x: auto;
padding-bottom: 6px;
}

/* one horizontal row for now */
.hm-row {
display: flex;
flex-direction: row;
gap: 6px;
}

/* cell base */
.hm-cell {
width: 14px;
height: 14px;
border-radius: 2px;
box-sizing: border-box;
border: 1px solid transparent;
}
.hm-cell.none {
background: #f0f0f0;
border: 1px solid #eaeaea;
}
.hm-cell.light {
background: #d6f5d6;
}
.hm-cell.medium {
background: #66d966;
}
.hm-cell.strong {
background: #238823;
}

@media (max-width: 520px) {
.hm-cell {
width: 12px;
height: 12px;
}
.hm-wrapper {
padding: 10px;
}
}


.hm-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}

.hm-header h4 {
margin: 0;
font-size: 14px;
}

.hm-legend {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: #444;
}

.legend-dot {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 2px;
margin-right: 4px;
vertical-align: middle;
}
.legend-dot.none { background: #f0f0f0; border: 1px solid #e0e0e0; }
.legend-dot.light { background: #d6f5d6; }
.legend-dot.medium { background: #66d966; }
.legend-dot.strong { background: #238823; }

.hm-grid {
display: flex;
gap: 6px;
align-items: stretch;
overflow-x: auto;
padding-bottom: 6px;
}

/* each column is a vertical stack for weekdays */
.hm-row {
display: flex;
flex-direction: column;
gap: 6px;
}

/* cell base */
.hm-cell {
width: 14px;
height: 14px;
border-radius: 2px;
box-sizing: border-box;
border: 1px solid transparent;
}
.hm-cell.none {
background: #f0f0f0;
border: 1px solid #eaeaea;
}
.hm-cell.light {
background: #d6f5d6;
}
.hm-cell.medium {
background: #66d966;
}
.hm-cell.strong {
background: #238823;
}

/* responsive tweaks */
@media (max-width: 520px) {
.hm-cell { width: 12px; height: 12px; }
.hm-wrapper { padding: 10px; }
}
43 changes: 43 additions & 0 deletions src/components/HeatMap.js

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HeatMap is a cool idea, but the logic and UI need a lot of work.

Logic: Your current calculation is wrong. If I have 3 habits with frequency 2, and I do each one once, I finished 0 habits. But your map shows "3" and marks me as a hero. We should count only fully completed habits (where progress >= frequency).

Calculation: Using a percentage (ratio) is also problematic because archiving or adding new habits will break the old history. Let's just count the absolute number of completed habits per day and use that for the levels (e.g., 1, 2, 3+).

If you disagree, explain your point of view.

UI/UX: Did you test this in the browser?

  • It doesn't support dark mode.
  • The borders don't match our design.
  • The cells are vertical and take up the whole screen, so I can't even see the habits.

ps: I left other comments in the file.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// src/components/HeatMap.js

import { useHabitsStore } from "../stores/habitsStore";
import { getPastDays } from "../utils/dateUtils";
import "./HeatMap.css";

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop summing raw progress. We only count a habit as "done" if progress >= frequency.

const DAYS_COUNT = 30;
const days = getPastDays(DAYS_COUNT);

export default function HeatMap() {
	const habits = useHabitsStore((s) => s.habits ?? []);
	const activity = {}

	habits.forEach((habit) => {
		habit.completedDays?.forEach((d, i) => {
			// Only check the latest 30 entries
			if (i >= DAYS_COUNT) return;

			const isCompleted = d.progress >= habit.frequency;
			if (isCompleted) {
				activity[d.date] = (activity[d.date] || 0) + 1;
			}
		});
	});

    ...
}

export default function HeatMap() {
const habits = useHabitsStore((s) => s.habits || []);

const days = getPastDays(30);

const activity = {};

habits.forEach((habit) => {
habit.completedDays?.forEach((d) => {
activity[d.date] = (activity[d.date] || 0) + (d.progress || 0);
});
});

return (
<div className="hm-wrapper">
<div className="hm-header">
<h4>Activity — last 30 days</h4>
</div>

<div className="hm-grid">
<div className="hm-row">
{days.map((d) => {
const value = activity[d] || 0;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI: Fixed the cell classes to use level-X format. Please fix the CSS: the grid should be horizontal, support dark mode, and remove the unnecessary borders.

	const completedCount = activity[d] ?? 0;
	let level = 0;

	if (completedCount === 1) level = 1;
	else if (completedCount === 2) level = 2;
	else if (completedCount >= 3) level = 3;

	return <div key={d} className={`hm-cell level-${level}`} />;


let cls = "hm-cell none";

if (value === 1) cls = "hm-cell light";
if (value === 2) cls = "hm-cell medium";
if (value >= 3) cls = "hm-cell strong";

return <div key={d} className={cls} />;
})}
</div>
</div>
</div>
);
}
Loading