Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
38e70ec
resolve heatmap-grid-styles
Abhishek2005-ard May 27, 2026
bb580ad
resolve standardize-form-errors
Abhishek2005-ard May 27, 2026
dc2b77b
Merge branch 'main' into ui/standardize-form-errors
Abhishek2005-ard May 27, 2026
69becc8
fix: update ci node version and correct tailwind css syntax
Abhishek2005-ard May 27, 2026
7ac84ac
fix: resolve unclosed jsx tags in Login and Signup
Abhishek2005-ard May 27, 2026
b044c38
routine-builder-persistence
Abhishek2005-ard May 27, 2026
61afa5a
adjust dashboard welcome card vertical positioning
Abhishek2005-ard May 28, 2026
b87e4d2
duplicate-password-icons
Abhishek2005-ard May 28, 2026
04a4085
Merge branch 'main' into fix/duplicate-password-icons
Abhishek2005-ard May 28, 2026
e7b4a54
fix(ui): clean up duplicate index.css and hide default browser passwo…
Abhishek2005-ard May 28, 2026
a0606f6
resove ci check
Abhishek2005-ard May 28, 2026
d03c099
fix(ui): remove undefined buttonRef and clean up Login redundant markup
Abhishek2005-ard May 28, 2026
b3bfa89
fix(auth): remove redundant /auth/me call on signup that causes false…
Abhishek2005-ard May 28, 2026
84cf1dc
fix(ui): prevent task form modal from closing on accidental backdrop …
Abhishek2005-ard May 28, 2026
0ae6cb2
feat: implement Forge Mode Pomodoro deep work environment with MERN i…
Abhishek2005-ard May 29, 2026
87106a9
fix: resolve React Rules of Hooks violation in Navbar early return
Abhishek2005-ard May 29, 2026
6a2ee5e
fix: resolve misleading empty state in TaskLibrary search
Abhishek2005-ard May 31, 2026
15f7ae9
Merge branch 'main' into fix/task-library-search-ux
Abhishek2005-ard May 31, 2026
cc7f3e9
fix: resolve react hook dependencies and unused variables in ForgeMode
Abhishek2005-ard May 31, 2026
514c06b
remove duplicate 2fa check resolve
Abhishek2005-ard Jun 8, 2026
d25ae6e
authcontext session restore
Abhishek2005-ard Jun 8, 2026
9bba2d1
routine schema adaptive settings
Abhishek2005-ard Jun 8, 2026
ad5e872
Merge branch 'main' into fix/routine-schema-adaptive-settings
Abhishek2005-ard Jun 8, 2026
3dc9944
Merge branch 'main' into fix/routine-schema-adaptive-settings
aryandas2911 Jun 9, 2026
461d281
Merge branch 'main' into fix/routine-schema-adaptive-settings
Abhishek2005-ard Jun 10, 2026
1b560a7
fix: revert ci.yml node version change, keep node 18 matrix as per ma…
Abhishek2005-ard Jun 10, 2026
46897c6
fix: move tags default into field definition to fix rogue schema fiel…
Abhishek2005-ard Jun 10, 2026
3956a75
ci: split into separate backend and frontend jobs (node 18) to satisf…
Abhishek2005-ard Jun 10, 2026
85676cd
fix: render AnimatedRoutes in App, add missing ForgeMode import and m…
Abhishek2005-ard Jun 10, 2026
b13f135
Merge branch 'main' into fix/animated-routes-not-rendered
Abhishek2005-ard Jun 13, 2026
a022e89
Merge branch 'main' into fix/animated-routes-not-rendered
Abhishek2005-ard Jun 13, 2026
be56288
fix: render animated routes and resolve lint errors
Abhishek2005-ard Jun 13, 2026
26dc596
Merge branch 'fix/animated-routes-not-rendered' of https://github.com…
Abhishek2005-ard Jun 13, 2026
81b0d92
Merge branch 'main' into fix/animated-routes-not-rendered
aryandas2911 Jun 16, 2026
4ca6026
Fix routes not rendering by using AnimatedRoutes in App component
Abhishek2005-ard Jun 16, 2026
9b9b772
fix: restore missing LandingPage route and ensure all routes use Page…
Abhishek2005-ard Jun 16, 2026
3f26908
fix: resolve conflicts with upstream main and preserve AnimatedRoutes
Abhishek2005-ard Jun 16, 2026
c1c1837
fix(security): resolve IDOR in getPublicRoutine by introducing isPubl…
Abhishek2005-ard Jun 17, 2026
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
9 changes: 5 additions & 4 deletions backend/controllers/routineController.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,13 @@ export const updateRoutine = async (req, res) => {
}

