Fix tool wall ordering, and make suite-me use the one entitlement helper
Two bugs Marty spotted from a screenshot of his own wall. 1. The Traffic Desk rendered between L5 and L6 under a stray second "L1 SCINTILLA" header. The wall emits a section header whenever the level changes while walking the TOOLS array, and the Traffic Desk was still in its original L5 array slot from before it moved to L1. Now the wall sorts by level before rendering, so the array can be edited in any order and this class of bug cannot come back. 2. Tiles above L4 still showed "UNLOCKS AT ..." despite suiteLevelOverride. /api/public/suite-me carried its OWN copy of the org/allowlist logic rather than calling suiteEntitlement, so the override reached every tool page but not the wall linking to them. That duplication is the actual defect — the route now calls the shared helper, and the override also carries levelName so the header cannot disagree with the tiles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -43,7 +43,11 @@
|
|||||||
var grid = $('suGrid');
|
var grid = $('suGrid');
|
||||||
grid.innerHTML = '';
|
grid.innerHTML = '';
|
||||||
var lastLv = 0;
|
var lastLv = 0;
|
||||||
TOOLS.forEach(function (t) {
|
// Render in LEVEL order, not array order. The array is maintained by hand
|
||||||
|
// and a tile whose level changes (the Traffic Desk moved from L5 to L1)
|
||||||
|
// would otherwise emit a stray section header in the middle of the wall.
|
||||||
|
// Sorting here means the array can be edited in any order, forever.
|
||||||
|
TOOLS.slice().sort(function (a, b) { return a.lv - b.lv; }).forEach(function (t) {
|
||||||
if (t.lv !== lastLv) {
|
if (t.lv !== lastLv) {
|
||||||
lastLv = t.lv;
|
lastLv = t.lv;
|
||||||
var h = document.createElement('div');
|
var h = document.createElement('div');
|
||||||
|
|||||||
@@ -684,7 +684,8 @@ async function handleApi(req,res,pathname){
|
|||||||
});
|
});
|
||||||
const ov=ovMap[Number(d.id)];
|
const ov=ovMap[Number(d.id)];
|
||||||
if(ov&&ov>Number(d.level||0)){
|
if(ov&&ov>Number(d.level||0)){
|
||||||
dd=Object.assign({},d,{level:ov,trueLevel:Number(d.level||0),levelOverridden:true});
|
dd=Object.assign({},d,{level:ov,levelName:LEVELS[ov-1]||d.levelName,
|
||||||
|
trueLevel:Number(d.level||0),trueLevelName:d.levelName,levelOverridden:true});
|
||||||
}
|
}
|
||||||
return {d:dd,inOrg,beta,allowed};
|
return {d:dd,inOrg,beta,allowed};
|
||||||
}
|
}
|
||||||
@@ -1065,34 +1066,17 @@ async function handleApi(req,res,pathname){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(req.method==='GET'&&pathname==='/api/public/suite-me'){
|
if(req.method==='GET'&&pathname==='/api/public/suite-me'){
|
||||||
// The Circle Suite entitlement: signed-in wallet -> live level + org check.
|
// One source of truth for entitlement. This route used to carry its own
|
||||||
const s=messages.authFromCookie(req);
|
// copy of the org/allowlist logic, which is exactly how the level override
|
||||||
if(!s)return json(res,401,{error:'Not signed in.'});
|
// came to work on every tool page but not on the wall that links to them.
|
||||||
try{
|
const e=await suiteEntitlement(req).catch(()=>({error:'Chain read hiccup - try again.',code:500}));
|
||||||
const cached=memberCache.get(s.id);
|
if(e.error)return json(res,e.code||500,{error:e.error});
|
||||||
let d;
|
const d=e.d;
|
||||||
if(cached&&Date.now()-cached.ts<120000)d=cached.data;
|
return json(res,200,{id:d.id,tier:d.tier,tierName:d.tierName,level:d.level,levelName:d.levelName,
|
||||||
else{
|
directCount:d.directCount,inOrg:e.inOrg,beta:e.beta,allowed:e.allowed,
|
||||||
d=await Promise.race([chain.memberPublic(s.id),new Promise((_,rej)=>setTimeout(()=>rej(new Error('timeout')),20000))]);
|
levelOverridden:!!d.levelOverridden,trueLevel:d.trueLevel});
|
||||||
memberCache.set(s.id,{ts:Date.now(),data:d});
|
|
||||||
}
|
|
||||||
if(!d||!d.registered)return json(res,404,{error:'Position not found.'});
|
|
||||||
const cfg=getConfig();
|
|
||||||
// teamRootId is a comma-separated LIST of team roots (e.g. "21,137,139").
|
|
||||||
// Number() on that yields NaN -> every member reads as outside the org,
|
|
||||||
// which is exactly the bug Marty hit signing in as #21. Parse the list.
|
|
||||||
const roots=String(cfg.teamRootId||cfg.orgRootId||'21').split(',').map(x=>Number(x.trim())).filter(Boolean);
|
|
||||||
const chainIds=Array.isArray(d.uplineChain)?d.uplineChain.map(Number):[];
|
|
||||||
const inOrg=roots.some(r=>Number(d.id)===r||chainIds.includes(r));
|
|
||||||
// Team-beta gate: while suiteAllowlist is set (comma-separated ids),
|
|
||||||
// only those positions light up; everyone else sees the beta notice.
|
|
||||||
// Clearing the allowlist opens the Suite to the whole org - no deploy.
|
|
||||||
const allow=String(cfg.suiteAllowlist||'').split(',').map(x=>Number(x.trim())).filter(Boolean);
|
|
||||||
const beta=allow.length>0;
|
|
||||||
const allowed=!beta||allow.includes(Number(d.id));
|
|
||||||
return json(res,200,{id:d.id,tier:d.tier,tierName:d.tierName,level:d.level,levelName:d.levelName,directCount:d.directCount,inOrg,beta,allowed});
|
|
||||||
}catch(e){return json(res,500,{error:'Chain read hiccup — refresh to retry.'});}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req.method==='GET'&&pathname==='/api/public/msg-me'){
|
if(req.method==='GET'&&pathname==='/api/public/msg-me'){
|
||||||
const s=messages.authFromCookie(req);
|
const s=messages.authFromCookie(req);
|
||||||
if(!s)return json(res,401,{error:'Not signed in.'});
|
if(!s)return json(res,401,{error:'Not signed in.'});
|
||||||
|
|||||||
Reference in New Issue
Block a user