Adjusted typography, styles, and other visual elements to ensure consistent layouts
This commit is contained in:
108
dist/chromium/content.js
vendored
108
dist/chromium/content.js
vendored
File diff suppressed because one or more lines are too long
108
dist/firefox/content.js
vendored
108
dist/firefox/content.js
vendored
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@ function composeScript(target, { bootstrap = true } = {}) {
|
|||||||
const calendarLicense = fs.readFileSync(path.join(root, "src/vendor/fullcalendar/LICENSE.md"), "utf8");
|
const calendarLicense = fs.readFileSync(path.join(root, "src/vendor/fullcalendar/LICENSE.md"), "utf8");
|
||||||
parts.unshift(`function loadOpportunityCalendarLibrary() {\n/* ${calendarLicense} */\n${calendarLibrary}\nreturn FullCalendar;\n}`);
|
parts.unshift(`function loadOpportunityCalendarLibrary() {\n/* ${calendarLicense} */\n${calendarLibrary}\nreturn FullCalendar;\n}`);
|
||||||
const css = ["src/content/styles/redwood.css", "src/features/debug/styles.css", "src/features/opportunities/styles.css", "src/features/install-base/styles.css", "src/features/service-requests/styles.css", "src/features/consumption/plan-detail.css", "src/features/ratecard/styles.css", "src/features/opportunities/customer-table-search.css", "src/features/opportunities/calendar.css"]
|
const css = ["src/content/styles/redwood.css", "src/features/debug/styles.css", "src/features/opportunities/styles.css", "src/features/install-base/styles.css", "src/features/service-requests/styles.css", "src/features/consumption/plan-detail.css", "src/features/ratecard/styles.css", "src/features/opportunities/customer-table-search.css", "src/features/opportunities/calendar.css"]
|
||||||
.concat("src/features/manage-time/styles.css", "src/features/products/ramp-projection.css", "src/features/org-chart/styles.css")
|
.concat("src/features/manage-time/styles.css", "src/features/products/ramp-projection.css", "src/features/org-chart/styles.css", "src/content/styles/tables.css", "src/content/styles/dialogs.css")
|
||||||
.map(file => fs.readFileSync(path.join(root, file), "utf8")).join("\n");
|
.map(file => fs.readFileSync(path.join(root, file), "utf8")).join("\n");
|
||||||
parts.unshift(`const redwoodStyles = ${JSON.stringify(css)};`);
|
parts.unshift(`const redwoodStyles = ${JSON.stringify(css)};`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,15 @@ const os = require("node:os");
|
|||||||
await page.evaluate(() => { window.fixtureOpportunitiesDelay = 0; });
|
await page.evaluate(() => { window.fixtureOpportunitiesDelay = 0; });
|
||||||
assert.equal(await page.evaluate(() => window.fixtureContactQueries.length), queriesBeforeOpportunities, "Opportunity lookup must reuse the person's PartyNumber without searching contacts");
|
assert.equal(await page.evaluate(() => window.fixtureContactQueries.length), queriesBeforeOpportunities, "Opportunity lookup must reuse the person's PartyNumber without searching contacts");
|
||||||
assert.equal(await contactOpportunities.locator("tbody tr").count(), 6);
|
assert.equal(await contactOpportunities.locator("tbody tr").count(), 6);
|
||||||
|
const presentation = await contactOpportunities.locator("table").evaluate(table => ({
|
||||||
|
marker: table.getAttribute("data-table-layout"),
|
||||||
|
textHeader: getComputedStyle(table.querySelector("th")).textAlign,
|
||||||
|
moneyHeader: getComputedStyle(table.querySelector('th[data-align="money"]')).textAlign,
|
||||||
|
moneyCell: getComputedStyle(table.querySelector('td[data-align="money"]')).textAlign,
|
||||||
|
nowrap: getComputedStyle(table.querySelector(".opportunities-extension-sort-label")).whiteSpace
|
||||||
|
}));
|
||||||
|
assert.deepEqual(presentation,{marker:"standard",textHeader:"left",moneyHeader:"right",moneyCell:"right",nowrap:"nowrap"});
|
||||||
|
await page.screenshot({path:path.join(output,"shared-table.png")});
|
||||||
assert.equal(await contactOpportunities.getByRole("link", {name:"Contact opportunity 0",exact:true}).getAttribute("target"), "_blank");
|
assert.equal(await contactOpportunities.getByRole("link", {name:"Contact opportunity 0",exact:true}).getAttribute("target"), "_blank");
|
||||||
assert.ok((await contactOpportunities.getByRole("link", {name:"Contact opportunity 0",exact:true}).getAttribute("href")).endsWith("?puid=CONTACT0"));
|
assert.ok((await contactOpportunities.getByRole("link", {name:"Contact opportunity 0",exact:true}).getAttribute("href")).endsWith("?puid=CONTACT0"));
|
||||||
assert.deepEqual((await page.evaluate(()=>window.fixtureOpportunitiesByContact)).map(url=>new URL(url).searchParams.get("offset")), ["0","5"]);
|
assert.deepEqual((await page.evaluate(()=>window.fixtureOpportunitiesByContact)).map(url=>new URL(url).searchParams.get("offset")), ["0","5"]);
|
||||||
|
|||||||
@@ -404,6 +404,7 @@ function createContentModal({
|
|||||||
const opportunitiesTable = document.createElement("table");
|
const opportunitiesTable = document.createElement("table");
|
||||||
opportunitiesTable.id = OPPORTUNITIES_TABLE_ID;
|
opportunitiesTable.id = OPPORTUNITIES_TABLE_ID;
|
||||||
opportunitiesTable.className = "opportunities-extension-table";
|
opportunitiesTable.className = "opportunities-extension-table";
|
||||||
|
opportunitiesTable.setAttribute("data-table-layout", "standard");
|
||||||
opportunitiesTable.setAttribute("aria-label", "Opportunities");
|
opportunitiesTable.setAttribute("aria-label", "Opportunities");
|
||||||
tableSurface.append(opportunitiesTable);
|
tableSurface.append(opportunitiesTable);
|
||||||
|
|
||||||
|
|||||||
78
src/content/styles/dialogs.css
Normal file
78
src/content/styles/dialogs.css
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/* Modal titles follow the customer Service Requests heading. Keep section and
|
||||||
|
calendar headings independent from dialog typography. */
|
||||||
|
.opportunities-extension-modal :is(
|
||||||
|
.opportunities-extension-header,
|
||||||
|
.opportunities-extension-account-plans-header,
|
||||||
|
.opportunities-extension-preferences-header
|
||||||
|
) {
|
||||||
|
padding: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
/* These headers live in panels that already supply the outer inset. */
|
||||||
|
.opportunities-extension-modal :is(.oe-org-dialog.oe-contact-dialog,
|
||||||
|
.oe-org-confirm-panel, .opportunities-extension-projection-confirm,
|
||||||
|
.opportunities-extension-debug-panel) { padding: 16px; }
|
||||||
|
.opportunities-extension-modal :is(.oe-org-editor-header,
|
||||||
|
.opportunities-extension-debug-panel-header) {
|
||||||
|
padding: 0;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal :is(
|
||||||
|
.opportunities-extension-account-plans-header,
|
||||||
|
.opportunities-extension-preferences-header,
|
||||||
|
.opportunities-extension-debug-panel-header
|
||||||
|
) p {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
font-family: "Oracle Sans", Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.35;
|
||||||
|
letter-spacing: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
/* Search controls share the 16px inset of their table frames. */
|
||||||
|
.opportunities-extension-modal :is(.opportunities-extension-customer-table-toolbar,
|
||||||
|
.opportunities-extension-ratecard-toolbar,
|
||||||
|
.opportunities-extension-ratecard-plan-details) { padding-inline: 16px; }
|
||||||
|
.opportunities-extension-modal .opportunities-extension-customer-table-toolbar {
|
||||||
|
padding-block: 12px;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal .opportunities-extension-customer-table-search {
|
||||||
|
width: min(420px, 100%);
|
||||||
|
height: 38px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal :is(
|
||||||
|
.opportunities-extension-header,
|
||||||
|
.opportunities-extension-account-plans-header,
|
||||||
|
.opportunities-extension-preferences-header,
|
||||||
|
.opportunities-extension-debug-panel-header,
|
||||||
|
.oe-org-editor-header
|
||||||
|
) :is(h1, h2, h3),
|
||||||
|
.opportunities-extension-modal .opportunities-extension-projection-confirm h2,
|
||||||
|
.opportunities-extension-modal .oe-org-confirm-panel h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Oracle Sans", Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: normal;
|
||||||
|
color: #312d2a;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal[data-theme="dark"] :is(
|
||||||
|
.opportunities-extension-header,
|
||||||
|
.opportunities-extension-account-plans-header,
|
||||||
|
.opportunities-extension-preferences-header,
|
||||||
|
.opportunities-extension-debug-panel-header,
|
||||||
|
.oe-org-editor-header
|
||||||
|
) :is(h1, h2, h3),
|
||||||
|
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-projection-confirm h2,
|
||||||
|
.opportunities-extension-modal[data-theme="dark"] .oe-org-confirm-panel h3 {
|
||||||
|
color: #f6f4f2;
|
||||||
|
}
|
||||||
106
src/content/styles/tables.css
Normal file
106
src/content/styles/tables.css
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/* Shared data-table presentation. Calendar grids are intentionally not data tables. */
|
||||||
|
.opportunities-extension-modal :is(
|
||||||
|
.opportunities-extension-table-surface,
|
||||||
|
.opportunities-extension-install-base-content,
|
||||||
|
.opportunities-extension-service-requests-content,
|
||||||
|
.opportunities-extension-ratecard-viewport,
|
||||||
|
.opportunities-extension-projection-viewport,
|
||||||
|
.oe-time-grid,
|
||||||
|
.oe-contact-opportunities-table
|
||||||
|
) {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 0;
|
||||||
|
border: 1px solid var(--oe-table-heading-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
/* Unpadded dialogs need an inset on the scrolling frame itself. */
|
||||||
|
.opportunities-extension-modal :is(
|
||||||
|
.opportunities-extension-install-base-content,
|
||||||
|
.opportunities-extension-service-requests-content,
|
||||||
|
.opportunities-extension-ratecard-viewport
|
||||||
|
) { margin: 16px; }
|
||||||
|
/* Other frames already sit inside a padded body: keep a single gutter. */
|
||||||
|
.opportunities-extension-modal :is(
|
||||||
|
.opportunities-extension-body,
|
||||||
|
.opportunities-extension-projection-body,
|
||||||
|
.opportunities-extension-products-detail,
|
||||||
|
.oe-time-body,
|
||||||
|
.oe-org-dialog.oe-contact-opportunities
|
||||||
|
) { padding: 16px; }
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-products-table {
|
||||||
|
border: 1px solid var(--oe-table-heading-border);
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal {
|
||||||
|
--oe-table-heading: #faf9f8;
|
||||||
|
--oe-table-text: #312d2a;
|
||||||
|
--oe-table-border: #ebe8e5;
|
||||||
|
--oe-table-heading-border: #b8b2ad;
|
||||||
|
--oe-table-hover: #f5f4f2;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal[data-theme="dark"] {
|
||||||
|
--oe-table-heading: #333130;
|
||||||
|
--oe-table-text: #f6f4f2;
|
||||||
|
--oe-table-border: #3f3c39;
|
||||||
|
--oe-table-heading-border: #4e4a46;
|
||||||
|
--oe-table-hover: #35322f;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"]:not(.fc-scrollgrid) {
|
||||||
|
width: 100%; border-collapse: collapse; border-spacing: 0; table-layout: auto;
|
||||||
|
color: var(--oe-table-text); font-size: 12px; line-height: 1.35;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > thead > tr > th {
|
||||||
|
padding: 10px 12px; height: 44px; vertical-align: middle; text-align: left;
|
||||||
|
white-space: nowrap; overflow-wrap: normal; word-break: normal;
|
||||||
|
font-size: 12px; font-weight: 700; color: var(--oe-table-text);
|
||||||
|
background: var(--oe-table-heading); border-bottom: 1px solid var(--oe-table-heading-border);
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > tbody > tr > td {
|
||||||
|
padding: 10px 12px; vertical-align: middle; text-align: left;
|
||||||
|
border-bottom: 1px solid var(--oe-table-border);
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > tbody > tr:hover { background: var(--oe-table-hover); }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > thead > tr > th .opportunities-extension-sort-button,
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] .opportunities-extension-ratecard-sort {
|
||||||
|
display: flex; align-items: center; justify-content: flex-start; gap: 8px;
|
||||||
|
width: 100%; min-width: max-content; min-height: 28px; padding: 4px 18px 4px 0;
|
||||||
|
border: 0; border-radius: 0; background: transparent; color: inherit;
|
||||||
|
text-align: left; white-space: nowrap; overflow-wrap: normal; font-weight: 700;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] .opportunities-extension-sort-label { white-space: nowrap; overflow-wrap: normal; word-break: normal; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > thead > tr > th .opportunities-extension-sort-button::after { position: absolute; right: 0; left: auto; font-size: 12px; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] .opportunities-extension-ratecard-sort-icon { margin-left: auto; }
|
||||||
|
/* Currency columns keep their heading and value on the same right edge. */
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] :is(th, td)[data-align="money"] { text-align: right; font-variant-numeric: tabular-nums; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > thead > tr > th[data-align="money"] .opportunities-extension-sort-button,
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] th[data-align="money"] .opportunities-extension-ratecard-sort { justify-content: flex-end; text-align: right; padding: 4px 0 4px 18px; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > thead > tr > th[data-align="money"] .opportunities-extension-sort-button::after { left: 0; right: auto; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] th[data-align="money"] .opportunities-extension-ratecard-sort-icon { order: -1; margin-left: 0; margin-right: auto; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] td[data-align="money"] > span[class*="skeleton"] { margin-left: auto; }
|
||||||
|
/* Preserve room for Overview badges and the compact daily entry controls. */
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-table > tbody > tr > td:is([data-column="name"], [data-column="customer"]) { padding-bottom: 38px; vertical-align: top; }
|
||||||
|
.opportunities-extension-modal .oe-time-grid table[data-table-layout="standard"] > :is(thead, tbody) > tr > :is(th, td) { padding-left: 5px; padding-right: 5px; }
|
||||||
|
.opportunities-extension-modal .oe-time-grid table[data-table-layout="standard"] { table-layout: fixed; }
|
||||||
|
.opportunities-extension-modal table[data-table-layout="standard"] > tbody > tr > th[scope="row"] { padding: 10px 12px; text-align: left; background: var(--oe-table-heading); border-bottom: 1px solid var(--oe-table-border); }
|
||||||
|
/* Rate Card must fit its drawer; headings may wrap at spaces, never mid-word. */
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-ratecard-table[data-table-layout="standard"] {
|
||||||
|
min-width: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-ratecard-table[data-table-layout="standard"] > :is(thead, tbody) > tr > :is(th, td) {
|
||||||
|
padding-inline: 6px;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-ratecard-table[data-table-layout="standard"] > thead > tr > th,
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-ratecard-table[data-table-layout="standard"] > thead > tr > th .opportunities-extension-ratecard-sort {
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
word-break: normal;
|
||||||
|
hyphens: none;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal table.opportunities-extension-ratecard-table[data-table-layout="standard"] > thead > tr > th .opportunities-extension-ratecard-sort {
|
||||||
|
padding: 4px 0;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.opportunities-extension-modal .opportunities-extension-ratecard-sort > span:first-child { min-width: 0; }
|
||||||
|
.opportunities-extension-modal .opportunities-extension-ratecard-sort-icon { flex: 0 0 12px; }
|
||||||
@@ -43,7 +43,6 @@
|
|||||||
width: 100%; min-width: 1120px; table-layout: fixed; border-collapse: collapse; font-size: 11px;
|
width: 100%; min-width: 1120px; table-layout: fixed; border-collapse: collapse; font-size: 11px;
|
||||||
}
|
}
|
||||||
.opportunities-extension-install-base-table col { width: 7%; }
|
.opportunities-extension-install-base-table col { width: 7%; }
|
||||||
.opportunities-extension-install-base-table .install-base-col-partyName { width: 12%; }
|
|
||||||
.opportunities-extension-install-base-table .install-base-col-productGroupName { width: 13%; }
|
.opportunities-extension-install-base-table .install-base-col-productGroupName { width: 13%; }
|
||||||
.opportunities-extension-install-base-table .install-base-col-productName { width: 13%; }
|
.opportunities-extension-install-base-table .install-base-col-productName { width: 13%; }
|
||||||
.opportunities-extension-install-base-table .install-base-col-quantity { width: 5%; }
|
.opportunities-extension-install-base-table .install-base-col-quantity { width: 5%; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ function createInstallBaseView({ MODAL_ID, createCloseIcon, formatOracleResponse
|
|||||||
const { document } = environment;
|
const { document } = environment;
|
||||||
let active = null;
|
let active = null;
|
||||||
const columns = [
|
const columns = [
|
||||||
["partyName", "Customer"], ["productGroupName", "Product Group"], ["productName", "Product"],
|
["productGroupName", "Product Group"], ["productName", "Product"],
|
||||||
["currentSupport", "Current Support", "amount"], ["orderNumber", "Order Number"],
|
["currentSupport", "Current Support", "amount"], ["orderNumber", "Order Number"],
|
||||||
["supportStatus", "Support Status", "status"], ["quantityType", "Quantity Type"],
|
["supportStatus", "Support Status", "status"], ["quantityType", "Quantity Type"],
|
||||||
["quantity", "Quantity", "quantity"], ["supportStartDate", "Support Start Date", "date"],
|
["quantity", "Quantity", "quantity"], ["supportStartDate", "Support Start Date", "date"],
|
||||||
@@ -36,6 +36,7 @@ function createInstallBaseView({ MODAL_ID, createCloseIcon, formatOracleResponse
|
|||||||
const sortColumns = columns.map(([key, , type]) => ({ key, type: ["amount", "quantity"].includes(type) ? "number" : type }));
|
const sortColumns = columns.map(([key, , type]) => ({ key, type: ["amount", "quantity"].includes(type) ? "number" : type }));
|
||||||
function createTable(items, loading = false, sorting = tableSorting.create("installed-base", sortColumns), onSort) {
|
function createTable(items, loading = false, sorting = tableSorting.create("installed-base", sortColumns), onSort) {
|
||||||
const table = element("table", "opportunities-extension-install-base-table");
|
const table = element("table", "opportunities-extension-install-base-table");
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
table.setAttribute("aria-label", "Installed base items");
|
table.setAttribute("aria-label", "Installed base items");
|
||||||
const head = element("thead");
|
const head = element("thead");
|
||||||
const header = element("tr");
|
const header = element("tr");
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ function createManageTime({ MODAL_ID, environment, tableSorting, searchableSelec
|
|||||||
const taskStatus = node("div", "oe-time-status"); taskStatus.setAttribute("role", "status");
|
const taskStatus = node("div", "oe-time-status"); taskStatus.setAttribute("role", "status");
|
||||||
const taskRetry = button("Retry Task Types", "clock", loadTaskTypes, true); taskRetry.hidden = true;
|
const taskRetry = button("Retry Task Types", "clock", loadTaskTypes, true); taskRetry.hidden = true;
|
||||||
const scroll = node("div", "oe-time-grid"), table = node("table");
|
const scroll = node("div", "oe-time-grid"), table = node("table");
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
table.setAttribute("aria-label", "Weekly time entries"); scroll.append(table);
|
table.setAttribute("aria-label", "Weekly time entries"); scroll.append(table);
|
||||||
const footer = node("footer", "opportunities-extension-action-form-footer oe-time-footer");
|
const footer = node("footer", "opportunities-extension-action-form-footer oe-time-footer");
|
||||||
const add = button("Add", "add", () => addRow("Service"), true);
|
const add = button("Add", "add", () => addRow("Service"), true);
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ function createOpportunitiesTable({
|
|||||||
const isSorted = opportunitiesTableState.opportunitiesTableState.sortKey === column.key;
|
const isSorted = opportunitiesTableState.opportunitiesTableState.sortKey === column.key;
|
||||||
header.scope = "col";
|
header.scope = "col";
|
||||||
header.setAttribute("data-column", column.key);
|
header.setAttribute("data-column", column.key);
|
||||||
|
if (column.key === "revenue") header.setAttribute("data-align", "money");
|
||||||
|
|
||||||
if (column.sortable === false) {
|
if (column.sortable === false) {
|
||||||
const label = document.createElement("span");
|
const label = document.createElement("span");
|
||||||
@@ -117,6 +118,7 @@ function createOpportunitiesTable({
|
|||||||
OPPORTUNITIES_COLUMNS.forEach(column => {
|
OPPORTUNITIES_COLUMNS.forEach(column => {
|
||||||
const cell = document.createElement("td");
|
const cell = document.createElement("td");
|
||||||
cell.setAttribute("data-column", column.key);
|
cell.setAttribute("data-column", column.key);
|
||||||
|
if (column.key === "revenue") cell.setAttribute("data-align", "money");
|
||||||
const bar = document.createElement("span");
|
const bar = document.createElement("span");
|
||||||
bar.className = "opportunities-extension-account-plans-skeleton-bar opportunities-extension-table-skeleton-text";
|
bar.className = "opportunities-extension-account-plans-skeleton-bar opportunities-extension-table-skeleton-text";
|
||||||
cell.append(bar);
|
cell.append(bar);
|
||||||
@@ -149,6 +151,7 @@ function createOpportunitiesTable({
|
|||||||
OPPORTUNITIES_COLUMNS.forEach((column) => {
|
OPPORTUNITIES_COLUMNS.forEach((column) => {
|
||||||
const cell = document.createElement("td");
|
const cell = document.createElement("td");
|
||||||
cell.setAttribute("data-column", column.key);
|
cell.setAttribute("data-column", column.key);
|
||||||
|
if (column.key === "revenue") cell.setAttribute("data-align", "money");
|
||||||
|
|
||||||
if (column.key === "stage") {
|
if (column.key === "stage") {
|
||||||
cell.append(createStageBadge(column.display(item)));
|
cell.append(createStageBadge(column.display(item)));
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ function createOrgChartView({ environment, tableSorting, model, loadLayout, icon
|
|||||||
header.append(title, button("Close opportunities", "close", finish));
|
header.append(title, button("Close opportunities", "close", finish));
|
||||||
const status = el("p", "oe-org-status"), surface = el("div", "oe-contact-opportunities-table"), table = el("table"), head = el("thead"), row = el("tr"), body = el("tbody");
|
const status = el("p", "oe-org-status"), surface = el("div", "oe-contact-opportunities-table"), table = el("table"), head = el("thead"), row = el("tr"), body = el("tbody");
|
||||||
status.setAttribute("role", "status");
|
status.setAttribute("role", "status");
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: "OptyNumber", label: "OptyNumber" }, { key: "WinProb", label: "Win Probability", type: "number" },
|
{ key: "OptyNumber", label: "OptyNumber" }, { key: "WinProb", label: "Win Probability", type: "number" },
|
||||||
{ key: "Name", label: "Name" }, { key: "CurrencyCode", label: "CurrencyCode" },
|
{ key: "Name", label: "Name" }, { key: "CurrencyCode", label: "CurrencyCode" },
|
||||||
@@ -143,7 +144,7 @@ function createOrgChartView({ environment, tableSorting, model, loadLayout, icon
|
|||||||
];
|
];
|
||||||
const sorting = tableSorting.create("contact-opportunities", columns);
|
const sorting = tableSorting.create("contact-opportunities", columns);
|
||||||
let allItems = [], loading = false;
|
let allItems = [], loading = false;
|
||||||
for (const column of columns) { const th = el("th"); th.scope = "col"; sorting.header(th, column.key, column.label, renderRows); row.append(th); }
|
for (const column of columns) { const th = el("th"); th.scope = "col"; if (column.key === "Revenue") th.setAttribute("data-align", "money"); sorting.header(th, column.key, column.label, renderRows); row.append(th); }
|
||||||
head.append(row); table.append(head, body); surface.append(table);
|
head.append(row); table.append(head, body); surface.append(table);
|
||||||
const footer = el("footer", "oe-org-form-actions");
|
const footer = el("footer", "oe-org-form-actions");
|
||||||
const retry = button("Retry", "plus", () => load(), true);
|
const retry = button("Retry", "plus", () => load(), true);
|
||||||
@@ -167,6 +168,7 @@ function createOrgChartView({ environment, tableSorting, model, loadLayout, icon
|
|||||||
const row = el("tr"); row.setAttribute("aria-hidden", "true");
|
const row = el("tr"); row.setAttribute("aria-hidden", "true");
|
||||||
for (const column of columns) {
|
for (const column of columns) {
|
||||||
const cell = el("td"), bar = el("span", "oe-contact-skeleton");
|
const cell = el("td"), bar = el("span", "oe-contact-skeleton");
|
||||||
|
if (column.key === "Revenue") cell.setAttribute("data-align", "money");
|
||||||
bar.style.width = column.key === "Name" ? "90%" : column.key === "WinProb" || column.key === "StatusCode" ? "45%" : "70%";
|
bar.style.width = column.key === "Name" ? "90%" : column.key === "WinProb" || column.key === "StatusCode" ? "45%" : "70%";
|
||||||
cell.append(bar); row.append(cell);
|
cell.append(bar); row.append(cell);
|
||||||
}
|
}
|
||||||
@@ -177,7 +179,7 @@ function createOrgChartView({ environment, tableSorting, model, loadLayout, icon
|
|||||||
for (const item of sorting.select(allItems)) {
|
for (const item of sorting.select(allItems)) {
|
||||||
const row = el("tr");
|
const row = el("tr");
|
||||||
for (const key of ["OptyNumber", "WinProb", "Name", "CurrencyCode", "Revenue", "EffectiveDate", "StatusCode"]) {
|
for (const key of ["OptyNumber", "WinProb", "Name", "CurrencyCode", "Revenue", "EffectiveDate", "StatusCode"]) {
|
||||||
const cell = el("td"); let value = item[key] ?? "—";
|
const cell = el("td"); if (key === "Revenue") cell.setAttribute("data-align", "money"); let value = item[key] ?? "—";
|
||||||
if (key === "Name" && item.OptyNumber) {
|
if (key === "Name" && item.OptyNumber) {
|
||||||
const link = el("a", "", String(value)); link.href = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-detail?puid=" + encodeURIComponent(item.OptyNumber); link.target = "_blank"; link.rel = "noopener noreferrer"; cell.append(link);
|
const link = el("a", "", String(value)); link.href = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-detail?puid=" + encodeURIComponent(item.OptyNumber); link.target = "_blank"; link.rel = "noopener noreferrer"; cell.append(link);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ function createProductsDetails({
|
|||||||
} else {
|
} else {
|
||||||
const table = document.createElement("table");
|
const table = document.createElement("table");
|
||||||
table.className = "opportunities-extension-products-table";
|
table.className = "opportunities-extension-products-table";
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
table.setAttribute("aria-label", `Products for opportunity ${optyNumber}`);
|
table.setAttribute("aria-label", `Products for opportunity ${optyNumber}`);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@@ -123,6 +124,7 @@ function createProductsDetails({
|
|||||||
columns.forEach((column) => {
|
columns.forEach((column) => {
|
||||||
const header = document.createElement("th");
|
const header = document.createElement("th");
|
||||||
header.scope = "col";
|
header.scope = "col";
|
||||||
|
if (column.label === "Amount") header.setAttribute("data-align", "money");
|
||||||
header.textContent = column.label;
|
header.textContent = column.label;
|
||||||
headerRow.append(header);
|
headerRow.append(header);
|
||||||
});
|
});
|
||||||
@@ -133,6 +135,7 @@ function createProductsDetails({
|
|||||||
const productRow = document.createElement("tr");
|
const productRow = document.createElement("tr");
|
||||||
columns.forEach((column) => {
|
columns.forEach((column) => {
|
||||||
const productCell = document.createElement("td");
|
const productCell = document.createElement("td");
|
||||||
|
if (column.label === "Amount") productCell.setAttribute("data-align", "money");
|
||||||
if (column.render) {
|
if (column.render) {
|
||||||
productCell.className = "opportunities-extension-projection-cell";
|
productCell.className = "opportunities-extension-projection-cell";
|
||||||
productCell.append(column.render(product));
|
productCell.append(column.render(product));
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ function createRampProjectionView({ MODAL_ID, compare, createAlertIcon, createCl
|
|||||||
viewport.setAttribute("role", "region");
|
viewport.setAttribute("role", "region");
|
||||||
viewport.setAttribute("aria-label", "Monthly consumption comparison");
|
viewport.setAttribute("aria-label", "Monthly consumption comparison");
|
||||||
const table = node("table", "opportunities-extension-projection-table");
|
const table = node("table", "opportunities-extension-projection-table");
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
const caption = node("caption", "opportunities-extension-visually-hidden", `Monthly consumption comparison (${currency}). Difference is forecast minus calculated.`);
|
const caption = node("caption", "opportunities-extension-visually-hidden", `Monthly consumption comparison (${currency}). Difference is forecast minus calculated.`);
|
||||||
const head = node("thead"), headRow = node("tr"), tbody = node("tbody");
|
const head = node("thead"), headRow = node("tr"), tbody = node("tbody");
|
||||||
const sorting = tableSorting.create("consumption-projection", [
|
const sorting = tableSorting.create("consumption-projection", [
|
||||||
@@ -103,7 +104,7 @@ function createRampProjectionView({ MODAL_ID, compare, createAlertIcon, createCl
|
|||||||
const rowNodes = new Map();
|
const rowNodes = new Map();
|
||||||
const renderRows = () => tbody.replaceChildren(...sorting.select(result.rows).map(item => rowNodes.get(item)));
|
const renderRows = () => tbody.replaceChildren(...sorting.select(result.rows).map(item => rowNodes.get(item)));
|
||||||
for (const [key, label] of [["month", "Month"], ["actual", "Forecast"], ["calculated", "Calculated"], ["difference", "Difference"]]) {
|
for (const [key, label] of [["month", "Month"], ["actual", "Forecast"], ["calculated", "Calculated"], ["difference", "Difference"]]) {
|
||||||
const th = node("th"); th.scope = "col";
|
const th = node("th"); th.scope = "col"; if (key !== "month") th.setAttribute("data-align", "money");
|
||||||
sorting.header(th, key, label, renderRows); headRow.append(th);
|
sorting.header(th, key, label, renderRows); headRow.append(th);
|
||||||
}
|
}
|
||||||
head.append(headRow);
|
head.append(headRow);
|
||||||
@@ -114,7 +115,7 @@ function createRampProjectionView({ MODAL_ID, compare, createAlertIcon, createCl
|
|||||||
month.scope = "row";
|
month.scope = "row";
|
||||||
if (item.prorated) month.append(node("span", "opportunities-extension-projection-prorated", " (Prorated)"));
|
if (item.prorated) month.append(node("span", "opportunities-extension-projection-prorated", " (Prorated)"));
|
||||||
tr.append(month);
|
tr.append(month);
|
||||||
for (const value of [item.actual, item.calculated, item.difference]) tr.append(node("td", "", value === null ? "-" : formatAmount(value, currency)));
|
for (const value of [item.actual, item.calculated, item.difference]) { const cell = node("td", "", value === null ? "-" : formatAmount(value, currency)); cell.setAttribute("data-align", "money"); tr.append(cell); }
|
||||||
rowNodes.set(item, tr);
|
rowNodes.set(item, tr);
|
||||||
}
|
}
|
||||||
renderRows();
|
renderRows();
|
||||||
|
|||||||
@@ -139,3 +139,8 @@
|
|||||||
.opportunities-extension-ratecard-overlay, .opportunities-extension-ratecard-drawer, .opportunities-extension-ratecard-skeleton,
|
.opportunities-extension-ratecard-overlay, .opportunities-extension-ratecard-drawer, .opportunities-extension-ratecard-skeleton,
|
||||||
.opportunities-extension-ratecard-overlay[data-closing="true"], .opportunities-extension-ratecard-overlay[data-closing="true"] .opportunities-extension-ratecard-drawer { animation: none; transition: none; }
|
.opportunities-extension-ratecard-overlay[data-closing="true"], .opportunities-extension-ratecard-overlay[data-closing="true"] .opportunities-extension-ratecard-drawer { animation: none; transition: none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opportunities-extension-ratecard-toolbar { flex-wrap: wrap; }
|
||||||
|
.opportunities-extension-ratecard-order-filter { position: relative; min-width: 0; }
|
||||||
|
.opportunities-extension-ratecard-order-filter .opportunities-extension-customer-filter-trigger { min-height: 38px; padding-block: 8px; font-size: 13px; }
|
||||||
|
.opportunities-extension-ratecard-order-filter .opportunities-extension-customer-filter-panel { width: min(320px, calc(100vw - 80px)); max-height: 45vh; overflow: auto; }
|
||||||
|
|||||||
@@ -65,12 +65,80 @@ function createRatecardView({ ACCOUNT_PLANS_MODAL_ID, columns, formatValue, sele
|
|||||||
search.setAttribute("aria-label", "Search rate card");
|
search.setAttribute("aria-label", "Search rate card");
|
||||||
const count = element("span", "count", "Loading rate card...");
|
const count = element("span", "count", "Loading rate card...");
|
||||||
count.setAttribute("role", "status");
|
count.setAttribute("role", "status");
|
||||||
toolbar.append(search, count);
|
const orderFilter = element("div", "order-filter");
|
||||||
|
const filterElement = (tag, suffix, text) => {
|
||||||
|
const node = element(tag, null, text);
|
||||||
|
node.className = `opportunities-extension-customer-filter-${suffix}`;
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
const orderButton = filterElement("button", "trigger", "All order numbers");
|
||||||
|
orderButton.type = "button";
|
||||||
|
orderButton.disabled = true;
|
||||||
|
orderButton.setAttribute("aria-label", "Filter by Order Number");
|
||||||
|
orderButton.setAttribute("aria-expanded", "false");
|
||||||
|
const orderPanel = filterElement("section", "panel");
|
||||||
|
orderPanel.id = "opportunities-extension-ratecard-orders";
|
||||||
|
orderButton.setAttribute("aria-controls", orderPanel.id);
|
||||||
|
orderPanel.hidden = true;
|
||||||
|
const orderSearch = filterElement("input", "search");
|
||||||
|
orderSearch.type = "search";
|
||||||
|
orderSearch.placeholder = "Search order numbers";
|
||||||
|
orderSearch.setAttribute("aria-label", "Search order numbers");
|
||||||
|
const allLabel = filterElement("label", "select-all");
|
||||||
|
const allOrders = element("input");
|
||||||
|
allOrders.type = "checkbox";
|
||||||
|
allLabel.append(allOrders, element("span", null, "Select all"));
|
||||||
|
const orderList = filterElement("div", "list");
|
||||||
|
orderPanel.append(orderSearch, allLabel, orderList);
|
||||||
|
orderFilter.append(orderButton, orderPanel);
|
||||||
|
toolbar.append(search, orderFilter, count);
|
||||||
|
let orders = [], selectedOrders = new Set();
|
||||||
|
const orderKey = item => String(item.orderNumber ?? "").trim();
|
||||||
|
function closeOrders() {
|
||||||
|
orderPanel.hidden = true;
|
||||||
|
orderButton.setAttribute("aria-expanded", "false");
|
||||||
|
}
|
||||||
|
function updateOrders() {
|
||||||
|
const all = orders.length > 0 && selectedOrders.size === orders.length;
|
||||||
|
allOrders.checked = all;
|
||||||
|
allOrders.indeterminate = selectedOrders.size > 0 && !all;
|
||||||
|
orderButton.textContent = all ? "All order numbers" : `${selectedOrders.size} order numbers selected`;
|
||||||
|
orderList.textContent = "";
|
||||||
|
const query = orderSearch.value.trim().toLowerCase();
|
||||||
|
orders.filter(value => (value || "Not provided").toLowerCase().includes(query)).forEach(value => {
|
||||||
|
const label = filterElement("label", "option");
|
||||||
|
const checkbox = element("input");
|
||||||
|
checkbox.type = "checkbox";
|
||||||
|
checkbox.checked = selectedOrders.has(value);
|
||||||
|
checkbox.addEventListener("change", () => {
|
||||||
|
if (checkbox.checked) selectedOrders.add(value); else selectedOrders.delete(value);
|
||||||
|
allOrders.checked = selectedOrders.size === orders.length;
|
||||||
|
allOrders.indeterminate = selectedOrders.size > 0 && !allOrders.checked;
|
||||||
|
orderButton.textContent = allOrders.checked ? "All order numbers" : `${selectedOrders.size} order numbers selected`;
|
||||||
|
renderRows();
|
||||||
|
});
|
||||||
|
label.append(checkbox, element("span", null, value || "Not provided"));
|
||||||
|
orderList.append(label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
orderButton.addEventListener("click", () => {
|
||||||
|
orderPanel.hidden = !orderPanel.hidden;
|
||||||
|
orderButton.setAttribute("aria-expanded", String(!orderPanel.hidden));
|
||||||
|
if (!orderPanel.hidden) orderSearch.focus();
|
||||||
|
});
|
||||||
|
orderSearch.addEventListener("input", updateOrders);
|
||||||
|
allOrders.addEventListener("change", () => {
|
||||||
|
selectedOrders = new Set(allOrders.checked ? orders : []);
|
||||||
|
updateOrders();
|
||||||
|
renderRows();
|
||||||
|
});
|
||||||
|
root.addEventListener("click", event => { if (!orderFilter.contains(event.target)) closeOrders(); });
|
||||||
const viewport = element("div", "viewport");
|
const viewport = element("div", "viewport");
|
||||||
viewport.tabIndex = 0;
|
viewport.tabIndex = 0;
|
||||||
viewport.setAttribute("role", "region");
|
viewport.setAttribute("role", "region");
|
||||||
viewport.setAttribute("aria-label", "Rate card items");
|
viewport.setAttribute("aria-label", "Rate card items");
|
||||||
const table = element("table", "table");
|
const table = element("table", "table");
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
const caption = element("caption", null, "Rate card items");
|
const caption = element("caption", null, "Rate card items");
|
||||||
caption.className = "opportunities-extension-visually-hidden";
|
caption.className = "opportunities-extension-visually-hidden";
|
||||||
const colgroup = element("colgroup");
|
const colgroup = element("colgroup");
|
||||||
@@ -90,6 +158,7 @@ function createRatecardView({ ACCOUNT_PLANS_MODAL_ID, columns, formatValue, sele
|
|||||||
colgroup.append(col);
|
colgroup.append(col);
|
||||||
const th = element("th");
|
const th = element("th");
|
||||||
th.scope = "col";
|
th.scope = "col";
|
||||||
|
if (column.type === "price") th.setAttribute("data-align", "money");
|
||||||
th.setAttribute("aria-sort", "none");
|
th.setAttribute("aria-sort", "none");
|
||||||
const sort = element("button", "sort");
|
const sort = element("button", "sort");
|
||||||
sort.type = "button";
|
sort.type = "button";
|
||||||
@@ -139,7 +208,7 @@ function createRatecardView({ ACCOUNT_PLANS_MODAL_ID, columns, formatValue, sele
|
|||||||
function renderRows() {
|
function renderRows() {
|
||||||
if (!alive()) return;
|
if (!alive()) return;
|
||||||
body.textContent = "";
|
body.textContent = "";
|
||||||
const visible = selectItems(current.items, search.value, current.sortKey, current.direction);
|
const visible = selectItems(current.items.filter(item => selectedOrders.has(orderKey(item))), search.value, current.sortKey, current.direction);
|
||||||
count.textContent = `${visible.length} of ${current.items.length} items`;
|
count.textContent = `${visible.length} of ${current.items.length} items`;
|
||||||
if (!visible.length) {
|
if (!visible.length) {
|
||||||
showNotice(current.items.length ? "No matching rate card items." : "No rate card items found for this plan.");
|
showNotice(current.items.length ? "No matching rate card items." : "No rate card items found for this plan.");
|
||||||
@@ -148,6 +217,7 @@ function createRatecardView({ ACCOUNT_PLANS_MODAL_ID, columns, formatValue, sele
|
|||||||
const row = element("tr");
|
const row = element("tr");
|
||||||
columns.forEach(column => {
|
columns.forEach(column => {
|
||||||
const cell = element("td", null, formatValue(item, column));
|
const cell = element("td", null, formatValue(item, column));
|
||||||
|
if (column.type === "price") cell.setAttribute("data-align", "money");
|
||||||
if (["price", "number", "percent"].includes(column.type)) cell.className = "opportunities-extension-ratecard-numeric";
|
if (["price", "number", "percent"].includes(column.type)) cell.className = "opportunities-extension-ratecard-numeric";
|
||||||
row.append(cell);
|
row.append(cell);
|
||||||
});
|
});
|
||||||
@@ -172,9 +242,12 @@ function createRatecardView({ ACCOUNT_PLANS_MODAL_ID, columns, formatValue, sele
|
|||||||
if (event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
if (!orderPanel.hidden) { closeOrders(); orderButton.focus(); return; }
|
||||||
closeRatecardDrawer();
|
closeRatecardDrawer();
|
||||||
} else if (event.key === "Tab") {
|
} else if (event.key === "Tab") {
|
||||||
const controls = [close, search, viewport, ...headRow.querySelectorAll("button")];
|
const controls = [close, search, ...(!orderButton.disabled ? [orderButton] : []),
|
||||||
|
...(!orderPanel.hidden ? [orderSearch, allOrders, ...orderList.querySelectorAll("input")] : []),
|
||||||
|
viewport, ...headRow.querySelectorAll("button")];
|
||||||
const index = controls.indexOf(document.activeElement);
|
const index = controls.indexOf(document.activeElement);
|
||||||
if (index < 0 || (event.shiftKey && index === 0) || (!event.shiftKey && index === controls.length - 1)) {
|
if (index < 0 || (event.shiftKey && index === 0) || (!event.shiftKey && index === controls.length - 1)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -196,6 +269,10 @@ function createRatecardView({ ACCOUNT_PLANS_MODAL_ID, columns, formatValue, sele
|
|||||||
showItems(items) {
|
showItems(items) {
|
||||||
if (!alive()) return;
|
if (!alive()) return;
|
||||||
current.items = items;
|
current.items = items;
|
||||||
|
orders = [...new Set(items.map(orderKey))].sort((a, b) => a.localeCompare(b, "en", { numeric: true }));
|
||||||
|
selectedOrders = new Set(orders);
|
||||||
|
orderButton.disabled = !orders.length;
|
||||||
|
updateOrders();
|
||||||
current.status = "ready";
|
current.status = "ready";
|
||||||
panel.setAttribute("aria-busy", "false");
|
panel.setAttribute("aria-busy", "false");
|
||||||
renderRows();
|
renderRows();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ function createServiceRequestsView({ MODAL_ID, createCloseIcon, formatOracleResp
|
|||||||
const sortColumns = columns.map(([key, , value]) => ({ key, value, type: ["created", "lastUpdated"].includes(key) ? "date" : "text" }));
|
const sortColumns = columns.map(([key, , value]) => ({ key, value, type: ["created", "lastUpdated"].includes(key) ? "date" : "text" }));
|
||||||
function createTable(items, loading = false, sorting = tableSorting.create("service-requests", sortColumns), onSort) {
|
function createTable(items, loading = false, sorting = tableSorting.create("service-requests", sortColumns), onSort) {
|
||||||
const table = element("table", "opportunities-extension-service-requests-table");
|
const table = element("table", "opportunities-extension-service-requests-table");
|
||||||
|
table.setAttribute("data-table-layout", "standard");
|
||||||
table.setAttribute("aria-label", "Customer service requests");
|
table.setAttribute("aria-label", "Customer service requests");
|
||||||
const group = element("colgroup");
|
const group = element("colgroup");
|
||||||
const head = element("thead");
|
const head = element("thead");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ test("every source belongs to an explicit runtime composition, with no duplicate
|
|||||||
const source = read(file);
|
const source = read(file);
|
||||||
assert.doesNotMatch(source, /\beval\s*\(|\bnew\s+Function\s*\(|\bimportScripts\s*\(|\brequire\s*\(|^\s*(?:import|export)\s/m, file);
|
assert.doesNotMatch(source, /\beval\s*\(|\bnew\s+Function\s*\(|\bimportScripts\s*\(|\brequire\s*\(|^\s*(?:import|export)\s/m, file);
|
||||||
if (file.endsWith("/application.js") || file.endsWith("/main.js")) continue;
|
if (file.endsWith("/application.js") || file.endsWith("/main.js")) continue;
|
||||||
|
if (file === "src/features/org-chart/calling-codes.js") { assert.match(source, /const orgChartCallingCodes\s*=/); continue; }
|
||||||
const declaration = source.match(/^function (\w+)\(/m)[1];
|
const declaration = source.match(/^function (\w+)\(/m)[1];
|
||||||
for (const factory of factories.filter(name => name !== declaration)) {
|
for (const factory of factories.filter(name => name !== declaration)) {
|
||||||
assert.doesNotMatch(source, new RegExp(`\\b${factory}\\s*\\(`), `${file} must receive dependencies, not construct ${factory}`);
|
assert.doesNotMatch(source, new RegExp(`\\b${factory}\\s*\\(`), `${file} must receive dependencies, not construct ${factory}`);
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ test("full-page debug CSS defines responsive bounds and readable semantic colors
|
|||||||
test("scoped debug styles are packaged after the unchanged main stylesheet", () => {
|
test("scoped debug styles are packaged after the unchanged main stylesheet", () => {
|
||||||
const root = path.join(__dirname, "..");
|
const root = path.join(__dirname, "..");
|
||||||
const css = ["src/content/styles/redwood.css", "src/features/debug/styles.css", "src/features/opportunities/styles.css", "src/features/install-base/styles.css", "src/features/service-requests/styles.css", "src/features/consumption/plan-detail.css", "src/features/ratecard/styles.css", "src/features/opportunities/customer-table-search.css", "src/features/opportunities/calendar.css"]
|
const css = ["src/content/styles/redwood.css", "src/features/debug/styles.css", "src/features/opportunities/styles.css", "src/features/install-base/styles.css", "src/features/service-requests/styles.css", "src/features/consumption/plan-detail.css", "src/features/ratecard/styles.css", "src/features/opportunities/customer-table-search.css", "src/features/opportunities/calendar.css"]
|
||||||
.concat("src/features/manage-time/styles.css", "src/features/products/ramp-projection.css", "src/features/org-chart/styles.css")
|
.concat("src/features/manage-time/styles.css", "src/features/products/ramp-projection.css", "src/features/org-chart/styles.css", "src/content/styles/tables.css")
|
||||||
.map(file => fs.readFileSync(path.join(root, file), "utf8")).join("\n");
|
.map(file => fs.readFileSync(path.join(root, file), "utf8")).join("\n");
|
||||||
assert.ok(composeScript("content").includes(`const redwoodStyles = ${JSON.stringify(css)};`));
|
assert.ok(composeScript("content").includes(`const redwoodStyles = ${JSON.stringify(css)};`));
|
||||||
assert.ok(!composeScript("background").includes("opportunities-extension-debug-tabs"));
|
assert.ok(!composeScript("background").includes("opportunities-extension-debug-tabs"));
|
||||||
|
|||||||
@@ -319,6 +319,6 @@ test("table styles reserve compact widths, prioritize Name/Customer and keep nes
|
|||||||
assert.ok(remaining > 0);
|
assert.ok(remaining > 0);
|
||||||
assert.ok(remaining / 2 >= 12 + 114, "four customer controls fit after the left inset");
|
assert.ok(remaining / 2 >= 12 + 114, "four customer controls fit after the left inset");
|
||||||
}
|
}
|
||||||
const allCss = ["src/content/styles/redwood.css", "src/features/debug/styles.css", "src/features/opportunities/styles.css", "src/features/install-base/styles.css", "src/features/service-requests/styles.css", "src/features/consumption/plan-detail.css", "src/features/ratecard/styles.css", "src/features/opportunities/customer-table-search.css", "src/features/opportunities/calendar.css", "src/features/manage-time/styles.css", "src/features/products/ramp-projection.css", "src/features/org-chart/styles.css"].map(read).join("\n");
|
const allCss = ["src/content/styles/redwood.css", "src/features/debug/styles.css", "src/features/opportunities/styles.css", "src/features/install-base/styles.css", "src/features/service-requests/styles.css", "src/features/consumption/plan-detail.css", "src/features/ratecard/styles.css", "src/features/opportunities/customer-table-search.css", "src/features/opportunities/calendar.css", "src/features/manage-time/styles.css", "src/features/products/ramp-projection.css", "src/features/org-chart/styles.css", "src/content/styles/tables.css"].map(read).join("\n");
|
||||||
assert.ok(composeScript("content").includes(`const redwoodStyles = ${JSON.stringify(allCss)};`));
|
assert.ok(composeScript("content").includes(`const redwoodStyles = ${JSON.stringify(allCss)};`));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user