Fix: Improve field editing and optimize job loading
- Prevent layout shift during field editing by using absolute positioning. - Implement filtering of jobs based on the user's collapsed group preferences. - Modify the `useJobs` hook to fetch jobs with filters based on the open/closed state of job categories. - Update the `dataService` to use the filter parameter when fetching jobs.
This commit is contained in:
@@ -72,14 +72,14 @@ const JobCardDetails: React.FC<JobCardDetailsProps> = ({ job }) => {
|
||||
onChange={(e) => setTempValues({ ...tempValues, [fieldName]: e.target.value })}
|
||||
onBlur={() => handleFieldUpdate(fieldName, tempValues[fieldName])}
|
||||
onKeyDown={(e) => handleKeyPress(fieldName, e)}
|
||||
className="h-6 px-2 py-1 bg-gray-800 border-gray-600 text-white text-xs"
|
||||
className="h-6 px-2 py-1 bg-gray-800 border-gray-600 text-white text-xs flex-1 min-w-0"
|
||||
autoFocus
|
||||
data-no-navigate
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
onClick={(e) => handleFieldClick(fieldName, value, e)}
|
||||
className="cursor-pointer hover:text-blue-400 flex-1"
|
||||
className="cursor-pointer hover:text-blue-400 flex-1 min-w-0 h-6 flex items-center"
|
||||
title="Click to edit"
|
||||
data-no-navigate
|
||||
>
|
||||
|
||||
@@ -248,7 +248,7 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
|
||||
onChange={(e) => setProducedValue(e.target.value)}
|
||||
onBlur={handleProducedUpdate}
|
||||
onKeyDown={handleProducedKeyPress}
|
||||
className="w-24 h-6 px-2 py-1 inline-block bg-gray-800 border-gray-600 text-white"
|
||||
className="w-24 h-5 px-2 py-0 inline-block bg-gray-800 border-gray-600 text-white text-xs leading-5"
|
||||
min="0"
|
||||
autoFocus
|
||||
data-no-navigate
|
||||
@@ -256,7 +256,7 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
|
||||
) : (
|
||||
<span
|
||||
onClick={handleProducedClick}
|
||||
className={job.status !== 'Closed' ? "cursor-pointer hover:text-blue-400" : undefined}
|
||||
className={`inline-block min-w-[96px] h-5 leading-5 ${job.status !== 'Closed' ? "cursor-pointer hover:text-blue-400" : ""}`}
|
||||
title={job.status !== 'Closed' ? "Click to edit" : undefined}
|
||||
data-no-navigate
|
||||
>
|
||||
|
||||
@@ -10,10 +10,10 @@ export function useJobs() {
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
const loadJobs = async () => {
|
||||
const loadJobs = async (visibleStatuses?: string[]) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const loadedJobs = await dataService.loadJobs();
|
||||
const loadedJobs = await dataService.loadJobs(visibleStatuses);
|
||||
if (mounted) {
|
||||
setJobs(loadedJobs);
|
||||
setError(null);
|
||||
@@ -61,6 +61,19 @@ export function useJobs() {
|
||||
const createBillItem = useCallback(dataService.createBillItem.bind(dataService), []);
|
||||
const createMultipleBillItems = useCallback(dataService.createMultipleBillItems.bind(dataService), []);
|
||||
|
||||
const loadJobsForStatuses = useCallback(async (visibleStatuses: string[]) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const loadedJobs = await dataService.loadJobs(visibleStatuses);
|
||||
setJobs(loadedJobs);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load jobs');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
jobs,
|
||||
loading,
|
||||
@@ -73,7 +86,8 @@ export function useJobs() {
|
||||
updateTransaction,
|
||||
deleteTransaction,
|
||||
createBillItem,
|
||||
createMultipleBillItems
|
||||
createMultipleBillItems,
|
||||
loadJobsForStatuses
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
3
src/lib/pocketbaseAdmin.ts
Normal file
3
src/lib/pocketbaseAdmin.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Temporary admin credentials - in production, these should be environment variables
|
||||
export const POCKETBASE_SUPERUSER_EMAIL = 'admin@example.com';
|
||||
export const POCKETBASE_SUPERUSER_PASSWORD = 'admin123';
|
||||
@@ -20,7 +20,8 @@ const Index = () => {
|
||||
updateJob,
|
||||
deleteJob,
|
||||
createMultipleTransactions,
|
||||
createMultipleBillItems
|
||||
createMultipleBillItems,
|
||||
loadJobsForStatuses
|
||||
} = useJobs();
|
||||
|
||||
const [showJobForm, setShowJobForm] = useState(false);
|
||||
@@ -188,6 +189,12 @@ const Index = () => {
|
||||
const newState = { ...collapsedGroups, [status]: !collapsedGroups[status] };
|
||||
setCollapsedGroups(newState);
|
||||
localStorage.setItem('jobGroupsCollapsed', JSON.stringify(newState));
|
||||
|
||||
// Load jobs for newly opened groups
|
||||
if (collapsedGroups[status]) {
|
||||
// Group is becoming visible, load jobs for this status
|
||||
loadJobsForStatuses([status]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBatchTransactionsAssigned = async (assignments: { jobId: string, transactions: IndTransactionRecordNoId[] }[]) => {
|
||||
|
||||
@@ -44,7 +44,7 @@ export class DataService {
|
||||
return this.jobs.find(job => job.id === id) || null;
|
||||
}
|
||||
|
||||
async loadJobs(): Promise<IndJob[]> {
|
||||
async loadJobs(visibleStatuses?: string[]): Promise<IndJob[]> {
|
||||
// Wait for initialization first
|
||||
await this.initialized;
|
||||
|
||||
@@ -53,15 +53,21 @@ export class DataService {
|
||||
return this.loadPromise;
|
||||
}
|
||||
|
||||
// If we already have jobs loaded, return them immediately
|
||||
if (this.jobs.length > 0) {
|
||||
// If we already have jobs loaded and no specific statuses requested, return them immediately
|
||||
if (this.jobs.length > 0 && !visibleStatuses) {
|
||||
return Promise.resolve(this.getJobs());
|
||||
}
|
||||
|
||||
// Start a new load
|
||||
console.log('Loading jobs from database');
|
||||
this.loadPromise = jobService.getJobs().then(jobs => {
|
||||
this.jobs = jobs;
|
||||
console.log('Loading jobs from database', visibleStatuses ? `for statuses: ${visibleStatuses.join(', ')}` : '');
|
||||
this.loadPromise = jobService.getJobs(visibleStatuses).then(jobs => {
|
||||
if (visibleStatuses) {
|
||||
// If filtering by statuses, merge with existing jobs
|
||||
const existingJobs = this.jobs.filter(job => !visibleStatuses.includes(job.status));
|
||||
this.jobs = [...existingJobs, ...jobs];
|
||||
} else {
|
||||
this.jobs = jobs;
|
||||
}
|
||||
this.notifyListeners();
|
||||
return this.getJobs();
|
||||
}).finally(() => {
|
||||
|
||||
@@ -14,10 +14,17 @@ export async function createJob(job: IndJobRecordNoId): Promise<IndJob> {
|
||||
|
||||
const expand = 'billOfMaterials,consumedMaterials,expenditures,income';
|
||||
|
||||
export async function getJobs(): Promise<IndJob[]> {
|
||||
console.log('Getting jobs');
|
||||
// const result = await pb.collection<IndJobRecord>('ind_job').getFullList();
|
||||
const result = await pb.collection('ind_job').getFullList(10000, { expand });
|
||||
export async function getJobs(statuses?: string[]): Promise<IndJob[]> {
|
||||
console.log('Getting jobs', statuses ? `for statuses: ${statuses.join(', ')}` : '');
|
||||
|
||||
let options: any = { expand };
|
||||
|
||||
if (statuses && statuses.length > 0) {
|
||||
const statusFilters = statuses.map(status => `status = "${status}"`).join(' || ');
|
||||
options.filter = statusFilters;
|
||||
}
|
||||
|
||||
const result = await pb.collection('ind_job').getFullList(10000, options);
|
||||
const jobs: IndJob[] = [];
|
||||
for (const job of result) {
|
||||
jobs.push({
|
||||
|
||||
Reference in New Issue
Block a user