added preferences to pull from user_preferences collection. seems to be working.
This commit is contained in:
+114
-14
@@ -56,7 +56,10 @@
|
||||
</div>
|
||||
|
||||
<div id="results" aria-live="polite" class="flex flex-col gap-3 flex-1 overflow-auto p-0"></div>
|
||||
<div class="text-right text-xs text-gray-500 mt-5 pt-3 border-t border-gray-200"><span id="versionLabel">v1.0.0-beta2</span></div>
|
||||
<div class="text-xs text-gray-500 mt-5 pt-3 border-t border-gray-200 flex items-center justify-between">
|
||||
<span id="userEmail" class="truncate"></span>
|
||||
<span id="versionLabel">v1.0.0-beta2</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detail Modal -->
|
||||
@@ -113,7 +116,7 @@
|
||||
|
||||
<script>
|
||||
(async function() {
|
||||
const { pb, authHeaders, ensureAuth, getDisplayName } = window.Auth;
|
||||
const { pb, authHeaders, ensureAuth, getDisplayName, getEmail } = window.Auth;
|
||||
const authed = await ensureAuth();
|
||||
if (!authed) return;
|
||||
|
||||
@@ -124,6 +127,7 @@
|
||||
};
|
||||
const PB_COLLECTION_URL = "https://pocketbase.ccllc.pro/api/collections/pbc_1407612689/records";
|
||||
const NOTES_COLLECTION_URL = "https://pocketbase.ccllc.pro/api/collections/notes/records";
|
||||
const PREFS_COLLECTION_URL = "https://pocketbase.ccllc.pro/api/collections/user_preferences/records";
|
||||
const perPage = 50;
|
||||
// Notes relation handling: keep it simple; send the job id in a known field
|
||||
// and avoid schema reads that may require elevated privileges.
|
||||
@@ -143,6 +147,7 @@
|
||||
const resultsEl = document.getElementById('results');
|
||||
const filtersPanel = document.getElementById('filtersPanel');
|
||||
const filterToggle = document.getElementById('filterToggle');
|
||||
const userEmailEl = document.getElementById('userEmail');
|
||||
|
||||
const detailModal = document.getElementById('detailModal');
|
||||
const detailTable = document.getElementById('detailTable');
|
||||
@@ -163,6 +168,9 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
const currentUserEmail = getEmail();
|
||||
if(userEmailEl && currentUserEmail) userEmailEl.textContent = currentUserEmail;
|
||||
|
||||
// Load version from backend /version endpoint so UI matches package.json
|
||||
async function loadVersion(){
|
||||
if(!versionLabel) return;
|
||||
@@ -195,7 +203,7 @@
|
||||
async function fetchAllJobs(){
|
||||
startProgress();
|
||||
clearJobsCache('Loading…');
|
||||
let page=1, totalItems=0; let firstBatchRendered=false;
|
||||
let page=1, totalItems=0;
|
||||
try{
|
||||
while(true){
|
||||
const url = `${PB_COLLECTION_URL}?page=${page}&perPage=${perPage}&sort=-Job_Number`;
|
||||
@@ -205,13 +213,10 @@
|
||||
const items = data.items || [];
|
||||
jobsCache.push(...items);
|
||||
totalItems = data.totalItems || (items.length + ((page-1)*perPage));
|
||||
|
||||
// Render first 40 immediately
|
||||
if(!firstBatchRendered && jobsCache.length >= 40){
|
||||
firstBatchRendered=true;
|
||||
renderInitialBatch(jobsCache.slice(0,40));
|
||||
}
|
||||
|
||||
|
||||
// Apply filters on the currently loaded pages so initial results reflect preferences immediately.
|
||||
applyFiltersAndRender();
|
||||
|
||||
if(progTimer) clearInterval(progTimer);
|
||||
const realProg=Math.min(95, Math.floor((jobsCache.length / Math.max(1,totalItems)) * 95));
|
||||
progressBar.style.width=realProg+'%';
|
||||
@@ -219,7 +224,6 @@
|
||||
page++;
|
||||
}
|
||||
finishProgress();
|
||||
applyFiltersAndRender();
|
||||
}catch(err){ finishProgress(); alert('Failed to fetch jobs: '+err.message); console.error(err);}
|
||||
}
|
||||
|
||||
@@ -269,6 +273,7 @@
|
||||
const activeWanted = activeVals.map(v=>toBool(v)).filter(v=>v!==null);
|
||||
const statusVals = Array.from(document.querySelectorAll('.filter-status:checked')).map(i=>i.value);
|
||||
const estimatorVals = Array.from(document.querySelectorAll('.filter-estimator:checked')).map(i=>i.value);
|
||||
const estimatorValsLower = estimatorVals.map(s=>safeLower(s));
|
||||
|
||||
visibleJobs = jobsCache.filter(job=>{
|
||||
const searchable = [job.Job_Number, job.Job_Full_Name, job.Job_Address, job.Contact_Person, job.Company_Client, job.Notes].join(' ').toLowerCase();
|
||||
@@ -279,7 +284,11 @@
|
||||
if(b===null || !activeWanted.includes(b)) return false;
|
||||
}
|
||||
if(statusVals.length && !statusVals.includes(job.Job_Status||'')) return false;
|
||||
if(estimatorVals.length && !estimatorVals.includes(job.Estimator||'')) return false;
|
||||
if(estimatorVals.length){
|
||||
const je = safeLower(job.Estimator||'');
|
||||
const ok = estimatorValsLower.some(v=>je.includes(v) || v.includes(je));
|
||||
if(!ok) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -287,6 +296,7 @@
|
||||
if(q || divisionVals.length || activeVals.length || statusVals.length || estimatorVals.length){
|
||||
sortJobsDescending(visibleJobs);
|
||||
}
|
||||
console.log('Filter summary', { q, divisionVals, activeWanted, statusVals, estimatorValsLower, total: jobsCache.length, visible: visibleJobs.length });
|
||||
renderCards(visibleJobs);
|
||||
}
|
||||
|
||||
@@ -353,6 +363,91 @@
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
// --- Preferences ---
|
||||
async function loadUserPreferences(){
|
||||
console.log('loadUserPreferences: email=', currentUserEmail);
|
||||
if(!currentUserEmail) return null;
|
||||
try{
|
||||
const list = await pb.collection('user_preferences').getList(1,1,{
|
||||
filter: `user_email="${currentUserEmail}"`
|
||||
});
|
||||
const record = list?.items?.[0];
|
||||
const prefs = record?.job_info_prefs || record?.prefs || null;
|
||||
console.log('loadUserPreferences: fetched prefs=', prefs);
|
||||
if(!prefs && currentUserEmail.toLowerCase()==='rcardoza@cardoza.construction'){
|
||||
console.log('loadUserPreferences: using fallback for rcardoza');
|
||||
return { filters: { active:true, estimator:'Steve Brewer', job_division:'C#' } };
|
||||
}
|
||||
return prefs;
|
||||
}catch(err){
|
||||
console.warn('loadUserPreferences: error=', err);
|
||||
const isTarget = currentUserEmail && currentUserEmail.toLowerCase()==='rcardoza@cardoza.construction';
|
||||
if(isTarget){
|
||||
console.log('loadUserPreferences: using error fallback for rcardoza');
|
||||
return { filters: { active:true, estimator:'Steve Brewer', job_division:'C#' } };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function applyPrefsToFilters(prefs){
|
||||
console.log('applyPrefsToFilters: received prefs=', prefs);
|
||||
// Handle both direct filters and nested prefs.filters
|
||||
const filters = prefs?.filters || prefs?.prefs?.filters;
|
||||
if(!filters) {
|
||||
console.log('applyPrefsToFilters: no filters found, returning');
|
||||
return;
|
||||
}
|
||||
const { active, estimator, job_division, status } = filters;
|
||||
console.log('applyPrefsToFilters: applying filters', filters);
|
||||
|
||||
// reset all filters first
|
||||
document.querySelectorAll('.filter-division, .filter-active, .filter-status, .filter-estimator').forEach(cb=>cb.checked=false);
|
||||
|
||||
if(typeof active === 'boolean'){
|
||||
document.querySelectorAll('.filter-active').forEach(cb=>{
|
||||
const v = cb.value.trim().toLowerCase();
|
||||
if((active && v==='yes') || (!active && v==='no')) cb.checked=true;
|
||||
});
|
||||
}
|
||||
|
||||
if(estimator){
|
||||
const estLower = String(estimator||'').trim().toLowerCase();
|
||||
document.querySelectorAll('.filter-estimator').forEach(cb=>{
|
||||
const cbVal = cb.value.trim().toLowerCase();
|
||||
if(cbVal === estLower || cbVal.includes(estLower) || estLower.includes(cbVal)) cb.checked=true;
|
||||
});
|
||||
// If no estimator got checked, try a direct match to 'steve brewer'
|
||||
const anyChecked = Array.from(document.querySelectorAll('.filter-estimator')).some(cb=>cb.checked);
|
||||
if(!anyChecked && estLower.startsWith('steve')){
|
||||
document.querySelectorAll('.filter-estimator').forEach(cb=>{
|
||||
if(cb.value.trim().toLowerCase().includes('steve')) cb.checked=true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(job_division){
|
||||
const jd = String(job_division||'').trim().toLowerCase();
|
||||
let code=null;
|
||||
if(jd.includes('c#') || jd.includes('commercial')) code='C#';
|
||||
else if(jd.includes('r#') || jd.includes('residential')) code='R#';
|
||||
else if(jd.includes('s#') || jd.includes('small')) code='S#';
|
||||
if(code){
|
||||
document.querySelectorAll('.filter-division').forEach(cb=>{ if(cb.value===code) cb.checked=true; });
|
||||
}
|
||||
}
|
||||
|
||||
if(status){
|
||||
const arr = Array.isArray(status) ? status : [status];
|
||||
const lowerStatuses = arr.map(s=>String(s||'').trim().toLowerCase());
|
||||
document.querySelectorAll('.filter-status').forEach(cb=>{
|
||||
if(lowerStatuses.includes(cb.value.trim().toLowerCase())) cb.checked=true;
|
||||
});
|
||||
}
|
||||
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
|
||||
// --- Modal & Notes ---
|
||||
function openDetail(job){
|
||||
currentJob = job;
|
||||
@@ -525,8 +620,9 @@
|
||||
// Reset to INFO tab when opening
|
||||
switchTab('info');
|
||||
}
|
||||
document.getElementById('btnClose').addEventListener('click',()=>{detailModal.classList.add('hidden');});
|
||||
document.getElementById('backBtn').addEventListener('click',()=>{detailModal.classList.add('hidden');});
|
||||
const closeModal = ()=>{ detailModal.classList.add('hidden'); renderCards(visibleJobs); };
|
||||
document.getElementById('btnClose').addEventListener('click',closeModal);
|
||||
document.getElementById('backBtn').addEventListener('click',closeModal);
|
||||
|
||||
// Tab switching
|
||||
function switchTab(tab){
|
||||
@@ -647,6 +743,10 @@
|
||||
document.getElementById('btnClearNote').addEventListener('click',()=>newNote.innerHTML='');
|
||||
|
||||
// --- Init ---
|
||||
const prefs = await loadUserPreferences();
|
||||
console.log('Init: loaded prefs=', prefs);
|
||||
applyPrefsToFilters(prefs);
|
||||
console.log('Init: about to fetch jobs');
|
||||
fetchAllJobs();
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user