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:
@@ -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)'}`,
|
||||||
|
|||||||
@@ -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 }}>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user