Skip to content

Commit da93ab5

Browse files
committed
chore: Update Projects page
1 parent 93b314a commit da93ab5

File tree

1 file changed

+50
-2
lines changed

1 file changed

+50
-2
lines changed

src/pages/Projects.tsx

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useState, useMemo, useRef } from "react";
22
import { useNavigate } from "react-router-dom";
33
import { useAuth } from "../context/AuthContext";
4-
import { getUserProjects, deleteProject, renameProject, duplicateProject } from "../services/projectService";
4+
import { getUserProjects, deleteProject, renameProject, duplicateProject, toggleProjectFeatured } from "../services/projectService";
55
import type { Project } from "../services/projectService";
66

77
type SortOption = "date-desc" | "date-asc" | "name";
@@ -130,14 +130,20 @@ function ProjectMenu({
130130
onRename,
131131
onDuplicate,
132132
onDelete,
133-
isDeleting
133+
onToggleFeatured,
134+
isDeleting,
135+
isFeatured,
136+
isFeaturingLoading
134137
}: {
135138
isOpen: boolean;
136139
onClose: () => void;
137140
onRename: () => void;
138141
onDuplicate: () => void;
139142
onDelete: () => void;
143+
onToggleFeatured: () => void;
140144
isDeleting: boolean;
145+
isFeatured: boolean;
146+
isFeaturingLoading: boolean;
141147
}) {
142148
const menuRef = useRef<HTMLDivElement>(null);
143149

@@ -178,6 +184,26 @@ function ProjectMenu({
178184
</svg>
179185
Duplicate
180186
</button>
187+
<button
188+
onClick={(e) => { e.stopPropagation(); onToggleFeatured(); }}
189+
disabled={isFeaturingLoading}
190+
className={`w-full px-3 py-2 text-left text-sm transition-colors flex items-center gap-2 disabled:opacity-50 ${isFeatured
191+
? "text-yellow-400 hover:bg-yellow-500/10 hover:text-yellow-300"
192+
: "text-slate-300 hover:bg-slate-700 hover:text-white"
193+
}`}
194+
>
195+
{isFeaturingLoading ? (
196+
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
197+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
198+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
199+
</svg>
200+
) : (
201+
<svg className="w-4 h-4" fill={isFeatured ? "currentColor" : "none"} viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
202+
<path strokeLinecap="round" strokeLinejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
203+
</svg>
204+
)}
205+
{isFeatured ? "Remove from Profile" : "Feature on Profile"}
206+
</button>
181207
<div className="border-t border-slate-700 my-1" />
182208
<button
183209
onClick={(e) => { e.stopPropagation(); onDelete(); }}
@@ -208,6 +234,7 @@ function Projects() {
208234
const [error, setError] = useState<string | null>(null);
209235
const [deletingId, setDeletingId] = useState<string | null>(null);
210236
const [duplicatingId, setDuplicatingId] = useState<string | null>(null);
237+
const [featuringId, setFeaturingId] = useState<string | null>(null);
211238

212239
// Search and Sort State
213240
const [searchQuery, setSearchQuery] = useState("");
@@ -346,6 +373,24 @@ function Projects() {
346373
}
347374
};
348375

376+
const handleToggleFeatured = async (project: Project) => {
377+
setFeaturingId(project.id);
378+
setOpenMenuId(null);
379+
try {
380+
await toggleProjectFeatured(project.id, !project.isFeatured);
381+
setProjects((prev) =>
382+
prev.map((p) =>
383+
p.id === project.id ? { ...p, isFeatured: !p.isFeatured } : p
384+
)
385+
);
386+
} catch (err) {
387+
console.error("Error toggling featured:", err);
388+
alert("Failed to update featured status.");
389+
} finally {
390+
setFeaturingId(null);
391+
}
392+
};
393+
349394
const formatDate = (timestamp: number | { toDate: () => Date } | null) => {
350395
if (!timestamp) return "Never";
351396

@@ -543,7 +588,10 @@ function Projects() {
543588
onRename={() => handleRenameClick(project)}
544589
onDuplicate={() => handleDuplicate(project)}
545590
onDelete={() => handleDelete(project.id)}
591+
onToggleFeatured={() => handleToggleFeatured(project)}
546592
isDeleting={deletingId === project.id}
593+
isFeatured={project.isFeatured ?? false}
594+
isFeaturingLoading={featuringId === project.id}
547595
/>
548596
</div>
549597
</div>

0 commit comments

Comments
 (0)