/** * This module should contain warnings subsystem. * First idea - there should be either definitive list of available warnings, * or defined as some json logic leading to the warning message(s), similar way how FillExtData works. * Second - resources indicator should be separate component */ import {crate_type_to_hp, item_occupied_counters, resource_counters} from "./count_resources"; import {data as shared_data} from "./utils"; const Levels = { "reminder": {priority: 1, icon: '/images/shop/icon-reminder.svg'}, "warning": {priority: 2, icon: '/images/shop/icon-warning.svg'}, } const find_in_counters = (counters, name) => { return counters.find((element) => element.name === name) } const find_previous_source = (data, index, source) => { return data.slice(0, index).find((element) => { return element.resources && find_in_counters(element.resources, source) }) } const find_next_source_index = (data, index, source) => { return data.slice(index + 1).findIndex((element) => { return element.resources && find_in_counters(element.resources, source) }) + index + 1 } const not_enough_resource_trigger = (name) => { return (_data, _index, counters) => { const eem = find_in_counters(counters, name); return eem.occupied > eem.max; } } const no_source_trigger = (name) => { return (data, index, _counters) => { const occupied = item_occupied_counters[name](data[index]); if (occupied > 0) return !find_previous_source(data, index, name); return false; } } const wiring_constraint = (name) => { return (data, index, _counters) => { const next = find_next_source_index(data, index, name); return next - index === 1; } } const Types = { "eem_resource": { level: "warning", trigger: not_enough_resource_trigger("eem"), message: "Insufficient EEM connectors" }, "no_eem_source": { level: "warning", trigger: no_source_trigger("eem"), message: 'This card needs a card that provides a EEM connector (e.g. Kasli) at its left.' }, "idc_resource": { level: "warning", trigger: not_enough_resource_trigger("idc"), message: "Insufficient IDC connectors." }, "no_idc_source": { level: "warning", trigger: no_source_trigger("idc"), message: 'Should be after a Zotino or a HD68-IDC or with another IDC-BNC.' }, "clk_resource": { level: "warning", trigger: not_enough_resource_trigger("clk"), message: "Insufficient clock connectors." }, "no_clk_source": { level: "warning", trigger: no_source_trigger("clk"), message: 'This card needs either a card that provides a clock source (e.g. Kasli or Clocker) at its left or use an external clock source.' }, "eem_wiring_constraint": { level: "reminder", trigger: wiring_constraint("eem"), message: "Due to wiring constraints, the carrier can only connect to EEM cards immediately at its right, without crossing another carrier." }, "default": { level: "warning", trigger: (_a, _b, _c) => { return true; }, message: 'This item has unimplemented warning' } } export function TriggerCardWarnings(data, index, precounted) { const element = data[index]; return element.warnings .map((warning, _) => { if (!!Types[warning]) return Types[warning].trigger(data, index, precounted) ? {trigger: undefined, name: warning, ...Types[warning]} : null; else return Types.default; }) .filter((warning, _) => { return !!warning }); } export function TriggerWarnings(data) { return data.map((element, index) => { if (!element.warnings) return element; element.show_warnings = element.warnings .map((warning, _) => { if (!!Types[warning]) return Types[warning].trigger(data, index, element.counted_resources) ? {trigger: undefined, name: warning, ...Types[warning]} : null; else return Types.default; }) .filter((warning, _) => { return !!warning }); return element; }) } export function MaxLevel(warnings) { let mx = {priority: 0, icon: null}; for (const warning of warnings) { if (Levels[warning.level].priority > mx.priority) mx = Levels[warning.level]; } return mx; } const crate_warnings = { "overfit": { message: "You have reached the maximum number of slots allowed for this crate. Consider removing cards.", level: "warning", trigger: (crate, occupied) => { const nbrHP = crate_type_to_hp(crate.crate_type); return occupied > nbrHP; } }, "underfit_rack": { message: "The selected cards fit in a 42hp desktop crate, consider switching to it for a more compact system", level: "reminder", trigger: (crate, occupied) => { const nbrHPDesktop = shared_data.crateModes.desktop.hp; return crate.crate_type === shared_data.crateModes.rack.id && occupied < nbrHPDesktop; } } } export function TriggerCrateWarnings(crate) { const nbrOccupied = resource_counters.hp(crate.items, -1); let warnings = []; Object.entries(crate_warnings).forEach(([id, warning], _) => { if (warning.trigger(crate, nbrOccupied)) warnings.push({...warning, id: id, trigger: undefined}); }) return warnings; }