diff --git a/worklenz-backend/src/controllers/project-ratecard-controller.ts b/worklenz-backend/src/controllers/project-ratecard-controller.ts index 8da36de6..79069006 100644 --- a/worklenz-backend/src/controllers/project-ratecard-controller.ts +++ b/worklenz-backend/src/controllers/project-ratecard-controller.ts @@ -57,14 +57,14 @@ export default class ProjectRateCardController extends WorklenzControllerBase { fprr.*, jt.name as jobtitle, ( - SELECT COALESCE(json_agg(pm.id), '[]'::json) - FROM project_members pm - WHERE pm.project_rate_card_role_id = fprr.id + SELECT COALESCE(json_agg(pm.id), '[]'::json) + FROM project_members pm + WHERE pm.project_rate_card_role_id = fprr.id ) AS members FROM finance_project_rate_card_roles fprr LEFT JOIN job_titles jt ON fprr.job_title_id = jt.id WHERE fprr.project_id = $1 - ORDER BY jt.name; + ORDER BY fprr.created_at; `; const result = await db.query(q, [project_id]); return res.status(200).send(new ServerResponse(true, result.rows)); diff --git a/worklenz-frontend/src/pages/projects/projectView/finance/ratecard-tab/reatecard-table/ratecard-table.tsx b/worklenz-frontend/src/pages/projects/projectView/finance/ratecard-tab/reatecard-table/ratecard-table.tsx index 9413e05d..51818231 100644 --- a/worklenz-frontend/src/pages/projects/projectView/finance/ratecard-tab/reatecard-table/ratecard-table.tsx +++ b/worklenz-frontend/src/pages/projects/projectView/finance/ratecard-tab/reatecard-table/ratecard-table.tsx @@ -30,13 +30,14 @@ const RatecardTable: React.FC = () => { const rolesRedux = useAppSelector((state) => state.projectFinanceRateCard.rateCardRoles) || []; const isLoading = useAppSelector((state) => state.projectFinanceRateCard.isLoading); const currency = useAppSelector((state) => state.financeReducer.currency).toUpperCase(); - + const rateInputRefs = React.useRef>([]); // Local state for editing const [roles, setRoles] = useState(rolesRedux); const [addingRow, setAddingRow] = useState(false); const [jobTitles, setJobTitles] = useState([]); const [members, setMembers] = useState([]); const [isLoadingMembers, setIsLoading] = useState(false); + const [focusRateIndex, setFocusRateIndex] = useState(null); const pagination = { current: 1, @@ -91,6 +92,13 @@ const RatecardTable: React.FC = () => { } }, [dispatch, projectId]); + useEffect(() => { + if (focusRateIndex !== null && rateInputRefs.current[focusRateIndex]) { + rateInputRefs.current[focusRateIndex]?.focus(); + setFocusRateIndex(null); + } + }, [roles, focusRateIndex]); + // Add new role row const handleAddRole = () => { setAddingRow(true); @@ -110,7 +118,7 @@ const RatecardTable: React.FC = () => { } }; - // Handle job title select for new row + // In handleSelectJobTitle, after successful insert, update the rate if needed const handleSelectJobTitle = async (jobTitleId: string) => { const jobTitle = jobTitles.find((jt) => jt.id === jobTitleId); if (!jobTitle || !projectId) return; @@ -120,27 +128,21 @@ const RatecardTable: React.FC = () => { ); if (insertProjectRateCardRole.fulfilled.match(resultAction)) { - const newRole = resultAction.payload; - setRoles([ - ...roles, - { - id: newRole.id, - job_title_id: newRole.job_title_id, - jobtitle: newRole.jobtitle, - rate: newRole.rate, - members: [], // Initialize members array - }, - ]); + // Re-fetch roles and focus the last one (newly added) + dispatch(fetchProjectRateCardRoles(projectId)).then(() => { + setFocusRateIndex(roles.length); // The new row will be at the end + }); } setAddingRow(false); }; - // Handle rate change + // Update handleRateChange to always update local state const handleRateChange = (value: string | number, index: number) => { - const updatedRoles = roles.map((role, idx) => - idx === index ? { ...role, rate: Number(value) } : role + setRoles(prev => + prev.map((role, idx) => + idx === index ? { ...role, rate: Number(value) } : role + ) ); - setRoles(updatedRoles); }; // Handle delete @@ -176,6 +178,18 @@ const RatecardTable: React.FC = () => { console.error('Error assigning member:', error); } }; + // Separate function for updating rate if changed + const handleRateBlur = (value: string, index: number) => { + if (value !== roles[index].rate) { + dispatch(updateProjectRateCardRoleById({ + id: roles[index].id!, + body: { + job_title_id: roles[index].job_title_id, + rate: value, + } + })); + } + }; // Columns const columns: TableProps['columns'] = [ @@ -216,6 +230,7 @@ const RatecardTable: React.FC = () => { align: 'right', render: (value: number, record: JobRoleType, index: number) => ( rateInputRefs.current[index] = el} type="number" value={roles[index]?.rate ?? 0} min={0} @@ -228,17 +243,8 @@ const RatecardTable: React.FC = () => { textAlign: 'right', }} onChange={(e) => handleRateChange(e.target.value, index)} - onBlur={(e) => { - if (e.target.value !== roles[index].rate) { - dispatch(updateProjectRateCardRoleById({ - id: roles[index].id!, - body: { - job_title_id: roles[index].job_title_id, - rate: e.target.value, - } - })); - } - }} + onBlur={(e) => handleRateBlur(e.target.value, index)} + onPressEnter={(e) => handleRateBlur(e.target.value, index)} /> ), },