// fetch updated routine details
const { name, description, items } = req.body;
const { name, description, items, isPublic } = req.body;

const updates = {
...(name && { name }),
...(description && { description }),
...(items && { items }),
...(isPublic !== undefined && { isPublic }),
};
const routineId = req.params.id;

Expand Down Expand Up @@ -430,10 +431,10 @@ export const getPublicRoutine = async (req, res) => {
try {
const routineId = req.params.id;
const routine = await Routine.findById(routineId).populate("items.taskId");
if (!routine) {
return res.status(404).json({
if (!routine || !routine.isPublic) {
return res.status(403).json({
success: false,
message: "Routine not found",
message: "Routine not found or not public",
});
}
return res.status(200).json({ success: true, routine });
Expand Down
4 changes: 4 additions & 0 deletions backend/src/models/Routine.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const routineSchema = mongoose.Schema(
required: true,
trim: true
},
isPublic: {
type: Boolean,
default: false,
},
description: {
type: String,
required: false,
Expand Down
169 changes: 55 additions & 114 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import ErrorBoundary from "./components/ErrorBoundary.jsx";
import PageTransition from "./components/PageTransition.jsx";
import ShareRoutine from "./pages/ShareRoutine.jsx";
import DailyJournal from "./pages/DailyJournal.jsx";
import ForgeMode from "./pages/ForgeMode.jsx";

const AuthLayout = ({ children }) => (
<div className="min-h-[calc(100vh-3.75rem)] flex items-center justify-center">
Expand All @@ -38,38 +39,44 @@ const AnimatedRoutes = () => {
path="/"
element={
<PublicRoute>
<AuthLayout>
<Login />
</AuthLayout>
<PageTransition>
<LandingPage />
</PageTransition>
</PublicRoute>
}
/>
<Route
path="/login"
element={
<PublicRoute>
<AuthLayout>
<Login />
</AuthLayout>
<PageTransition>
<AuthLayout>
<Login />
</AuthLayout>
</PageTransition>
</PublicRoute>
}
/>
<Route
path="/signup"
element={
<PublicRoute>
<AuthLayout>
<Signup />
</AuthLayout>
<PageTransition>
<AuthLayout>
<Signup />
</AuthLayout>
</PageTransition>
</PublicRoute>
}
/>
<Route
path="/about"
element={
<AuthLayout>
<About />
</AuthLayout>
<PageTransition>
<AuthLayout>
<About />
</AuthLayout>
</PageTransition>
}
/>
<Route
Expand Down Expand Up @@ -108,6 +115,16 @@ const AnimatedRoutes = () => {
</ProtectedRoutes>
}
/>
<Route
path="/focus-mode"
element={
<ProtectedRoutes>
<PageTransition>
<Pomodoro />
</PageTransition>
</ProtectedRoutes>
}
/>
<Route
path="/profile"
element={
Expand All @@ -133,7 +150,9 @@ const AnimatedRoutes = () => {
element={
<ProtectedRoutes>
<ErrorBoundary>
<PageTransition><DailyJournal /></PageTransition>
<PageTransition>
<DailyJournal />
</PageTransition>
</ErrorBoundary>
</ProtectedRoutes>
}
Expand All @@ -142,19 +161,38 @@ const AnimatedRoutes = () => {
path="/forge"
element={
<ProtectedRoutes>
<PageTransition><ForgeMode /></PageTransition>
<PageTransition>
<ForgeMode />
</PageTransition>
</ProtectedRoutes>
}
/>
<Route
path="/focus"
element={
<ProtectedRoutes>
<PageTransition><ForgeMode /></PageTransition>
<PageTransition>
<ForgeMode />
</PageTransition>
</ProtectedRoutes>
}
/>
<Route path="*" element={<NotFound />} />
<Route
path="/share/routine/:id"
element={
<PageTransition>
<ShareRoutine />
</PageTransition>
}
/>
<Route
path="*"
element={
<PageTransition>
<NotFound />
</PageTransition>
}
/>
</Routes>

</AnimatePresence>
Expand All @@ -166,104 +204,7 @@ const App = () => {
<BrowserRouter>
<Navbar />
<main className="app-bg min-h-screen pt-24 sm:pt-28 flex flex-col text-main transition-colors duration-300">
<Routes>
<Route
path="/"
element={
<PublicRoute>
<AuthLayout>
<Login />
</AuthLayout>
</PublicRoute>
}
/>
<Route
path="/login"
element={
<PublicRoute>
<AuthLayout>
<Login />
</AuthLayout>
</PublicRoute>
}
/>
<Route
path="/signup"
element={
<PublicRoute>
<AuthLayout>
<Signup />
</AuthLayout>
</PublicRoute>
}
/>
<Route
path="/about"
element={
<AuthLayout>
<About />
</AuthLayout>
}
/>
<Route
path="/dashboard"
element={
<ProtectedRoutes>
<Dashboard />
</ProtectedRoutes>
}
/>
<Route
path="/tasks"
element={
<ProtectedRoutes>
<Tasks />
</ProtectedRoutes>
}
/>
<Route
path="/routine-builder"
element={
<ProtectedRoutes>
<RoutineBuilder />
</ProtectedRoutes>
}
/>
<Route
path="/focus-mode"
element={
<ProtectedRoutes>
<Pomodoro />
</ProtectedRoutes>
}
/>
<Route
path="/profile"
element={
<ProtectedRoutes>
<Profile />
</ProtectedRoutes>
}
/>
<Route
path="/analytics"
element={
<ProtectedRoutes>
<Analytics />
</ProtectedRoutes>
}
/>
<Route
path="/daily-journal"
element={
<ProtectedRoutes>
<DailyJournal />
</ProtectedRoutes>
}
/>
<Route path="/share/routine/:id" element={<ShareRoutine />} />
<Route path="*" element={<NotFound />} />
</Routes>
<AnimatedRoutes />
</main>
<Footer />
<ScrollToTop />
Expand Down
28 changes: 27 additions & 1 deletion frontend/src/components/Routine/RoutineCard.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { MoreVertical, Trash2, Share2, Copy, Download, Loader2 } from "lucide-react";
import { MoreVertical, Trash2, Share2, Copy, Download, Loader2, EyeOff } from "lucide-react";
import RoutineOverviewModal from "./RoutineOverviewModal";
import api from "../../api/axios.js";
import { exportRoutineToPDF, generateRoutineSummary } from "../../utils/routineExport.js";
Expand Down Expand Up @@ -36,6 +36,10 @@ export default function RoutineCard({
e.stopPropagation();
setShowMenu(false);
try {
if (!routine.isPublic) {
await api.put(`/routines/${routine._id}`, { isPublic: true });
await fetchRoutines();
}
const shareUrl = `${window.location.origin}/share/routine/${routine._id}`;
await navigator.clipboard.writeText(shareUrl);
triggerToast("Share Link Copied", "The public routine link was copied to clipboard.");
Expand All @@ -45,6 +49,19 @@ export default function RoutineCard({
}
};

const handleRevokeLink = async (e) => {
e.stopPropagation();
setShowMenu(false);
try {
await api.put(`/routines/${routine._id}`, { isPublic: false });
await fetchRoutines();
triggerToast("Link Revoked", "The routine is now private.");
} catch (err) {
console.error(err);
alert("Failed to revoke share link");
}
};

const handleCopySummary = async (e) => {
e.stopPropagation();
setShowMenu(false);
Expand Down Expand Up @@ -414,6 +431,15 @@ export default function RoutineCard({
<Share2 size={16} />
Copy Share Link
</button>
{routine.isPublic && (
<button
onClick={handleRevokeLink}
className="w-full flex items-center gap-3 px-4 py-3 text-sm text-main hover:bg-slate-100 dark:hover:bg-slate-800 transition font-medium cursor-pointer"
>
<EyeOff size={16} />
Revoke Share Link
</button>
)}
<button
onClick={handleCopySummary}
className="w-full flex items-center gap-3 px-4 py-3 text-sm text-main hover:bg-slate-100 dark:hover:bg-slate-800 transition font-medium cursor-pointer"
Expand Down
Loading
Loading