๐
diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx
index 6b1fde3..3b7eafc 100755
--- a/src/context/AuthContext.jsx
+++ b/src/context/AuthContext.jsx
@@ -16,15 +16,19 @@ export function AuthProvider({ children }) {
const [loading, setLoading] = useState(true);
const fetchAndCacheProfile = async (authUser) => {
- const { data } = await supabase
- .from('profiles')
- .select('*, company:companies(id, name, phone, address)')
- .eq('id', authUser.id)
- .single();
- if (data) {
- const profile = { ...data, email: authUser.email };
- setCurrentUser(profile);
- localStorage.setItem(PROFILE_CACHE_KEY, JSON.stringify(profile));
+ try {
+ const { data } = await supabase
+ .from('profiles')
+ .select('*, company:companies(id, name, phone, address)')
+ .eq('id', authUser.id)
+ .single();
+ if (data) {
+ const profile = { ...data, email: authUser.email };
+ setCurrentUser(profile);
+ localStorage.setItem(PROFILE_CACHE_KEY, JSON.stringify(profile));
+ }
+ } catch (err) {
+ console.error('Profile fetch failed:', err);
}
};
diff --git a/src/pages/client/MyCompany.jsx b/src/pages/client/MyCompany.jsx
index a40ed27..33fa345 100644
--- a/src/pages/client/MyCompany.jsx
+++ b/src/pages/client/MyCompany.jsx
@@ -27,12 +27,13 @@ export default function MyCompany() {
const handleSave = async (e) => {
e.preventDefault();
setSaving(true);
- await supabase.from('companies').update({
+ const { error } = await supabase.from('companies').update({
name: form.name.trim(),
phone: form.phone.trim(),
address: form.address.trim(),
}).eq('id', company.id);
setSaving(false);
+ if (error) { alert('Failed to save. Please try again.'); return; }
setEditing(false);
};
diff --git a/src/pages/client/MyProjects.jsx b/src/pages/client/MyProjects.jsx
index 4493814..07aac2f 100755
--- a/src/pages/client/MyProjects.jsx
+++ b/src/pages/client/MyProjects.jsx
@@ -125,15 +125,11 @@ export default function MyProjects() {
useEffect(() => {
async function load() {
- const { data: p } = await supabase
- .from('projects').select('*').order('created_at', { ascending: false });
+ const [{ data: p }, { data: t }] = await Promise.all([
+ supabase.from('projects').select('*').order('created_at', { ascending: false }),
+ supabase.from('tasks').select('*').order('submitted_at', { ascending: false }),
+ ]);
setProjects(p || []);
-
- if (!p || p.length === 0) { setLoading(false); return; }
-
- const { data: t } = await supabase
- .from('tasks').select('*').in('project_id', p.map(pr => pr.id))
- .order('submitted_at', { ascending: false });
setTasks(t || []);
if (t && t.length > 0) {
diff --git a/src/pages/client/NewRequest.jsx b/src/pages/client/NewRequest.jsx
index 6139d6d..1752d51 100755
--- a/src/pages/client/NewRequest.jsx
+++ b/src/pages/client/NewRequest.jsx
@@ -16,6 +16,7 @@ export default function NewRequest() {
const [existingProjects, setExistingProjects] = useState([]);
const [submitted, setSubmitted] = useState(false);
const [saving, setSaving] = useState(false);
+ const [error, setError] = useState(null);
const [form, setForm] = useState({ project: preselectedProject, serviceType: '', title: '', deadline: '', description: '' });
const [files, setFiles] = useState([]);
const [customProjects, setCustomProjects] = useState([]);
@@ -24,16 +25,16 @@ export default function NewRequest() {
useEffect(() => {
async function load() {
- if (!currentUser.company_id) return;
+ if (!currentUser.company?.id) return;
const { data: p } = await supabase
.from('projects')
.select('id, name')
- .eq('company_id', currentUser.company_id)
+ .eq('company_id', currentUser.company.id)
.order('created_at', { ascending: false });
setExistingProjects((p || []).map(pr => ({ id: pr.id, name: pr.name })));
}
load();
- }, [currentUser.company_id]);
+ }, [currentUser.company?.id]);
const allProjectNames = [
...existingProjects.map(p => p.name),
@@ -64,11 +65,12 @@ export default function NewRequest() {
const handleSubmit = async (e) => {
e.preventDefault();
- if (!currentUser.company_id) {
+ if (!currentUser.company?.id) {
alert('Your account is not yet assigned to a company. Please contact support.');
return;
}
setSaving(true);
+ setError(null);
// Find existing project by name within this company, or create new one
let projectId;
@@ -76,12 +78,13 @@ export default function NewRequest() {
if (existing) {
projectId = existing.id;
} else {
- const { data: newProject } = await supabase.from('projects').insert({
- company_id: currentUser.company_id,
+ const { data: newProject, error: projectError } = await supabase.from('projects').insert({
+ company_id: currentUser.company.id,
name: form.project,
status: 'active',
}).select().single();
- projectId = newProject?.id;
+ if (projectError || !newProject) { setError('Failed to create project.'); setSaving(false); return; }
+ projectId = newProject.id;
}
if (!projectId) { setSaving(false); return; }
@@ -112,7 +115,12 @@ export default function NewRequest() {
if (submission && files.length > 0) {
for (const file of files) {
const path = `${task.id}/${Date.now()}_${file.name}`;
- const { data: uploaded } = await supabase.storage.from('submissions').upload(path, file);
+ const { data: uploaded, error: uploadError } = await supabase.storage.from('submissions').upload(path, file);
+ if (uploadError) {
+ setError(`Failed to upload "${file.name}": ${uploadError.message}`);
+ setSaving(false);
+ return;
+ }
if (uploaded) {
await supabase.from('submission_files').insert({
submission_id: submission.id,
@@ -139,7 +147,7 @@ export default function NewRequest() {
setSubmitted(true);
};
- if (!currentUser.company_id) {
+ if (!currentUser.company?.id) {
return (
@@ -251,6 +259,12 @@ export default function NewRequest() {
+ {error && (
+
+ {error}
+
+ )}
+
Submitting as {currentUser?.name} ยท {currentUser?.company?.name}
diff --git a/src/pages/client/RequestDetail.jsx b/src/pages/client/RequestDetail.jsx
index a8f868b..4962cd1 100755
--- a/src/pages/client/RequestDetail.jsx
+++ b/src/pages/client/RequestDetail.jsx
@@ -217,7 +217,7 @@ export default function RequestDetail() {
{action === 'confirm-delete' && (
-
+
โ Delete this request?
This will permanently delete {titleWithVersion} and all its history. This cannot be undone.
diff --git a/src/pages/team/Companies.jsx b/src/pages/team/Companies.jsx
index 0af6362..04dc8d3 100644
--- a/src/pages/team/Companies.jsx
+++ b/src/pages/team/Companies.jsx
@@ -44,7 +44,7 @@ export default function Companies() {
setSaving(false);
if (data) {
setShowNew(false);
- setNewForm({ name: '', email: '', phone: '' });
+ setNewForm({ name: '', phone: '', address: '' });
navigate(`/companies/${data.id}`);
}
};
diff --git a/src/pages/team/CompanyDetail.jsx b/src/pages/team/CompanyDetail.jsx
index 3bb575c..73e1c9e 100644
--- a/src/pages/team/CompanyDetail.jsx
+++ b/src/pages/team/CompanyDetail.jsx
@@ -25,27 +25,20 @@ export default function CompanyDetail() {
}, [id]);
async function load() {
- const [{ data: co }, { data: p }, { data: pr }, { data: u }, { data: unassignedUsers }] = await Promise.all([
+ const [{ data: co }, { data: p }, { data: pr }, { data: u }, { data: unassignedUsers }, { data: t }] = await Promise.all([
supabase.from('companies').select('*').eq('id', id).single(),
supabase.from('projects').select('*').eq('company_id', id).order('created_at', { ascending: false }),
supabase.from('company_prices').select('*').eq('company_id', id),
supabase.from('profiles').select('id, name, email, created_at').eq('company_id', id).eq('role', 'client'),
supabase.from('profiles').select('id, name, email').eq('role', 'client').is('company_id', null),
+ supabase.from('tasks').select('*, project:projects!inner(company_id)').eq('project.company_id', id),
]);
setCompany(co);
- const projectList = p || [];
- setProjects(projectList);
+ setProjects(p || []);
setPrices(pr || []);
setUsers(u || []);
setUnassigned(unassignedUsers || []);
-
- if (projectList.length > 0) {
- const { data: t } = await supabase
- .from('tasks')
- .select('*')
- .in('project_id', projectList.map(pr => pr.id));
- setTasks(t || []);
- }
+ setTasks(t || []);
setLoading(false);
}
@@ -86,11 +79,13 @@ export default function CompanyDetail() {
const priceVal = getPrice(serviceType);
const existing = prices.find(p => p.service_type === serviceType && p.id);
if (existing) {
- await supabase.from('company_prices').update({ price: Number(priceVal) }).eq('id', existing.id);
+ const { error: updateError } = await supabase.from('company_prices').update({ price: Number(priceVal) }).eq('id', existing.id);
+ if (updateError) { setSavingPrice(null); alert('Failed to save price. Please try again.'); return; }
} else {
- const { data } = await supabase.from('company_prices').insert({
+ const { data, error: insertError } = await supabase.from('company_prices').insert({
company_id: id, service_type: serviceType, price: Number(priceVal),
}).select().single();
+ if (insertError) { setSavingPrice(null); alert('Failed to save price. Please try again.'); return; }
if (data) setPrices(prev => prev.map(p => p.service_type === serviceType ? data : p));
}
setSavingPrice(null);
diff --git a/src/pages/team/InvoiceDetail.jsx b/src/pages/team/InvoiceDetail.jsx
index 5444cfc..18fc766 100644
--- a/src/pages/team/InvoiceDetail.jsx
+++ b/src/pages/team/InvoiceDetail.jsx
@@ -89,7 +89,6 @@ export default function InvoiceDetail() {
Bill To
{company?.name}
- {company?.email &&
{company.email}
}
View Company
diff --git a/src/pages/team/ProjectDetail.jsx b/src/pages/team/ProjectDetail.jsx
index 54daac7..be29bf0 100755
--- a/src/pages/team/ProjectDetail.jsx
+++ b/src/pages/team/ProjectDetail.jsx
@@ -56,7 +56,6 @@ export default function ProjectDetail() {
Project Info
{company?.name || 'โ'}
-
{company?.email || 'โ'}
{new Date(project.created_at).toLocaleDateString()}
diff --git a/src/pages/team/TaskDetail.jsx b/src/pages/team/TaskDetail.jsx
index 25a5f42..70c0fc9 100755
--- a/src/pages/team/TaskDetail.jsx
+++ b/src/pages/team/TaskDetail.jsx
@@ -37,18 +37,14 @@ export default function TaskDetail() {
setTask(t);
const [{ data: p }, { data: subs }, { data: team }] = await Promise.all([
- supabase.from('projects').select('*').eq('id', t.project_id).single(),
+ supabase.from('projects').select('*, company:companies(*)').eq('id', t.project_id).single(),
supabase.from('submissions').select('*, delivery:deliveries(*, files:delivery_files(*))').eq('task_id', id).order('version_number'),
supabase.from('profiles').select('*').eq('role', 'team'),
]);
setProject(p);
+ setCompany(p?.company || null);
setSubmissions(subs || []);
setTeamMembers(team || []);
-
- if (p) {
- const { data: co } = await supabase.from('companies').select('*').eq('id', p.company_id).single();
- setCompany(co);
- }
setLoading(false);
}
load();
@@ -187,7 +183,7 @@ export default function TaskDetail() {
Send to Client โ {company?.name}
- Upload the completed file and add an optional message. An email will be sent to {company?.email}.
+ Upload the completed file and add an optional message for the client.