Link project titles to detail page, add inline project name edit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-03-27 00:57:52 -04:00
parent 0371e3eba5
commit 7000b5a840
3 changed files with 48 additions and 10 deletions
+1 -1
View File
@@ -82,7 +82,7 @@ export default function ClientDashboard() {
const pendingReview = projectTasks.filter(t => t.status === 'client_review').length; const pendingReview = projectTasks.filter(t => t.status === 'client_review').length;
const active = projectTasks.filter(t => ['in_progress', 'on_hold', 'not_started'].includes(t.status)).length; const active = projectTasks.filter(t => ['in_progress', 'on_hold', 'not_started'].includes(t.status)).length;
return ( return (
<Link key={project.id} to="/my-projects" style={{ textDecoration: 'none' }}> <Link key={project.id} to={`/my-projects/${project.id}`} style={{ textDecoration: 'none' }}>
<div style={{ <div style={{
padding: '12px 16px', padding: '12px 16px',
border: `1px solid ${pendingReview > 0 ? 'var(--accent)' : 'var(--border)'}`, border: `1px solid ${pendingReview > 0 ? 'var(--accent)' : 'var(--border)'}`,
+37 -3
View File
@@ -16,7 +16,10 @@ export default function MyProjectDetail() {
const [tasks, setTasks] = useState([]); const [tasks, setTasks] = useState([]);
const [submissions, setSubmissions] = useState([]); const [submissions, setSubmissions] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [filter, setFilter] = useState('all'); // 'all' | 'mine' const [filter, setFilter] = useState('all');
const [editingName, setEditingName] = useState(false);
const [nameVal, setNameVal] = useState('');
const [savingName, setSavingName] = useState(false);
useEffect(() => { useEffect(() => {
async function load() { async function load() {
@@ -42,6 +45,16 @@ export default function MyProjectDetail() {
load(); load();
}, [id]); }, [id]);
const handleSaveName = async (e) => {
e.preventDefault();
if (!nameVal.trim()) return;
setSavingName(true);
await supabase.from('projects').update({ name: nameVal.trim() }).eq('id', id);
setProject(p => ({ ...p, name: nameVal.trim() }));
setEditingName(false);
setSavingName(false);
};
if (loading) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p></Layout>; if (loading) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p></Layout>;
if (!project) return <Layout><p>Project not found.</p></Layout>; if (!project) return <Layout><p>Project not found.</p></Layout>;
@@ -58,17 +71,38 @@ export default function MyProjectDetail() {
<div className="page-header"> <div className="page-header">
<div> <div>
{editingName ? (
<form onSubmit={handleSaveName} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<input
type="text"
value={nameVal}
onChange={e => setNameVal(e.target.value)}
autoFocus
required
style={{ fontSize: 22, fontWeight: 700, padding: '4px 10px', margin: 0, width: 260 }}
/>
<button type="submit" className="btn btn-primary btn-sm" disabled={savingName}>{savingName ? '...' : 'Save'}</button>
<button type="button" className="btn btn-outline btn-sm" onClick={() => setEditingName(false)}>Cancel</button>
</form>
) : (
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div className="page-title">{project.name}</div> <div className="page-title">{project.name}</div>
<button
className="btn btn-outline btn-sm"
onClick={() => { setNameVal(project.name); setEditingName(true); }}
>
Edit
</button>
</div>
)}
<div className="page-subtitle"> <div className="page-subtitle">
{tasks.length} request{tasks.length !== 1 ? 's' : ''} · Started {new Date(project.created_at).toLocaleDateString()} {tasks.length} request{tasks.length !== 1 ? 's' : ''} · Started {new Date(project.created_at).toLocaleDateString()}
</div> </div>
</div> </div>
<div style={{ display: 'flex', gap: 8 }}>
<Link to={`/new-request?project=${encodeURIComponent(project.name)}`} className="btn btn-primary"> <Link to={`/new-request?project=${encodeURIComponent(project.name)}`} className="btn btn-primary">
+ Add Request + Add Request
</Link> </Link>
</div> </div>
</div>
{/* Filter toggle */} {/* Filter toggle */}
<div style={{ display: 'flex', gap: 8, marginBottom: 20 }}> <div style={{ display: 'flex', gap: 8, marginBottom: 20 }}>
+6 -2
View File
@@ -33,9 +33,13 @@ function ProjectGroup({ project, tasks, submissions, currentUserId, filter }) {
}} }}
> >
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span style={{ fontSize: 14, fontWeight: 700, color: 'var(--text-primary)' }}> <Link
to={`/my-projects/${project.id}`}
onClick={e => e.stopPropagation()}
style={{ fontSize: 14, fontWeight: 700, color: 'var(--text-primary)', textDecoration: 'none' }}
>
{project.name} {project.name}
</span> </Link>
<span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 4, background: 'rgba(245,165,35,0.15)', color: 'var(--accent)' }}> <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 4, background: 'rgba(245,165,35,0.15)', color: 'var(--accent)' }}>
{filteredTasks.length} request{filteredTasks.length !== 1 ? 's' : ''} {filteredTasks.length} request{filteredTasks.length !== 1 ? 's' : ''}
</span> </span>