T9.8 - D7: archiving stops reading as deletion - for project admins
Archiving already froze a project (the server refuses every write); what did not exist was the way back in. Now: - GET /api/projects?archived=only|all filters the answer BY PER-PROJECT ROLE: a project admin (or super/app admin) on THAT project sees it; everyone else receives an empty list from the same request - archived projects appear nowhere for them, counts and pickers included (the default listing already excluded them for everyone; asking is what got gated). Admin-on-Job-A does not surface archived Job B. - The launcher gains a visibly separate, labelled "Archived projects" section (dashed border, read-only stated in words), rendered only when the server returns rows. Opening one makes it active; the launcher's reconcile learned that an active project whose stored summary says archived:true was opened ON PURPOSE and keeps it, while a project archived out from under someone still drops with the existing explanation. - The creator shows ARCHIVED - READ-ONLY where the project is named (both ctx-bar branches, from the SERVER's answer - the page's project comes from the URL, so a stale local summary is not trusted) and refuses saves with a reason before the round trip. The courtesy; the server's refusal is the rule, verified by calling the endpoints directly (wp upsert AND the material-list write both refuse with "archived" even for an admin). - No unarchive button, no second mechanism, and it fits at 390px. Verification (each probe run alone): NEW tests/archived_check.py 15/15. Regressions: launcher_check 58/58, sample_check 10/10, export_check 20/20, frame_check 38/38. Items: D7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -542,15 +542,20 @@ function editQuality(id){
|
||||
}
|
||||
function renderCtxBar(){
|
||||
const bar=document.getElementById('ctx-bar');
|
||||
const archMark = window._projArchived
|
||||
? ` <span class="ctx-sample" style="background:var(--red);color:var(--cds-text-on-color)">ARCHIVED — READ-ONLY</span>` : '';
|
||||
if(!SOP){
|
||||
bar.innerHTML = activeProjectId
|
||||
? `<div class="ctx-empty">No SOP found for this project yet — complete the <strong>SOP Configuration</strong> first, then return here.</div>`
|
||||
: `<div class="ctx-empty">No SOP loaded — import one from the Configuration tool, or use <strong>Load sample data</strong> in the toolbar above.</div>`;
|
||||
? `<div class="ctx-empty">No SOP found for this project yet — complete the <strong>SOP Configuration</strong> first, then return here.${archMark}</div>`
|
||||
: `<div class="ctx-empty">No SOP loaded — import one from the Configuration tool, or use <strong>Load sample data</strong> in the toolbar above.${archMark}</div>`;
|
||||
return;
|
||||
}
|
||||
const p=SOP.project||{}, g=SOP.governance||{};
|
||||
// D7: an archived project is read-only. The chip is the courtesy; the server's
|
||||
// write refusal is the rule, and savePackage() says so before the round trip.
|
||||
const archived = archMark;
|
||||
const sample=SOP.meta&&SOP.meta.sample?`<span class="ctx-sample">SAMPLE</span>`:'';
|
||||
bar.innerHTML=`<div class="ctx-main"><div class="ctx-proj">${esc(p.name||'Untitled')} ${sample}</div>
|
||||
bar.innerHTML=`<div class="ctx-main"><div class="ctx-proj">${esc(p.name||'Untitled')} ${sample}${archived}</div>
|
||||
<div class="ctx-sub">${esc(p.number||'')}${p.division?' · '+esc(p.division):''}</div></div>
|
||||
<div class="ctx-meta"><span><b>${enabledTypes().length}</b> types</span><span>format <code>${esc(g.woFormat||'—')}</code></span><span>track: <b>${esc((SOP.field&&SOP.field.trackPlatform)||'—')}</b></span></div>`;
|
||||
}
|
||||
@@ -1764,6 +1769,10 @@ function collectPackage(){
|
||||
};
|
||||
}
|
||||
async function savePackage(view){
|
||||
if(window._projArchived){
|
||||
toast('This project is archived — read-only. Nothing can be saved to it.', 'alert');
|
||||
return;
|
||||
}
|
||||
if(!wpValidateForm()) return;
|
||||
// A BIM package marked "Signed off (IFF)" without the number isn't traceable.
|
||||
// The status can also be set programmatically (the per-discipline roll-up), so
|
||||
@@ -3833,6 +3842,15 @@ function bootData(){
|
||||
loadLocations(); // CR-004: the option lists come from the server
|
||||
wpFilesRefreshUsage(); // CR-007: the storage meter is live before the first upload
|
||||
mreqLoadMaterials(); // CR-013/D6: the request datalist comes from the project list
|
||||
// D7: the read-only courtesy needs the SERVER's answer, not a stale local
|
||||
// summary - the page's project comes from the URL, which may not be the one
|
||||
// last stored. The chip and the save guard both read this flag.
|
||||
if(activeProjectId && typeof ProjectData!=='undefined' && ProjectData.get){
|
||||
ProjectData.get(activeProjectId).then(p=>{
|
||||
window._projArchived = !!(p && p.archived);
|
||||
if(window._projArchived) renderCtxBar();
|
||||
}).catch(()=>{});
|
||||
}
|
||||
initWpNavDrawer();
|
||||
renderSavedList();
|
||||
positionSectionNav();
|
||||
|
||||
Reference in New Issue
Block a user