Simplify daily review UI and compact header cards

This commit is contained in:
Wyndham ARR committed 2026-10-09 14:30:41 +08:00
1 parent 6d1a98743f
commit 64f4a0d3de
13 files changed
+185 -133

No files matched your search

+26 -21
View File
@@ -318,21 +318,23 @@ test('pending date labels have Chinese, English and Thai translations',()=>{
assert.equal(catalog['arr_download.pending_price'][0],'待复核价格');
});
test('cancelled source rows need no fields and the exclusion stays visible before continuation',async()=>{
test('cancelled source exclusions need no fields or summary and still permit continuation',async()=>{
const h=harness(()=>response(200,{...review([]),excluded_cancelled_count:6}));
await h.acceptARRDownloadTask(task,{sync:false});
assert.equal(h.element('#arr-data-review-body').innerHTML,'');
assert.match(h.element('#arr-data-review-progress').textContent,/data_review\.excluded_cancelled/);
assert.equal(h.element('#arr-data-review-progress').textContent,'');
assert.equal(h.state.arrDataReview.excluded_cancelled_count,6);
assert.equal(h.element('#arr-data-review-finalize').disabled,false);
assert.equal(h.calls.every(call=>call.method==='GET'),true);
});
test('PM exclusions stay visible without requiring field completion',async()=>{
test('PM exclusions retain their original state without displaying a summary or requiring fields',async()=>{
const h=harness(()=>response(200,{...review([]),excluded_cancelled_count:6,excluded_pm_count:19}));
await h.acceptARRDownloadTask(task,{sync:false});
const progress=h.element('#arr-data-review-progress').textContent;
assert.match(progress,/data_review\.excluded_cancelled/);
assert.match(progress,/data_review\.excluded_pm/);
assert.equal(progress,'');
assert.equal(h.state.arrDataReview.excluded_cancelled_count,6);
assert.equal(h.state.arrDataReview.excluded_pm_count,19);
assert.equal(h.element('#arr-data-review-body').innerHTML,'');
assert.equal(h.element('#arr-data-review-finalize').disabled,false);
assert.equal(h.calls.every(call=>call.method==='GET'),true);
@@ -383,39 +385,41 @@ for (const fixture of [
{name:'confirmed decisions show the saved confirmation instead of the original problem',
source_state:'failed', reason_code:'http_failure', confirmed:true, value:'VERIFIED', expected:'confirmed_value'},
]) {
test(`field review explanation: ${fixture.name}`,async()=>{
test(`compact field review preserves decisions without explanatory metadata: ${fixture.name}`,async()=>{
const source=item(fixture);
const original=plain(source);
const h=harness(()=>response(200,review([source])));
await h.acceptARRDownloadTask(task,{sync:false});
const markup=h.element('#arr-data-review-body').innerHTML;
assert.match(markup,new RegExp(`data_review\\.${fixture.expected}\\b`));
assert.match(markup,new RegExp(`data_review\\.${source.can_be_empty?'optional':'required'}_field_rule\\b`));
assert.doesNotMatch(markup,/<small>|data_review\.(?:optional_field_rule|required_field_rule|source_row|room_missing_in_scope|source_unconfirmed|fetch_failed|conflict_value|invalid_value|confirmed_value)/);
assert.match(markup,new RegExp(`<label[^>]+>${source.field_label}</label>`));
assert.match(markup,/<strong>SYNTHETIC-001<\/strong>/);
assert.doesNotMatch(markup,/Synthetic Company|SYNTHETIC-ROOM|<small>/);
assert.equal((markup.match(/<td /g)||[]).length,4);
assert.match(markup,/data-arr-data-review-value/);
assert.match(markup,/data-arr-data-review-save/);
assert.match(markup,new RegExp(source.confirmed?'data_review.confirmed':'data_review.pending'));
assert.equal(markup.includes('data_review.missing_value'),false);
assert.equal(markup.includes('data-arr-data-review-empty'),source.can_be_empty);
assert.deepEqual(plain(source),original,'rendering explanations never changes a source or confirmation');
assert.deepEqual(plain(source),original,'a compact display never changes source metadata or confirmation');
assert.equal(h.calls.every(call=>call.method==='GET'),true);
});
}
test('review explanations have distinct Chinese, English and Thai copy for required, optional and failed reads',()=>{
test('compact review controls and validation messages remain available in Chinese, English and Thai',()=>{
const context=vm.createContext({window:{},document:{readyState:'loading',addEventListener(){}}});
vm.runInContext(fs.readFileSync(path.resolve(__dirname,'../../arr_web/static/i18n.js'),'utf8'),context);
const catalog=context.window.ARRI18n.CATALOG;
const keys=['required_field_rule','optional_field_rule','required_value_missing','room_missing_in_scope','source_unconfirmed',
'fetch_failed','conflict_value','invalid_value'];
const keys=['save','confirm_empty','confirmed','pending','field_block_code','field_disp_room_no','use_empty',
'complete_first','conflict','disconnected','invalid','required','integer_required'];
for(const key of keys) {
const translations=catalog[`data_review.${key}`];
assert.equal(translations.length,3);
translations.forEach(text=>assert.equal(typeof text==='string' && text.length>0,true));
}
assert.match(catalog['data_review.required_field_rule'][0],/原处理规则必填/);
assert.match(catalog['data_review.optional_field_rule'][0],/允许为空/);
assert.match(catalog['data_review.room_missing_in_scope'][0],/费率在处理范围内/);
assert.match(catalog['data_review.source_unconfirmed'][0],/确认无此信息/);
assert.match(catalog['data_review.fetch_failed'][0],/不能直接认定没有此信息/);
assert.equal(catalog['data_review.fetch_failed'][0].includes('刷新'),false,
'refreshing the review must not imply a retry of Oracle acquisition');
assert.equal(catalog['arr_acquisition.label'][0],'状态');
assert.equal(catalog['arr_acquisition.label'][1],'Status');
assert.equal(catalog['arr_download.date_independent'],undefined);
});
test('source review opens before a job exists and another date can start its own download',async()=>{
@@ -423,7 +427,8 @@ test('source review opens before a job exists and another date can start its own
? response(202,{...JSON.parse(options.body),status:'queued',job_id:null,can_retry:false})
: response(200,review([item()])));
await h.acceptARRDownloadTask(task,{sync:false});
assert.equal(h.element('#arr-download-data-review').hidden,false);
assert.equal(h.element('#arr-download-button').textContent,'arr_download.complete_data');
assert.equal(h.element('#arr-download-help').hidden,true);
assert.equal(h.element('#arr-data-review-panel').hidden,false);
assert.equal(h.element('#arr-download-review').hidden,true);
assert.equal(h.element('#arr-data-review-finalize').disabled,true);
@@ -586,7 +591,7 @@ test('revision conflict reloads the current revision before any next save',async
test('unconfirmed source values are never prefilled and all labels and stored values are escaped',async()=>{
const h=harness(()=>response(200,review([
item({company_name:'<img src=x onerror=alert(1)>',value:'GUESSED-BLOCK'}),
item({company_name:'<img src=x onerror=alert(1)>',confirmation_no:'<img src=x onerror=alert(1)>',value:'GUESSED-BLOCK'}),
item({item_id:'2:RES_COMMENT',field:'RES_COMMENT',confirmed:true,value:'</textarea><script>bad()</script>'}),
])));
await h.acceptARRDownloadTask(task,{sync:false});
+1 -1
View File
@@ -74,7 +74,7 @@ test('completed retrieval remains distinct from pending field and price review',
assert.equal(h.element('#arr-acquisition-status').textContent,'已完成');
assert.equal(h.element('#arr-acquisition-percent').textContent,'100%');
assert.equal(h.element('#arr-acquisition-detail').textContent,'已获取 85 / 85 笔');
if(status==='needs_data_review') assert.equal(h.element('#arr-download-data-review').hidden,false);
if(status==='needs_data_review') assert.equal(h.element('#arr-download-button').textContent,'完善报表字段');
if(status==='needs_review') assert.equal(h.element('#arr-download-review').hidden,false);
assert.doesNotMatch(h.element('#arr-acquisition-detail').textContent,/生成|报表已完成/);
}
+9 -1
View File
@@ -19,8 +19,16 @@ test('zero is a valid explicit price while blank and negative prices remain inva
test('zero is rendered as a saved value and permits continuation when all items are saved',()=>{
const h=harness(()=>{throw new Error('unexpected request');});
h.renderDailyPriceReview(fixture(0));
const review=fixture(0);
review.items[0].candidate_prices=[{effective_rate_amount:888888,real_price:999999}];
review.items[0].affected_records=333333;
h.renderDailyPriceReview(review);
const html=h.element('#daily-price-review-body').innerHTML;
assert.equal((html.match(/<td /g)||[]).length,5);
assert.match(html,/Fixture Company|FIXTURE/);
assert.match(html,/>100<\/td>/);
assert.doesNotMatch(html,/888888|999999|333333|daily-price-review-candidates/);
assert.match(html,/aria-describedby="daily-price-review-message"/);
assert.match(html,/value="0"/);
assert.match(html,/daily-price-review-row is-complete/);
assert.match(html,/daily_review.saved/);
@@ -62,6 +62,19 @@ test('a successful replacement displays only the accepted new report for the sam
assert.doesNotMatch(h.element('#jobs-body').innerHTML,/job_id=old/);
});
test('history access appears only when the date has historical versions while current download and delete remain available',()=>{
const h=setup();
for(const count of [0,undefined,1,2]) {
h.state.dailyReports=[day(job('current'),undefined,{history_count:count})];
h.renderJobs();
const html=h.element('#jobs-body').innerHTML;
assert.equal(html.includes(`data-daily-history="${date}"`),Number(count)>0);
assert.match(html,/job_id=current/);
assert.match(html,/data-daily-delete="current"/);
}
assert.equal(h.calls.length,0);
});
test('a failed replacement never removes the valid old report',()=>{
const h=setup();
h.state.dailyReports=[day(job('old'),job('new',{status:'failed',is_current:false,daily_report_sha256:null}))];