Refactor: clients → companies schema v2
This commit is contained in:
Executable
+122
@@ -0,0 +1,122 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Layout from '../../components/Layout';
|
||||
import StatusBadge from '../../components/StatusBadge';
|
||||
import { supabase } from '../../lib/supabase';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
|
||||
export default function MyRequests() {
|
||||
const { currentUser } = useAuth();
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [submissions, setSubmissions] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
// Only fetch tasks where current user was the original submitter
|
||||
const { data: mySubs } = await supabase
|
||||
.from('submissions')
|
||||
.select('task_id, submitted_by_name, version_number, type')
|
||||
.eq('submitted_by', currentUser.id)
|
||||
.eq('type', 'initial');
|
||||
|
||||
if (!mySubs || mySubs.length === 0) { setLoading(false); return; }
|
||||
|
||||
const myTaskIds = mySubs.map(s => s.task_id);
|
||||
|
||||
const { data: t } = await supabase
|
||||
.from('tasks').select('*, project:projects(id, name, created_at, status)')
|
||||
.in('id', myTaskIds);
|
||||
setTasks(t || []);
|
||||
|
||||
// Also fetch all submissions for these tasks (to show revision history)
|
||||
const { data: allSubs } = await supabase
|
||||
.from('submissions')
|
||||
.select('task_id, submitted_by_name, version_number, type')
|
||||
.in('task_id', myTaskIds)
|
||||
.order('version_number');
|
||||
setSubmissions(allSubs || []);
|
||||
|
||||
// Group tasks by project
|
||||
const projectMap = {};
|
||||
(t || []).forEach(task => {
|
||||
const p = task.project;
|
||||
if (!p) return;
|
||||
if (!projectMap[p.id]) projectMap[p.id] = { ...p, id: p.id };
|
||||
});
|
||||
setProjects(Object.values(projectMap));
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
load();
|
||||
}, [currentUser.id]);
|
||||
|
||||
if (loading) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p></Layout>;
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="page-header">
|
||||
<div>
|
||||
<div className="page-title">My Requests</div>
|
||||
<div className="page-subtitle">Requests you have submitted.</div>
|
||||
</div>
|
||||
<Link to="/new-request" className="btn btn-primary">+ New Request</Link>
|
||||
</div>
|
||||
|
||||
{projects.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<div className="empty-state-icon">📋</div>
|
||||
<h3>No requests yet</h3>
|
||||
<p>Submit a new request to get started.</p>
|
||||
<Link to="/new-request" className="btn btn-primary" style={{ marginTop: 16 }}>Submit Request</Link>
|
||||
</div>
|
||||
) : (
|
||||
projects.map(project => {
|
||||
const projectTasks = tasks.filter(t => t.project?.id === project.id);
|
||||
return (
|
||||
<div key={project.id} className="request-card">
|
||||
<div className="request-card-header">
|
||||
<div>
|
||||
<div className="request-card-title">{project.name}</div>
|
||||
<div className="request-card-meta">
|
||||
Started {new Date(project.created_at).toLocaleDateString()} · {projectTasks.length} job{projectTasks.length !== 1 ? 's' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<StatusBadge status={project.status} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{projectTasks.map(task => {
|
||||
const taskSubs = submissions.filter(s => s.task_id === task.id);
|
||||
const initialSub = taskSubs.find(s => s.type === 'initial') || taskSubs[0];
|
||||
const latestSub = taskSubs[taskSubs.length - 1];
|
||||
const hasRevision = taskSubs.length > 1 && latestSub?.submitted_by_name !== initialSub?.submitted_by_name;
|
||||
return (
|
||||
<div key={task.id} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'var(--bg)', borderRadius: 8 }}>
|
||||
<div>
|
||||
<span style={{ fontWeight: 600, fontSize: 13 }}>
|
||||
{task.title}{' '}
|
||||
<span style={{ fontWeight: 400, color: 'var(--text-muted)' }}>
|
||||
{'v' + String(task.current_version).padStart(2, '0')}
|
||||
</span>
|
||||
</span>
|
||||
<div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 2 }}>
|
||||
Submitted by {initialSub?.submitted_by_name || 'Unknown'}
|
||||
{hasRevision && ` · Last updated by ${latestSub.submitted_by_name}`}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<StatusBadge status={task.status} />
|
||||
<Link to={`/my-requests/${task.id}`} className="btn btn-outline btn-sm">Details</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user