diff --git a/web/gui-v2/src/components/AddRemoveColumnDialog.jsx b/web/gui-v2/src/components/AddRemoveColumnDialog.jsx
index 7255f97e..e5dea698 100644
--- a/web/gui-v2/src/components/AddRemoveColumnDialog.jsx
+++ b/web/gui-v2/src/components/AddRemoveColumnDialog.jsx
@@ -19,7 +19,7 @@ const styles = {
columnDialogContents: css`
font-family: GTZirkonLight;
padding: 0 0.5rem 0.5rem;
- width: 320px;
+ width: 400px;
`,
columnDialogTitle: css`
font-family: GTZirkonRegular;
diff --git a/web/gui-v2/src/components/CellStat.jsx b/web/gui-v2/src/components/CellStat.jsx
index 7dd08c50..6b4b708a 100644
--- a/web/gui-v2/src/components/CellStat.jsx
+++ b/web/gui-v2/src/components/CellStat.jsx
@@ -6,7 +6,13 @@ import { commas } from '../util';
const styles = {
cell: css`
display: grid;
- grid-template-columns: 60% 40%;
+ gap: 0.5rem;
+ /*
+ * 3.2em was chosen because it was a touch above the size needed to safely
+ * display a 4-digit company ranking. If we ever get above 10,000 companies
+ * and things start looking funky, just increase this accordingly.
+ */
+ grid-template-columns: 1fr 3.2em;
justify-content: center;
& > div {
@@ -15,7 +21,6 @@ const styles = {
.rank {
color: #a0a0a0;
- margin-left: 0.5rem;
}
`,
};
@@ -23,9 +28,11 @@ const styles = {
const CellStat = ({data, colKey}) => {
return (
-
{commas(data.total)}
+
+ { data?.total === null ? 'n/a' : commas(data.total) }
+
- { data?.total === 0 ? '---' : <>#{data.rank}> }
+ { (data?.total === 0 || data?.total === null) ? '---' : <>#{data.rank}> }
);
diff --git a/web/gui-v2/src/components/HeaderSlider.jsx b/web/gui-v2/src/components/HeaderSlider.jsx
index ac159289..8583eebb 100644
--- a/web/gui-v2/src/components/HeaderSlider.jsx
+++ b/web/gui-v2/src/components/HeaderSlider.jsx
@@ -37,9 +37,10 @@ const HeaderSlider = ({
);
// Debounce handler for propagating internal changes to the outside
+ const externalHandler = (newVal) => onChange(newVal);
const handleExternalChange = useMemo(() => {
- return debounce(onChange, 300);
- }, [onChange]);
+ return debounce(externalHandler, 300);
+ }, []);
// Trigger external state change
useEffect(
diff --git a/web/gui-v2/src/components/ListViewTable.jsx b/web/gui-v2/src/components/ListViewTable.jsx
index 38abc421..c28b3471 100644
--- a/web/gui-v2/src/components/ListViewTable.jsx
+++ b/web/gui-v2/src/components/ListViewTable.jsx
@@ -111,6 +111,7 @@ const styles = {
const DATAKEYS_WITH_SUBKEYS = [
"articles",
"patents",
+ "other_metrics",
];
const DEFAULT_COLUMNS = columnDefinitions
@@ -125,13 +126,14 @@ const SLIDER_COLUMNS = columnDefinitions
.filter(colDef => colDef.type === "slider")
.map(colDef => colDef.key);
+const ALL_COLUMNS = [
+ ...DROPDOWN_COLUMNS,
+ ...SLIDER_COLUMNS,
+];
+
const DEFAULT_FILTER_VALUES = {
- name: [],
- country: [],
- continent: [],
- stage: [],
- ai_pubs: [0, 100],
- ai_patents: [0, 100],
+ ...DROPDOWN_COLUMNS.reduce((obj, e) => { obj[e] = []; return obj; }, {}),
+ ...SLIDER_COLUMNS.reduce((obj, e) => { obj[e] = [0, 100]; return obj; }, {}),
};
const initialVal = (key) => {
return DEFAULT_FILTER_VALUES[key]?.join(',') ?? '';
@@ -149,7 +151,6 @@ const AGGREGATE_SUM_COLUMNS = [
'ai_patents',
];
-
// Determine whether a given row matches the filters and/or selected group
const filterRow = (row, filters, selectedGroupMembers) => {
const filterKeys = Object.keys(filters);
@@ -194,10 +195,6 @@ const filterRow = (row, filters, selectedGroupMembers) => {
if ( rowVal < min || ( max < 100 && max < rowVal) ) {
return false;
}
- } else if ( colDef.type === "stock" ) {
- // TODO: Figure out how we're filtering the `market_list` column
- // -- Actually - are we even wanting this column, or did I just make
- // it as a placeholder?
} else {
console.error(`Invalid column type for key '${colDef.key}': column.type should be either "dropdown" or "slider" but is instead "${colDef.type}"`);
}
@@ -236,17 +233,10 @@ const ListViewTable = ({
// Store filters via the URL parameters, making the values (and setters)
// accessible via an object.
const filters = useMultiState(
- {
- name: useQueryParamString('name', initialVal('name')),
- country: useQueryParamString('country', initialVal('country')),
- continent: useQueryParamString('continent', initialVal('continent')),
- stage: useQueryParamString('stage', initialVal('stage')),
- // ...
- ai_pubs: useQueryParamString('ai_pubs', initialVal('ai_pubs')),
- ai_patents: useQueryParamString('ai_patents', initialVal('ai_patents')),
- // ...
- // market_list: useQueryParamString('market_list', ''),
- },
+ ALL_COLUMNS.reduce((obj, e) => {
+ obj[e] = useQueryParamString(e, initialVal(e));
+ return obj;
+ }, {}),
(key, val) => {
if ( DROPDOWN_COLUMNS.includes(key) ) {
let result = val.split(',').filter(e => e !== "");
@@ -422,11 +412,6 @@ const ListViewTable = ({
/>
);
break;
- case 'stock':
- display_name = (
- colDef.title
- );
- break;
default:
display_name = colDef.title;
}
diff --git a/web/gui-v2/src/static_data/table_columns.js b/web/gui-v2/src/static_data/table_columns.js
index c18954fe..a354c609 100644
--- a/web/gui-v2/src/static_data/table_columns.js
+++ b/web/gui-v2/src/static_data/table_columns.js
@@ -12,6 +12,7 @@ const styles = {
}
`,
sliderColumn: css`
+ min-width: 100px;
width: 120px;
.MuiButtonBase-root {
@@ -20,6 +21,39 @@ const styles = {
`,
};
+/**
+ * Helper function to define the `extract` and `format` functions of slider
+ * fields in a consistent way across all columns.
+ *
+ * @param {string} dataKey
+ * @param {string} dataSubkey
+ * @returns {{
+ * css: SerializedStyles,
+ * dataKey: string,
+ * dataSubkey: string,
+ * extract: (val: any, row: object) => any,
+ * format: (val: any, row: object) => ReactNode,
+ * initialCol: boolean,
+ * sortable: boolean,
+ * type: 'dropdown'|'slider',
+ * }}
+ */
+const generateSliderColDef = (dataKey, dataSubkey) => {
+ return {
+ css: styles.sliderColumn,
+ dataKey,
+ dataSubkey,
+ extract: (_val, row) => {
+ const res = row[dataKey][dataSubkey].total;
+ return res === null ? 0 : res;
+ },
+ format: (_val, row) => ,
+ initialCol: false,
+ sortable: true,
+ type: 'slider',
+ }
+};
+
export default [
{
title: "Company",
@@ -33,38 +67,155 @@ export default [
{ title: "Country", key: "country", initialCol: true, type: 'dropdown' },
{ title: "Region", key: "continent", initialCol: true, type: 'dropdown' },
{ title: "Stage", key: "stage", initialCol: true, type: 'dropdown' },
+
+ {
+ title: "All publications",
+ key: "all_pubs",
+ ...generateSliderColDef("articles", "all_publications"),
+ },
+ {
+ title: "Citation counts",
+ key: "citations",
+ ...generateSliderColDef("articles", "citation_counts"),
+ },
{
title: "AI publications",
key: "ai_pubs",
- dataKey: "articles",
- dataSubkey: "ai_publications",
- css: styles.sliderColumn,
+ ...generateSliderColDef("articles", "ai_publications"),
initialCol: true,
- extract: (_val, row) => row.articles.ai_publications.total,
- format: (_val, row) => ,
- sortable: true,
- type: 'slider',
+ },
+ {
+ title: "AI publications in top conferences",
+ key: "ai_pubs_top_conf",
+ ...generateSliderColDef("articles", "ai_pubs_top_conf"),
},
{
title: "AI patents",
key: "ai_patents",
- dataKey: "patents",
- dataSubkey: "ai_patents",
- css: styles.sliderColumn,
+ ...generateSliderColDef("patents", "ai_patents"),
initialCol: true,
- extract: (_val, row) => row.patents.ai_patents.total,
- format: (_val, row) => ,
- sortable: true,
- type: 'slider',
},
- // { title: "AI publication intensity", key: "ai_pubs_int", initialCol: false, type: 'slider' },
- // { title: "NLP publications", key: "nlp_pubs", initialCol: true },
- // { title: "NLP patents", key: "nlp_patents", initialCol: true },
- // { title: "CV publications", key: "cv_pubs", initialCol: false },
- // { title: "CV patents", key: "cv_patents", initialCol: false },
- // { title: "Robotics publications", key: "ro_pubs", initialCol: false },
- // { title: "Robotics patents", key: "ro_patents", initialCol: false },
- // { title: "tt1 jobs (??)", key: "tt1_jobs", initialCol: false },
- // { title: "AI jobs", key: "ai_jobs", initialCol: false },
- // { title: "Stock ticker", key: "market_list", type: "stock" },
+ {
+ title: "CV publications",
+ key: "cv_pubs",
+ ...generateSliderColDef("articles", "cv_pubs"),
+ },
+ {
+ title: "NLP publications",
+ key: "nlp_pubs",
+ ...generateSliderColDef("articles", "nlp_pubs"),
+ },
+ {
+ title: "Robotics publications",
+ key: "ro_pubs",
+ ...generateSliderColDef("articles", "robotics_pubs"),
+ },
+
+ {
+ title: "Agricultural patents",
+ key: "agri_patents",
+ ...generateSliderColDef("patents", "Agricultural"),
+ },
+ {
+ title: "Banking and finance patents",
+ key: "finance_patents",
+ ...generateSliderColDef("patents", "Banking_and_Finance"),
+ },
+ {
+ title: "Business patents",
+ key: "business_patents",
+ ...generateSliderColDef("patents", "Business"),
+ },
+ {
+ title: "Computing in government patents",
+ key: "comp_in_gov_patents",
+ ...generateSliderColDef("patents", "Computing_in_Government"),
+ },
+ {
+ title: "Document management and publishing patents",
+ key: "doc_mgt_patents",
+ ...generateSliderColDef("patents", "Document_Mgt_and_Publishing"),
+ },
+ {
+ title: "Education patents",
+ key: "edu_patents",
+ ...generateSliderColDef("patents", "Education"),
+ },
+ {
+ title: "Energy patents",
+ key: "energy_mgt_patents",
+ ...generateSliderColDef("patents", "Energy_Management"),
+ },
+ {
+ title: "Entertainment patents",
+ key: "entertain_patents",
+ ...generateSliderColDef("patents", "Entertainment"),
+ },
+ {
+ title: "Industrial and manufacturing patents",
+ key: "industry_patents",
+ ...generateSliderColDef("patents", "Industrial_and_Manufacturing"),
+ },
+ {
+ title: "Life sciences patents",
+ key: "life_patents",
+ ...generateSliderColDef("patents", "Life_Sciences"),
+ },
+ {
+ title: "Military patents",
+ key: "mil_patents",
+ ...generateSliderColDef("patents", "Military"),
+ },
+ {
+ title: "Nanotechnology patents",
+ key: "nano_patents",
+ ...generateSliderColDef("patents", "Nanotechnology"),
+ },
+ {
+ title: "Networks patents",
+ key: "network_patents",
+ ...generateSliderColDef("patents", "Networks__eg_social_IOT_etc"),
+ },
+ {
+ title: "Personal devices and computing patents",
+ key: "personal_comp_patents",
+ ...generateSliderColDef("patents", "Personal_Devices_and_Computing"),
+ },
+ {
+ title: "Physical sciences and engineering patents",
+ key: "phys_sci_patents",
+ ...generateSliderColDef("patents", "Physical_Sciences_and_Engineering"),
+ },
+ {
+ title: "Security patents",
+ key: "security_patents",
+ ...generateSliderColDef("patents", "Security__eg_cybersecurity"),
+ },
+ {
+ title: "Semiconductor patents",
+ key: "semiconductor_patents",
+ ...generateSliderColDef("patents", "Semiconductors"),
+ },
+ {
+ title: "Telecommunications patents",
+ key: "telecom_patents",
+ ...generateSliderColDef("patents", "Telecommunications"),
+ },
+ {
+ title: "Transportation patents",
+ key: "transport_patents",
+ ...generateSliderColDef("patents", "Transportation"),
+ },
+
+ {
+ title: "AI jobs",
+ key: "ai_jobs",
+ ...generateSliderColDef("other_metrics", "ai_jobs"),
+ initialCol: true,
+ },
+ {
+ title: "Tech Tier 1 jobs",
+ key: "tt1_jobs",
+ ...generateSliderColDef("other_metrics", "tt1_jobs"),
+ },
];