Repository: strudel-science/strudel-kit (language: python)

Issue:
Add typescript-based task flow config



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 69380db1..a5ebc4b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,28 @@ __pycache__
 
 # distribution files
 dist/
+
+# generouted
+router.ts
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/scripts/update_templates.py b/scripts/update_templates.py
new file mode 100644
index 00000000..ff6326af
--- /dev/null
+++ b/scripts/update_templates.py
@@ -0,0 +1,50 @@
+import shutil
+import os
+import json
+from pathlib import Path
+
+
+# Run this script from the root of the strudel-kit repo
+# to update the strudel-cookiecutter template files from
+# the files in strudel-taskflows.
+
+# Get all the taskflow directories that are in strudel-taskflows
+taskflows = os.listdir('./strudel-taskflows/src/pages')
+taskflows.remove('index.tsx')
+taskflows.remove('playground')
+
+# Update each taskflow cookiecutter template
+for tf in taskflows:
+  # Source of the updated template files
+  taskflow_src = Path(f'./strudel-taskflows/src/pages/{tf}')
+  # Root of the cookiecutter template
+  cookiecutter_root = Path(f'./strudel-cookiecutter/{tf}')
+  # Destination of the new template files
+  cookiecutter_dest = Path(f'{cookiecutter_root}/{{@cookiecutter.name@}}')
+  # Remove existing files inside the cookiecutter template
+  if cookiecutter_dest.exists() and cookiecutter_dest.is_dir():
+    shutil.rmtree(cookiecutter_dest)
+  # Create a root cookiecutter directory for new taskflows
+  elif not cookiecutter_root.exists():
+    os.makedirs(cookiecutter_root)
+    # Include a default cookiecutter.json for the new taskflow
+    cookiecutterJson = {
+      "name": "my-taskflow",
+      "_jinja2_env_vars": {
+        "variable_start_string": "{@", 
+        "variable_end_string": "@}"
+      }
+    }
+    with open(f'{cookiecutter_root}/cookiecutter.json', 'w') as file:
+      json.dump(cookiecutterJson, file)
+  # Copy and paste the new template files into the cookiecutter template
+  shutil.copytree(taskflow_src, cookiecutter_dest)
+
+# Update the base app template
+base_src = Path('./strudel-taskflows/')
+base_dest = Path('./strudel-cookiecutter/base/{@cookiecutter.name@}')
+if base_dest.exists() and base_dest.is_dir():
+    shutil.rmtree(base_dest)
+# Copy and paste the whole strudel-taskflows app
+# but ignore the taskflow and node_modules directories
+shutil.copytree(base_src, base_dest, ignore=shutil.ignore_patterns(*taskflows, 'node_modules'))
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/.env b/strudel-cookiecutter/base/{@cookiecutter.name@}/.env
deleted file mode 100644
index 5f841f19..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/.env
+++ /dev/null
@@ -1,2 +0,0 @@
-PUBLIC_URL=/
-FAST_REFRESH=false
\ No newline at end of file
diff --git a/strudel-demo-app/COPYRIGHT.md b/strudel-cookiecutter/base/{@cookiecutter.name@}/COPYRIGHT.md
similarity index 100%
rename from strudel-demo-app/COPYRIGHT.md
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/COPYRIGHT.md
diff --git a/strudel-demo-app/LICENSE b/strudel-cookiecutter/base/{@cookiecutter.name@}/LICENSE
similarity index 100%
rename from strudel-demo-app/LICENSE
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/LICENSE
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/package-lock.json b/strudel-cookiecutter/base/{@cookiecutter.name@}/package-lock.json
index 137513a8..a2ec413b 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/package-lock.json
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/package-lock.json
@@ -1,15 +1,17 @@
 {
-  "name": "{@ cookiecutter.name @}",
+  "name": "strudel-demo-app",
   "version": "0.0.0",
   "lockfileVersion": 3,
   "requires": true,
+  "//": "strudel-kit-variable-next-line",
   "packages": {
     "": {
-      "name": "{@ cookiecutter.name @}p",
+      "name": "strudel-demo-app",
       "version": "0.0.0",
       "dependencies": {
         "@emotion/react": "^11.11.4",
         "@emotion/styled": "^11.11.0",
+        "@generouted/react-router": "^1.19.3",
         "@mui/icons-material": "^5.15.14",
         "@mui/lab": "^5.0.0-alpha.169",
         "@mui/material": "^5.15.14",
@@ -35,7 +37,7 @@
         "eslint-plugin-react-hooks": "^4.6.0",
         "eslint-plugin-react-refresh": "^0.4.6",
         "typescript": "^5.2.2",
-        "vite": "^5.2.0"
+        "vite": "^5.2.10"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -586,7 +588,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "aix"
@@ -602,7 +603,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -618,7 +618,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -634,7 +633,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -650,7 +648,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -666,7 +663,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -682,7 +678,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "freebsd"
@@ -698,7 +693,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "freebsd"
@@ -714,7 +708,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -730,7 +723,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -746,7 +738,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -762,7 +753,6 @@
       "cpu": [
         "loong64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -778,7 +768,6 @@
       "cpu": [
         "mips64el"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -794,7 +783,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -810,7 +798,6 @@
       "cpu": [
         "riscv64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -826,7 +813,6 @@
       "cpu": [
         "s390x"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -842,7 +828,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -858,7 +843,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "netbsd"
@@ -874,7 +858,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "openbsd"
@@ -890,7 +873,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "sunos"
@@ -906,7 +888,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -922,7 +903,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -938,7 +918,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -1074,6 +1053,20 @@
       "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
       "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
     },
+    "node_modules/@generouted/react-router": {
+      "version": "1.19.3",
+      "resolved": "https://registry.npmjs.org/@generouted/react-router/-/react-router-1.19.3.tgz",
+      "integrity": "sha512-3/Y0j302HnwxyU0KOoCOFSVwJZN4eD73+CVtSaOKUnLbGW1ftP1dT8Eur14PALGvZOVR4gsFrUJFq6a4c/kWvQ==",
+      "dependencies": {
+        "fast-glob": "^3.3.2",
+        "generouted": "^1.19.3"
+      },
+      "peerDependencies": {
+        "react": ">=18",
+        "react-router-dom": ">=6",
+        "vite": ">=4"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.14",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -1620,7 +1613,6 @@
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
       "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "2.0.5",
         "run-parallel": "^1.1.9"
@@ -1633,7 +1625,6 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
       "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true,
       "engines": {
         "node": ">= 8"
       }
@@ -1642,7 +1633,6 @@
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
       "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.scandir": "2.1.5",
         "fastq": "^1.6.0"
@@ -1750,7 +1740,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -1763,7 +1752,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -1776,7 +1764,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -1789,7 +1776,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -1802,7 +1788,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1815,7 +1800,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1828,7 +1812,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1841,7 +1824,6 @@
       "cpu": [
         "ppc64le"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1854,7 +1836,6 @@
       "cpu": [
         "riscv64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1867,7 +1848,6 @@
       "cpu": [
         "s390x"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1880,7 +1860,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1893,7 +1872,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1906,7 +1884,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -1919,7 +1896,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -1932,7 +1908,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -2052,8 +2027,7 @@
     "node_modules/@types/estree": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
-      "dev": true
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
     },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
@@ -2512,7 +2486,6 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
       "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
       "dependencies": {
         "fill-range": "^7.0.1"
       },
@@ -3198,7 +3171,6 @@
       "version": "0.20.2",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
       "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
-      "dev": true,
       "hasInstallScript": true,
       "bin": {
         "esbuild": "bin/esbuild"
@@ -3642,7 +3614,6 @@
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
       "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
         "@nodelib/fs.walk": "^1.2.3",
@@ -3658,7 +3629,6 @@
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
       "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
       "dependencies": {
         "is-glob": "^4.0.1"
       },
@@ -3690,7 +3660,6 @@
       "version": "1.17.1",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
       "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
-      "dev": true,
       "dependencies": {
         "reusify": "^1.0.4"
       }
@@ -3711,7 +3680,6 @@
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
       "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -3803,7 +3771,6 @@
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
       "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "dev": true,
       "hasInstallScript": true,
       "optional": true,
       "os": [
@@ -3821,6 +3788,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/generouted": {
+      "version": "1.19.3",
+      "resolved": "https://registry.npmjs.org/generouted/-/generouted-1.19.3.tgz",
+      "integrity": "sha512-1KnOmPYBNryAm+9PvvOTOzKiWvuBKRwK+Xr4dP/I9U00y7UAv/VrFGeb0v7mYMiny6jpWBmfBFKaUvq4Q0Z4dQ==",
+      "peerDependencies": {
+        "vite": ">=3"
+      }
+    },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4335,7 +4310,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
       "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -4363,7 +4337,6 @@
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
       "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
       "dependencies": {
         "is-extglob": "^2.1.1"
       },
@@ -4388,7 +4361,6 @@
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
       "engines": {
         "node": ">=0.12.0"
       }
@@ -4636,7 +4608,6 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
       "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
       "engines": {
         "node": ">= 8"
       }
@@ -4645,7 +4616,6 @@
       "version": "4.0.5",
       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
       "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
-      "dev": true,
       "dependencies": {
         "braces": "^3.0.2",
         "picomatch": "^2.3.1"
@@ -4728,7 +4698,6 @@
       "version": "3.3.7",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
       "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5000,7 +4969,6 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
       "engines": {
         "node": ">=8.6"
       },
@@ -5078,7 +5046,6 @@
       "version": "8.4.38",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
       "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
-      "dev": true,
       "funding": [
         {
           "type": "opencollective",
@@ -5164,7 +5131,6 @@
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
       "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5432,7 +5398,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
       "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
       "engines": {
         "iojs": ">=1.0.0",
         "node": ">=0.10.0"
@@ -5462,7 +5427,6 @@
       "version": "4.13.2",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz",
       "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==",
-      "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
       },
@@ -5496,7 +5460,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
       "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5642,7 +5605,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
       "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -5865,7 +5827,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
       "dependencies": {
         "is-number": "^7.0.0"
       },
@@ -6014,13 +5975,12 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
     "node_modules/vite": {
-      "version": "5.2.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
-      "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==",
-      "dev": true,
+      "version": "5.2.10",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
+      "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
       "dependencies": {
         "esbuild": "^0.20.1",
-        "postcss": "^8.4.36",
+        "postcss": "^8.4.38",
         "rollup": "^4.13.0"
       },
       "bin": {
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/package.json b/strudel-cookiecutter/base/{@cookiecutter.name@}/package.json
index 5c6b37be..66b6d5d6 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/package.json
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/package.json
@@ -13,6 +13,7 @@
   "dependencies": {
     "@emotion/react": "^11.11.4",
     "@emotion/styled": "^11.11.0",
+    "@generouted/react-router": "^1.19.3",
     "@mui/icons-material": "^5.15.14",
     "@mui/lab": "^5.0.0-alpha.169",
     "@mui/material": "^5.15.14",
@@ -38,7 +39,7 @@
     "eslint-plugin-react-hooks": "^4.6.0",
     "eslint-plugin-react-refresh": "^0.4.6",
     "typescript": "^5.2.2",
-    "vite": "^5.2.0"
+    "vite": "^5.2.10"
   },
   "engines": {
     "node": "^18.18.0 || >=20.0.0"
diff --git a/strudel-demo-app/src/App.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/App.tsx
similarity index 80%
rename from strudel-demo-app/src/App.tsx
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/src/App.tsx
index 6763697b..31a4a816 100644
--- a/strudel-demo-app/src/App.tsx
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/App.tsx
@@ -3,12 +3,11 @@ import { ThemeProvider } from '@mui/material/styles';
 import { CssBaseline } from '@mui/material';
 import { LocalizationProvider } from '@mui/x-date-pickers';
 import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
-import { Routes } from '@generouted/react-router'
+import { routes } from '@generouted/react-router'
 import { theme } from './theme';
 import { AppProvider } from './context/ContextProvider';
 import { ApiModal } from './components/ApiModal';
-
-export const basename = document.querySelector('base')?.getAttribute('href') ?? '';
+import { RouterProvider, createBrowserRouter } from 'react-router-dom';
 
 const App: React.FC = () => {
   return (
@@ -17,7 +16,7 @@ const App: React.FC = () => {
         <CssBaseline />
         {/* strudel-kit-variable-next-line */}
         <AppProvider appTitle="Demo App">
-          <Routes />
+          <RouterProvider router={createBrowserRouter(routes)} />
           <ApiModal />
         </AppProvider>
       </ThemeProvider>
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.css b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.css
deleted file mode 100644
index 74b5e053..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.App {
-  text-align: center;
-}
-
-.App-logo {
-  height: 40vmin;
-  pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
-  .App-logo {
-    animation: App-logo-spin infinite 20s linear;
-  }
-}
-
-.App-header {
-  background-color: #282c34;
-  min-height: 100vh;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  font-size: calc(10px + 2vmin);
-  color: white;
-}
-
-.App-link {
-  color: #61dafb;
-}
-
-@keyframes App-logo-spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/home/HomePage.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/home/HomePage.tsx
deleted file mode 100644
index 8f6a7b6a..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/home/HomePage.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import { Box, Container, Paper } from '@mui/material';
-import { TopBar } from '../../components/TopBar';
-
-/**
- * TODO:
- * - Add instructions about how to rig up the router for task flows
- * - Add instructions for putting dataset into app
- */
-export const HomePage: React.FC = () => {
-  return (
-    <Box>
-      <TopBar />
-      <Container
-        maxWidth="md"
-        sx={{
-          marginTop: 3,
-          marginBottom: 3,
-        }}
-      >
-        <Paper
-          sx={{
-            padding: 2
-          }}
-        >
-          Welcome to your STRUDEL app!
-        </Paper>
-      </Container>
-    </Box>
-  )
-}
\ No newline at end of file
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/home/TaskFlowsPage.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/home/TaskFlowsPage.tsx
deleted file mode 100644
index 5f7c790e..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/home/TaskFlowsPage.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React, { PropsWithChildren } from 'react';
-import { Link, Container, Paper, List, ListItem, Typography, Box } from '@mui/material';
-import { Link as RouterLink } from 'react-router-dom';
-import { TopBar } from '../../components/TopBar';
-
-export const TaskFlowsPage: React.FC = () => {
-  return (
-    <Box>
-      <TopBar />
-      <Container
-        maxWidth="md"
-        sx={{
-          marginTop: 3,
-          marginBottom: 3,
-        }}
-      >
-        <Paper
-          sx={{
-            padding: 2
-          }}
-        >
-          <Box
-            component="aside"
-            sx={{ 
-              position: 'relative', 
-              width: '250px' 
-            }}
-          >
-            <List>
-              <NavListItem>
-                <Link component={RouterLink} to="compare-data">
-                  Compare Data
-                </Link>
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="contribute-data">
-                  Contribute Data
-                </Link>
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="explore-data">
-                  Explore Data
-                </Link>
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="monitor-activities">
-                  Monitor Activities
-                </Link> 
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="run-computation">
-                  Run Computation
-                </Link> 
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="search-data-repositories">
-                  Search Data Repositories
-                </Link>
-              </NavListItem>
-            </List>
-          </Box>
-        </Paper>
-      </Container>
-    </Box>
-  )
-}
-
-const NavListItem: React.FC<PropsWithChildren> = ({ children }) => {
-  return (
-    <ListItem
-      sx={{
-        padding: 0,
-      }}
-    >
-      <Typography
-        sx={{
-          padding: '0.5rem 1rem',
-          color: 'neutral.main',
-          fontSize: '1.125rem',
-          fontWeight: 'bold',
-        }}
-      >
-        {children}
-      </Typography>
-    </ListItem>
-  )
-}
\ No newline at end of file
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/routes.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/routes.tsx
deleted file mode 100644
index c26c1a35..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/routes.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { createHashRouter, Navigate } from "react-router-dom";
-import { HomePage } from "./home/HomePage";
-
-/**
- * App Router
- * Registers URL paths to page components.
- */
-export const router = createHashRouter([
-  {
-    path: "/",
-    element: <HomePage />,
-  },
-  /**
-   * Add more routes to task flows below.
-   * 
-   * Example:
-   *   
-      {
-        path: "/my-task-flow",
-        element: <MyTaskFlowWrapper />,
-        children: [
-          {
-            index: true,
-            element: <TaskFlowStart />
-          },
-          {
-            path: 'two',
-            element: <TaskFlowPageTwo />
-          },
-          {
-            path: 'three',
-            element: <TaskFlowPageThree />
-          },
-        ]
-      },
-   */
-  {
-    path: '*',
-    element: <Navigate to="/" replace />
-  }
-]);
\ No newline at end of file
diff --git a/strudel-demo-app/src/assets/react.svg b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/assets/react.svg
similarity index 100%
rename from strudel-demo-app/src/assets/react.svg
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/src/assets/react.svg
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/components/TopBar.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/components/TopBar.tsx
index 74ecbd53..8fd470f1 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/components/TopBar.tsx
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/components/TopBar.tsx
@@ -4,12 +4,14 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
 import React from 'react';
 import { useAppState } from '../context/ContextProvider';
 import { AppLink } from './AppLink';
+import { routes } from '@generouted/react-router';
 
 /**
  * Top navigation bar component
  */
 export const TopBar: React.FC = () => {
   const app = useAppState();
+  console.log(routes);
   return (
     <AppBar color="default" position="static">
       <Toolbar>
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/context/ContextProvider.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/context/ContextProvider.tsx
index 4577b46d..e036720b 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/context/ContextProvider.tsx
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/context/ContextProvider.tsx
@@ -65,7 +65,8 @@ export const AppProvider: React.FC<AppProviderProps> = (props) => {
 export const useAppState = () => {
   const context = useContext(AppContext)
   if (context === undefined) {
-    throw new Error('useAppState must be used within an AppProvider')
+    console.log('useAppState must be used within an AppProvider')
+    return { state: { appTitle: 'Test' } };
   }
   return context
 }
\ No newline at end of file
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/index.css b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/index.css
index ec2585e8..3e3b6a19 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/index.css
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/index.css
@@ -10,4 +10,4 @@ body {
 code {
   font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
     monospace;
-}
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/main.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/main.tsx
index be1c6f0c..0a66e3b7 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/main.tsx
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/main.tsx
@@ -1,7 +1,10 @@
 import React from 'react'
 import ReactDOM from 'react-dom/client'
 import './index.css'
-import App from './app/App'
+import App from './App'
+
+// TODO: might need to move this into the useDataFromSource hook
+export const basename = document.querySelector('base')?.getAttribute('href') ?? '';
 
 ReactDOM.createRoot(document.getElementById('root')!).render(
   <React.StrictMode>
diff --git a/strudel-demo-app/src/pages/index.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/pages/index.tsx
similarity index 82%
rename from strudel-demo-app/src/pages/index.tsx
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/src/pages/index.tsx
index 74142192..ac8bda83 100644
--- a/strudel-demo-app/src/pages/index.tsx
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/pages/index.tsx
@@ -10,6 +10,22 @@ import { AppLink } from '../components/AppLink';
  * - Add instructions for putting dataset into app
  */
 const HomePage: React.FC = () => {
+  /**
+   * Sort routes alphabetically by path
+   */
+  routes[0].children?.sort((a, b) => {
+    const pathA = a.path || '';
+    const pathB = b.path || '';
+
+    if (pathA === 'playground' || pathA < pathB) {
+      return -1;
+    } else if (pathA > pathB) {
+      return 1;
+    } else {
+      return 0;
+    }
+  });
+
   return (
     <Box>
       <TopBar />
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/pages/playground/index.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/pages/playground/index.tsx
new file mode 100644
index 00000000..06ebe08c
--- /dev/null
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/pages/playground/index.tsx
@@ -0,0 +1,44 @@
+import { Button, Container, Stack } from '@mui/material';
+import React, { useState } from 'react';
+
+/**
+ * A blank canvas to test out content and components
+ */
+const Playground: React.FC = () => {
+  const [count, setCount] = useState(0);
+
+  const handleIncrement = () => {
+    setCount(count + 1);
+  }
+
+  return (
+    <Container
+      maxWidth="xl"
+      /**
+       * Style STRUDEL and MUI components using the `sx` prop.
+       */
+      sx={{
+        marginBottom: 3,
+        marginTop: 3,
+      }}
+    >
+      {/** 
+       * Add your own components here!
+       * See MUI's component library: https://mui.com/material-ui/all-components/
+       */}
+      <h1>Playground</h1>
+      <Stack 
+        direction="row" 
+        spacing={2}
+        sx={{
+          alignItems: 'center'
+        }}
+      >
+        <Button variant="contained" onClick={handleIncrement}>Increment</Button>
+        <p>{count}</p>
+      </Stack>
+    </Container>
+  );
+}
+
+export default Playground;
\ No newline at end of file
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/react-app-env.d.ts b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/react-app-env.d.ts
deleted file mode 100644
index 6431bc5f..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/react-app-env.d.ts
+++ /dev/null
@@ -1,1 +0,0 @@
-/// <reference types="react-scripts" />
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/router.ts b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/router.ts
new file mode 100644
index 00000000..ea4e05b8
--- /dev/null
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/router.ts
@@ -0,0 +1,17 @@
+// Generouted, changes to this file will be overriden
+/* eslint-disable */
+
+import { components, hooks, utils } from '@generouted/react-router/client'
+
+export type Path =
+  | ``
+
+export type Params = {
+  
+}
+
+export type ModalPath = never
+
+export const { Link, Navigate } = components<Path, Params>()
+export const { useModals, useNavigate, useParams } = hooks<Path, Params, ModalPath>()
+export const { redirect } = utils<Path, Params>()
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/setupTests.ts b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/setupTests.ts
deleted file mode 100644
index 8f2609b7..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/setupTests.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom';
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/theme.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/theme.tsx
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/theme.tsx
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/src/theme.tsx
diff --git a/strudel-demo-app/src/utils/string.utils.ts b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/utils/string.utils.ts
similarity index 100%
rename from strudel-demo-app/src/utils/string.utils.ts
rename to strudel-cookiecutter/base/{@cookiecutter.name@}/src/utils/string.utils.ts
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/vite.config.ts b/strudel-cookiecutter/base/{@cookiecutter.name@}/vite.config.ts
index 5a33944a..2a9bd588 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/vite.config.ts
+++ b/strudel-cookiecutter/base/{@cookiecutter.name@}/vite.config.ts
@@ -1,7 +1,8 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import generouted from '@generouted/react-router/plugin';
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  plugins: [react()],
+  plugins: [react(), generouted()],
 })
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_config/taskflow.config.ts b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_config/taskflow.config.ts
new file mode 100644
index 00000000..cc748d0d
--- /dev/null
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_config/taskflow.config.ts
@@ -0,0 +1,105 @@
+import { CompareDataConfig } from "./taskflow.types";
+
+export const taskflow: CompareDataConfig = {
+  properties: {
+    itemName: "scenario",
+    itemNamePlural: "scenarios",
+  },
+  data: {
+    items: {
+      source: "default/compare-data/scenarios.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Compare Data App",
+      description: "Description of this app section",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Scenario Name",
+          width: 200
+        },
+        {
+          field: "description",
+          headerName: "Description",
+          width: 200
+        },
+        {
+          field: "analysis_type",
+          headerName: "Analysis Type",
+          width: 200
+        },
+        {
+          field: "volumetric_flow_rate",
+          headerName: "Volumetric Flow Rate",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "tss_concentration",
+          headerName: "TSS Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "cod_concentration",
+          headerName: "COD Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "tkn_concentration",
+          headerName: "TKN Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "acetic_acid_concentration",
+          headerName: "Acetic Acid Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric6",
+          headerName: "metric6",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric7",
+          headerName: "metric7",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric8",
+          headerName: "metric8",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric9",
+          headerName: "metric9",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric10",
+          headerName: "metric10",
+          width: 200,
+          isComparisonMetric: true
+        }
+      ]
+    },
+    new: {
+      title: "New Scenario",
+      description: "Description of this app section"
+    },
+    compare: {
+      title: "Compare Scenarios",
+      description: "Description of this app section"
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_config/taskflow.types.ts b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_config/taskflow.types.ts
new file mode 100644
index 00000000..73b8965f
--- /dev/null
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_config/taskflow.types.ts
@@ -0,0 +1,44 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+export type CompareDataConfigColDef = GridColDef & {
+  isComparisonMetric?: boolean;
+}
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface CompareDataConfig {
+  /** Attributes that are used across the Task Flow */
+  properties: {
+    /** Name of entity being compared */
+    itemName: string,
+    /** Pluralized version of itemName */
+    itemNamePlural: string,
+  },
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      tableColumns: CompareDataConfigColDef[]
+    },
+    new: {
+      title: string,
+      description: string
+    },
+    compare: {
+      title: string,
+      description: string
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/context/ContextProvider.tsx b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
similarity index 95%
rename from strudel-cookiecutter/compare-data/{@cookiecutter.name@}/context/ContextProvider.tsx
rename to strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
index abd3b93b..8d43ba51 100644
--- a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/context/ContextProvider.tsx
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
@@ -1,6 +1,6 @@
 import React, { useEffect, useReducer, useContext } from 'react';
 import { CompareDataAction, CompareDataActionType, setComparisonData, setData } from './actions';
-import { CompareDataGridColDef } from '../CompareDataWrapper';
+import { CompareDataConfigColDef } from '../_config/taskflow.types';
 
 interface ComparisonRow {
   [key: string]: number | string | null | undefined;
@@ -9,8 +9,8 @@ interface ComparisonRow {
 export interface CompareDataState {
   data: any[];
   selectedRows: any[];
-  columns: CompareDataGridColDef[];
-  comparisonColumns: CompareDataGridColDef[];
+  columns: CompareDataConfigColDef[];
+  comparisonColumns: CompareDataConfigColDef[];
   dataIdField: string;
   comparing?: boolean;
   comparisonData: ComparisonRow[]
@@ -104,7 +104,7 @@ export const CompareDataProvider: React.FC<CompareDataProviderProps> = (props) =
     if (state.comparing && state.selectedRows.length > 1) {
       const metrics = state.columns.filter((c) => c.isComparisonMetric);
       const scenarios = state.data.filter((d) => state.selectedRows.indexOf(d.id) > -1);
-      const comparisonColumns: CompareDataGridColDef[] = [
+      const comparisonColumns: CompareDataConfigColDef[] = [
         {
           field: 'metric',
           headerName: 'Metric',
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/context/actions.ts b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_context/actions.ts
similarity index 100%
rename from strudel-cookiecutter/compare-data/{@cookiecutter.name@}/context/actions.ts
rename to strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_context/actions.ts
diff --git a/strudel-demo-app/src/pages/compare-data/_layout.tsx b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_layout.tsx
similarity index 76%
rename from strudel-demo-app/src/pages/compare-data/_layout.tsx
rename to strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_layout.tsx
index 7cbd8ece..7d6d1130 100644
--- a/strudel-demo-app/src/pages/compare-data/_layout.tsx
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/_layout.tsx
@@ -4,16 +4,15 @@ import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
 import { CompareDataProvider } from './_context/ContextProvider';
-import definitions from './_config/definitions.json';
 import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the compare-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
 const CompareDataWrapper: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const scenarios = useDataFromSource('default/compare-data/scenarios.json', basename);
+  const scenarios = useDataFromSource(taskflow.data.items.source, basename);
 
   /**
    * Content to render on the page for this component
@@ -24,10 +23,9 @@ const CompareDataWrapper: React.FC = () => {
         <TopBar />
       </Box>
       <Box>
-        {/* strudel-kit-variable-next-line */}
         <CompareDataProvider 
           data={scenarios || []} 
-          columns={definitions.columns.list.table} 
+          columns={taskflow.pages.index.tableColumns} 
           dataIdField='id'
         >
           <Outlet />
diff --git a/strudel-demo-app/src/pages/compare-data/compare.tsx b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/compare.tsx
similarity index 86%
rename from strudel-demo-app/src/pages/compare-data/compare.tsx
rename to strudel-cookiecutter/compare-data/{@cookiecutter.name@}/compare.tsx
index 27e92095..b65bc877 100644
--- a/strudel-demo-app/src/pages/compare-data/compare.tsx
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/compare.tsx
@@ -6,6 +6,7 @@ import { PageHeader } from '../../components/PageHeader';
 import { useCompareData } from './_context/ContextProvider';
 import { setComparing } from './_context/actions';
 import { DataGrid } from '@mui/x-data-grid';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Comparison page for the compare-data Task Flow.
@@ -32,25 +33,21 @@ const ScenarioComparison: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        // strudel-kit-variable-next-line
-        pageTitle="Compare Scenarios"
-        // strudel-kit-variable-next-line
-        description="Comparing multiple saved scenarios for a selected model, calculation, or analysis."
+        pageTitle={taskflow.pages.compare.title}
+        description={taskflow.pages.compare.description}
         actions={
           <Stack direction="row">
             <Box>
               <Link component={RouterLink} to="..">
                 <Button variant="contained" startIcon={<ArrowBackIcon />}>
-                  {/* strudel-kit-variable-next-line */}
-                  Back to Scenarios
+                  Back to {taskflow.properties.itemNamePlural}
                 </Button>
               </Link>
             </Box>
             <Box>
               <Link component={RouterLink} to="../new">
                 <Button variant="contained">
-                  {/* strudel-kit-variable-next-line */}
-                  New Scenario
+                  New {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/definitions.json b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/definitions.json
deleted file mode 100644
index b6e9e0ed..00000000
--- a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/definitions.json
+++ /dev/null
@@ -1,83 +0,0 @@
-{
-  "columns": {
-    "list": {
-      "table": [
-        { 
-          "field": "name", 
-          "headerName": "Scenario Name", 
-          "width": 200 
-        },
-        { 
-          "field": "description", 
-          "headerName": "Description", 
-          "width": 200 
-        },
-        { 
-          "field": "analysis_type", 
-          "headerName": "Analysis Type", 
-          "width": 200
-        },
-        { 
-          "field": "volumetric_flow_rate", 
-          "headerName": "Volumetric Flow Rate", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "tss_concentration", 
-          "headerName": "TSS Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "cod_concentration", 
-          "headerName": "COD Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "tkn_concentration", 
-          "headerName": "TKN Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "acetic_acid_concentration", 
-          "headerName": "Acetic Acid Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric6", 
-          "headerName": "metric6", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric7", 
-          "headerName": "metric7", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric8", 
-          "headerName": "metric8", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric9", 
-          "headerName": "metric9", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric10", 
-          "headerName": "metric10", 
-          "width": 200,
-          "isComparisonMetric": true
-        }
-      ]
-    }
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/compare-data/index.tsx b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/index.tsx
similarity index 84%
rename from strudel-demo-app/src/pages/compare-data/index.tsx
rename to strudel-cookiecutter/compare-data/{@cookiecutter.name@}/index.tsx
index 0a8087c5..11524798 100644
--- a/strudel-demo-app/src/pages/compare-data/index.tsx
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/index.tsx
@@ -6,6 +6,7 @@ import { DataGrid } from '@mui/x-data-grid';
 import { PageHeader } from '../../components/PageHeader';
 import { useCompareData } from './_context/ContextProvider';
 import { setSelectedRows } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
   
 /**
  * List page to show comparable items in the compare-data Task Flow.
@@ -21,10 +22,8 @@ const ScenarioList: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        // strudel-kit-variable-next-line
-        pageTitle="Scenarios"
-        // strudel-kit-variable-next-line
-        description="Collection of various scenarios saved from simulations, optimizations, or calculations."
+        pageTitle={taskflow.pages.index.title}
+        description={taskflow.pages.index.description}
         actions={
           <Stack direction="row">
             <Box>
@@ -33,16 +32,14 @@ const ScenarioList: React.FC = () => {
                   <Button 
                     variant="outlined"
                   >
-                    {/* strudel-kit-variable-next-line */}
-                    Compare Scenarios
+                    Compare {taskflow.properties.itemNamePlural}
                   </Button>
                 )}
                 {state.selectedRows.length > 0 && (
                   <Button 
                     variant={state.selectedRows.length > 1 ? 'contained' : 'outlined' }
                   >
-                    {/* strudel-kit-variable-next-line */}
-                    Compare Scenarios ({state.selectedRows.length})
+                    Compare {taskflow.properties.itemNamePlural} ({state.selectedRows.length})
                   </Button>
                 )}
               </Link>
@@ -50,8 +47,7 @@ const ScenarioList: React.FC = () => {
             <Box>
               <Link component={RouterLink} to="new">
                 <Button variant="contained">
-                  {/* strudel-kit-variable-next-line */}
-                  New Scenario
+                  New {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
diff --git a/strudel-demo-app/src/pages/compare-data/new.tsx b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/new.tsx
similarity index 88%
rename from strudel-demo-app/src/pages/compare-data/new.tsx
rename to strudel-cookiecutter/compare-data/{@cookiecutter.name@}/new.tsx
index 1180b0f1..7b7c9574 100644
--- a/strudel-demo-app/src/pages/compare-data/new.tsx
+++ b/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/new.tsx
@@ -4,6 +4,7 @@ import { Link as RouterLink } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
 import { useCompareData } from './_context/ContextProvider';
 import { setComparing } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page for filling out a form for adding a new item to 
@@ -29,10 +30,8 @@ export const NewScenario: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        // strudel-kit-variable-next-line
-        pageTitle="New Scenario"
-        // strudel-kit-variable-next-line
-        description="Add a new scenario to compare to the others in your list."
+        pageTitle={taskflow.pages.new.title}
+        description={taskflow.pages.new.description}
         actions={
           <Stack direction="row">
             <Box>
@@ -45,7 +44,7 @@ export const NewScenario: React.FC = () => {
             <Box>
               <Link component={RouterLink} to="..">
                 <Button variant="contained">
-                  Save Scenario
+                  Save {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/DataFilesPanel.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/DataFilesPanel.tsx
similarity index 100%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/DataFilesPanel.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/DataFilesPanel.tsx
diff --git a/strudel-demo-app/src/pages/contribute-data/_components/DatasetView.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/DatasetView.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_components/DatasetView.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/DatasetView.tsx
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/MetadataPanel.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/MetadataPanel.tsx
similarity index 100%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/MetadataPanel.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/MetadataPanel.tsx
diff --git a/strudel-demo-app/src/pages/contribute-data/_components/ValidationChecks.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/ValidationChecks.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_components/ValidationChecks.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_components/ValidationChecks.tsx
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_config/taskflow.config.ts b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_config/taskflow.config.ts
new file mode 100644
index 00000000..fc25e122
--- /dev/null
+++ b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_config/taskflow.config.ts
@@ -0,0 +1,57 @@
+import { ContributeDataConfig } from "./taskflow.types";
+
+export const taskflow: ContributeDataConfig = {
+  data: {
+    datasets: {
+      source: "default/contribute-data/contributor_datasets.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Register as a data contributor"
+    },
+    portal: {
+      title: "Your Dataset Uploads",
+      tableColumns: [
+        {
+          field: "title",
+          headerName: "Dataset Title",
+          width: 200
+        },
+        {
+          field: "category",
+          headerName: "Category",
+          width: 200
+        },
+        {
+          field: "summary",
+          headerName: "Summary",
+          width: 200
+        },
+        {
+          field: "doi",
+          headerName: "DOI",
+          width: 200
+        },
+        {
+          field: "publication_date",
+          headerName: "Created Date",
+          width: 200
+        },
+        {
+          field: "status",
+          headerName: "Status",
+          width: 200
+        }
+      ]
+    },
+    new: {
+      title: "Upload a new dataset",
+      description: "Description of this app section"
+    },
+    review: {
+      title: "Review your new dataset"
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_config/taskflow.types.ts b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_config/taskflow.types.ts
new file mode 100644
index 00000000..948df04e
--- /dev/null
+++ b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_config/taskflow.types.ts
@@ -0,0 +1,36 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface ContributeDataConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: Record<string, any>,
+  data: {
+    datasets: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+    },
+    portal: {
+      title: string,
+      tableColumns: GridColDef[]
+    },
+    new: {
+      title: string,
+      description: string
+    },
+    review: {
+      title: string,
+    },
+  }
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/contribute-data/_context/ContextProvider.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_context/ContextProvider.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/pages/contribute-data/_context/actions.ts b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_context/actions.ts
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_context/actions.ts
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/utils.ts b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_context/utils.ts
similarity index 100%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/utils.ts
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_context/utils.ts
diff --git a/strudel-demo-app/src/pages/contribute-data/_layout.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_layout.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_layout.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/_layout.tsx
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/definitions.json b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/definitions.json
deleted file mode 100644
index 85886ed4..00000000
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/definitions.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "columns": {
-    "portal": {
-      "table": [
-        { 
-          "field": "title", 
-          "headerName": "Dataset Title", 
-          "width": 200 
-        },
-        { 
-          "field": "category", 
-          "headerName": "Category", 
-          "width": 200 
-        },
-        { 
-          "field": "summary", 
-          "headerName": "Summary", 
-          "width": 200 
-        },
-        { 
-          "field": "doi", 
-          "headerName": "DOI", 
-          "width": 200 
-        },
-        { 
-          "field": "publication_date", 
-          "headerName": "Created Date", 
-          "width": 200 
-        },
-        { 
-          "field": "status", 
-          "headerName": "Status", 
-          "width": 200 
-        }
-      ]
-    }
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/contribute-data/index.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/index.tsx
similarity index 97%
rename from strudel-demo-app/src/pages/contribute-data/index.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/index.tsx
index d40dd382..cd166231 100644
--- a/strudel-demo-app/src/pages/contribute-data/index.tsx
+++ b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/index.tsx
@@ -2,6 +2,7 @@ import { Box, Button, Container, Link, Paper, Stack, TextField, Typography } fro
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Initial page for registering as a contributor in the contribute-data Task Flow.
@@ -25,8 +26,7 @@ const Register: React.FC = () => {
               variant="h6" 
               component="h1" 
             >
-              {/* strudel-kit-variable-next-line */}
-              Register as a data contributor
+              {taskflow.pages.index.title}
             </Typography>
             <Box>
               <Typography fontWeight="medium" mb={1}>
diff --git a/strudel-demo-app/src/pages/contribute-data/new.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/new.tsx
similarity index 83%
rename from strudel-demo-app/src/pages/contribute-data/new.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/new.tsx
index eaf15a1b..a14a5c1d 100644
--- a/strudel-demo-app/src/pages/contribute-data/new.tsx
+++ b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/new.tsx
@@ -3,6 +3,7 @@ import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataFilesPanel } from './_components/DataFilesPanel';
 import { MetadataPanel } from './_components/MetadataPanel';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page for entering information about a new dataset in the contribute-data Task Flow.
@@ -21,12 +22,10 @@ const NewDataset: React.FC = () => {
         <Stack direction="row" justifyContent="space-between">
           <Stack>
             <Typography variant="h6" component="h1">
-              {/* strudel-kit-variable-next-line */}
-              Upload a new dataset
+              {taskflow.pages.new.title}
             </Typography>
             <Typography>
-              {/* strudel-kit-variable-next-line */}
-              Mention the data contribution steps in brief, and also major requirements if any. Also give links to detailed documentation of steps, requirements and guidelines. Link to documentation.
+              {taskflow.pages.new.description}
             </Typography>
           </Stack>
           <Stack direction="row">
diff --git a/strudel-demo-app/src/pages/contribute-data/portal.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/portal.tsx
similarity index 82%
rename from strudel-demo-app/src/pages/contribute-data/portal.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/portal.tsx
index 06249895..c9e76398 100644
--- a/strudel-demo-app/src/pages/contribute-data/portal.tsx
+++ b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/portal.tsx
@@ -6,16 +6,15 @@ import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import definitions from './_config/definitions.json';
 import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page to show a contributor's uploads in the contribute-data Task Flow.
  * Also allows users to start a new dataset which sends them to the `<NewDataset>` component.
  */
 const ContributorPortal: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const datasets = useDataFromSource('default/contribute-data/contributor_datasets.json', basename);
+  const datasets = useDataFromSource(taskflow.data.datasets.source, basename);
   
   /**
    * Content to render on the page for this component
@@ -30,8 +29,7 @@ const ContributorPortal: React.FC = () => {
       <Stack>
         <Stack direction="row" justifyContent="space-between" alignItems="center">
           <Typography variant="h6" component="h1">
-            {/* strudel-kit-variable-next-line */}
-            Your Dataset Uploads
+            {taskflow.pages.portal.title}
           </Typography>
           <Link component={RouterLink} to="../new">
             <Button variant="contained">
@@ -42,8 +40,7 @@ const ContributorPortal: React.FC = () => {
         <Paper>
           <DataGrid
             rows={datasets || []}
-            // strudel-kit-variable-next-line
-            getRowId={(row) => row['id']}
+            getRowId={(row) => row[taskflow.data.datasets.idField]}
             columns={columns}
             disableColumnSelector
             disableRowSelectionOnClick
@@ -58,7 +55,7 @@ const ContributorPortal: React.FC = () => {
  * Define column definitions in-file for prototyping
  */
 const columns: GridColDef[] = [
-  ...definitions.columns.portal.table,
+  ...taskflow.pages.portal.tableColumns,
   { 
     field: 'actions', 
     headerName: 'Actions', 
diff --git a/strudel-demo-app/src/pages/contribute-data/review.tsx b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/review.tsx
similarity index 97%
rename from strudel-demo-app/src/pages/contribute-data/review.tsx
rename to strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/review.tsx
index 764d4a86..23774966 100644
--- a/strudel-demo-app/src/pages/contribute-data/review.tsx
+++ b/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/review.tsx
@@ -6,6 +6,7 @@ import { DatasetView } from './_components/DatasetView';
 import { ValidationChecks } from './_components/ValidationChecks';
 import { useContributeData } from './_context/ContextProvider';
 import { finishChecks, runChecks } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page to review a new dataset before submitting it in the contribute-data Task Flow.
@@ -65,8 +66,7 @@ const ReviewDataset: React.FC = () => {
           <Stack direction="row" justifyContent="space-between">
             <Stack>
               <Typography variant="h6" component="h1">
-                {/* strudel-kit-variable-next-line */}
-                Review your new dataset
+                {taskflow.pages.review.title}
               </Typography>
             </Stack>
             <Stack direction="row">
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataExplorer.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataExplorer.tsx
deleted file mode 100644
index 36239158..00000000
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataExplorer.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Box, Grid } from '@mui/material';
-import React, { useState } from 'react';
-import { FiltersPanel } from './FiltersPanel';
-import { PreviewPanel } from './PreviewPanel';
-import { DataTablePanel } from './DataTablePanel';
-import { PageHeader } from '../../components/PageHeader';
-import { useExploreData } from './context/ContextProvider';
-import { setPreviewItem } from './context/actions';
-
-/**
- * Main explorer page in the explore-data Task Flow.
- * This page includes the page header, filters panel, 
- * main table, and the table row preview panel.
- */
-export const DataExplorer: React.FC = () => {
-  const {state, dispatch} = useExploreData();
-  const [showFiltersPanel, setShowFiltersPanel] = useState(true);
-
-  const handleCloseFilters = () => {
-    setShowFiltersPanel(false);
-  };
-  
-  const handleToggleFilters = () => {
-    setShowFiltersPanel(!showFiltersPanel);
-  }
-
-  const handleClosePreview = () => {
-    dispatch(setPreviewItem(null));
-  }
-
-  /**
-   * Content to render on the page for this component
-   */
-  return (
-    <Box>
-      <PageHeader
-        pageTitle="{@ cookiecutter.pages.main.pageTitle @}"
-        description="{@ cookiecutter.pages.main.pageDescription @}"
-        sx={{
-          marginBottom: 1,
-          padding: 2,
-        }}
-      />
-      <Grid container spacing={1}>
-        {showFiltersPanel && (
-          <Grid item xs={2}>
-            <FiltersPanel onClose={handleCloseFilters} />
-          </Grid>
-        )}
-        <Grid item xs={getMainColumnSize(showFiltersPanel, !!state.previewItem)}>
-          <DataTablePanel onToggleFiltersPanel={handleToggleFilters} />
-        </Grid>
-        {state.previewItem && (
-          <Grid item xs={4}>
-            <PreviewPanel onClose={handleClosePreview} />
-          </Grid>
-        )}
-      </Grid>
-    </Box>
-  )
-}
-
-const getMainColumnSize = (showFiltersPanel: boolean, showPreviewPanel: boolean) => {
-  if (!showFiltersPanel && !showPreviewPanel) {
-    return 12;
-  } else if (showFiltersPanel && !showPreviewPanel) {
-    return 10;
-  } else if (!showFiltersPanel && showPreviewPanel) {
-    return 8;
-  } else if (showFiltersPanel && showPreviewPanel) {
-    return 6;
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/explore-data/[id].tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/[id].tsx
similarity index 100%
rename from strudel-demo-app/src/pages/explore-data/[id].tsx
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/[id].tsx
diff --git a/strudel-demo-app/src/pages/explore-data/_components/DataTablePanel.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_components/DataTablePanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/explore-data/_components/DataTablePanel.tsx
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_components/DataTablePanel.tsx
diff --git a/strudel-demo-app/src/pages/explore-data/_components/FiltersPanel.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_components/FiltersPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/explore-data/_components/FiltersPanel.tsx
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_components/FiltersPanel.tsx
diff --git a/strudel-demo-app/src/pages/explore-data/_components/PreviewPanel.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_components/PreviewPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/explore-data/_components/PreviewPanel.tsx
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_components/PreviewPanel.tsx
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_config/taskflow.config.ts b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_config/taskflow.config.ts
new file mode 100644
index 00000000..ed00a6b8
--- /dev/null
+++ b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_config/taskflow.config.ts
@@ -0,0 +1,108 @@
+import { ExploreDataConfig } from "./taskflow.types";
+
+export const taskflow: ExploreDataConfig = {
+  data: {
+    items: {
+      source: "default/explore-data/genomes.tsv",
+      idField: "Proteome_ID"
+    }
+  },
+  pages: {
+    index: {
+      title: "Explore Data App Test",
+      description: "Description of this app section",
+      tableColumns: [
+        {
+          field: "Organism",
+          headerName: "Organism",
+          width: 200
+        },
+        {
+          field: "Common Name",
+          headerName: "Common Name",
+          width: 200
+        },
+        {
+          field: "Assembly",
+          headerName: "Assembly",
+          width: 150
+        },
+        {
+          field: "Data Usage Policy",
+          headerName: "Data Usage Policy",
+          width: 220
+        },
+        {
+          field: "Euk. BUSCO %",
+          headerName: "Euk. BUSCO %",
+          type: "number",
+          width: 200
+        },
+        {
+          field: "Emb. BUSCO %",
+          headerName: "Emb. BUSCO %",
+          type: "number",
+          width: 200
+        }
+      ],
+      tableFilters: [
+        {
+          field: "Assembly",
+          displayName: "Assembly",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "JGI",
+                value: "JGI"
+              },
+              {
+                label: "BYU",
+                value: "BYU"
+              },
+              {
+                label: "AGP",
+                value: "AGP"
+              }
+            ]
+          }
+        },
+        {
+          field: "Data Usage Policy",
+          displayName: "Data Usage Policy",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "restricted",
+                value: "restricted"
+              },
+              {
+                label: "unrestricted",
+                value: "unrestricted"
+              }
+            ]
+          }
+        },
+        {
+          field: "Euk. BUSCO %",
+          displayName: "Euk. BUSCO %",
+          filterType: "Slider",
+          props: {
+            min: 0,
+            max: 100
+          }
+        },
+        {
+          field: "Emb. BUSCO %",
+          displayName: "Emb. BUSCO %",
+          filterType: "Slider",
+          props: {
+            min: 0,
+            max: 100
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_config/taskflow.types.ts b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_config/taskflow.types.ts
new file mode 100644
index 00000000..a8a22e23
--- /dev/null
+++ b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_config/taskflow.types.ts
@@ -0,0 +1,33 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface ExploreDataConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: Record<string, any>,
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      tableColumns: GridColDef[],
+      tableFilters: {
+        field: string,
+        displayName: string,
+        filterType: 'CheckboxList' | 'Slider' | 'date range',
+        props?: object
+      }[]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/context/ContextProvider.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/context/ContextProvider.tsx
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_context/ContextProvider.tsx
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/context/actions.ts b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_context/actions.ts
similarity index 100%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/context/actions.ts
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_context/actions.ts
diff --git a/strudel-demo-app/src/pages/explore-data/_layout.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_layout.tsx
similarity index 70%
rename from strudel-demo-app/src/pages/explore-data/_layout.tsx
rename to strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_layout.tsx
index ca8117e1..aa60b508 100644
--- a/strudel-demo-app/src/pages/explore-data/_layout.tsx
+++ b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/_layout.tsx
@@ -2,18 +2,17 @@ import React from 'react';
 import { Box } from '@mui/material';
 import { Outlet } from 'react-router';
 import { ExploreDataProvider } from './_context/ContextProvider';
-import definitions from './_config/definitions.json';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
 import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the explore-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
 const ExploreDataLayout: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const entities = useDataFromSource('default/explore-data/genomes.tsv', basename);
+  const entities = useDataFromSource(taskflow.data.items.source, basename);
 
   /**
    * Content to render on the page for this component
@@ -26,10 +25,9 @@ const ExploreDataLayout: React.FC = () => {
       <Box>
         <ExploreDataProvider 
           data={entities} 
-          columns={definitions.columns.main.table} 
-          filters={definitions.filters.main} 
-          // strudel-kit-variable-next-line
-          dataIdField='Proteome_ID'
+          columns={taskflow.pages.index.tableColumns} 
+          filters={taskflow.pages.index.tableFilters} 
+          dataIdField={taskflow.data.items.idField}
         >
           <Outlet />
         </ExploreDataProvider>
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/definitions.json b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/definitions.json
deleted file mode 100644
index 84b140d2..00000000
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/definitions.json
+++ /dev/null
@@ -1,100 +0,0 @@
-{
-  "columns": {
-    "main": {
-      "table": [
-        {
-          "field":"Organism",
-          "headerName":"Organism",
-          "width":200
-        },
-        {
-          "field":"Common Name",
-          "headerName":"Common Name",
-          "width":200
-        },
-        {
-          "field":"Assembly",
-          "headerName":"Assembly",
-          "width":150
-        },
-        {
-          "field":"Data Usage Policy",
-          "headerName":"Data Usage Policy",
-          "width":220
-        },
-        {
-          "field":"Euk. BUSCO %",
-          "headerName":"Euk. BUSCO %",
-          "type":"number",
-          "width":200
-        },
-        {
-          "field":"Emb. BUSCO %",
-          "headerName":"Emb. BUSCO %",
-          "type":"number",
-          "width":200
-        }
-      ]
-    }
-  },
-  "filters": {
-    "main": [
-      {
-        "field": "Assembly",
-        "displayName":"Assembly",
-        "filterType":"CheckboxList",
-        "props":{
-          "options":[
-            {
-              "label":"JGI",
-              "value":"JGI"
-            },
-            {
-              "label":"BYU",
-              "value":"BYU"
-            },
-            {
-              "label":"AGP",
-              "value":"AGP"
-            }
-          ]
-        }
-      },
-      {
-        "field":"Data Usage Policy",
-        "displayName":"Data Usage Policy",
-        "filterType":"CheckboxList",
-        "props":{
-          "options":[
-            {
-              "label":"restricted",
-              "value":"restricted"
-            },
-            {
-              "label":"unrestricted",
-              "value":"unrestricted"
-            }
-          ]
-        }
-      },
-      {
-        "field":"Euk. BUSCO %",
-        "displayName":"Euk. BUSCO %",
-        "filterType":"Slider",
-        "props":{
-          "min":0,
-          "max":100
-        }
-      },
-      {
-        "field":"Emb. BUSCO %",
-        "displayName":"Emb. BUSCO %",
-        "filterType":"Slider",
-        "props":{
-          "min":0,
-          "max":100
-        }
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/index.tsx b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/index.tsx
index d4ea4b29..d61c8c77 100644
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/index.tsx
+++ b/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/index.tsx
@@ -1,1 +1,76 @@
-export { ExploreDataWrapper } from './ExploreDataWrapper';
\ No newline at end of file
+import { Box, Grid } from '@mui/material';
+import React, { useState } from 'react';
+import { FiltersPanel } from './_components/FiltersPanel';
+import { PreviewPanel } from './_components/PreviewPanel';
+import { DataTablePanel } from './_components/DataTablePanel';
+import { PageHeader } from '../../components/PageHeader';
+import { useExploreData } from './_context/ContextProvider';
+import { setPreviewItem } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
+
+/**
+ * Main explorer page in the explore-data Task Flow.
+ * This page includes the page header, filters panel, 
+ * main table, and the table row preview panel.
+ */
+const DataExplorer: React.FC = () => {
+  const {state, dispatch} = useExploreData();
+  const [showFiltersPanel, setShowFiltersPanel] = useState(true);
+
+  const handleCloseFilters = () => {
+    setShowFiltersPanel(false);
+  };
+  
+  const handleToggleFilters = () => {
+    setShowFiltersPanel(!showFiltersPanel);
+  }
+
+  const handleClosePreview = () => {
+    dispatch(setPreviewItem(null));
+  }
+
+  /**
+   * Content to render on the page for this component
+   */
+  return (
+    <Box>
+      <PageHeader
+        pageTitle={taskflow.pages.index.title}
+        description={taskflow.pages.index.description}
+        sx={{
+          marginBottom: 1,
+          padding: 2,
+        }}
+      />
+      <Grid container spacing={1}>
+        {showFiltersPanel && (
+          <Grid item xs={2}>
+            <FiltersPanel onClose={handleCloseFilters} />
+          </Grid>
+        )}
+        <Grid item xs={getMainColumnSize(showFiltersPanel, !!state.previewItem)}>
+          <DataTablePanel onToggleFiltersPanel={handleToggleFilters} />
+        </Grid>
+        {state.previewItem && (
+          <Grid item xs={4}>
+            <PreviewPanel onClose={handleClosePreview} />
+          </Grid>
+        )}
+      </Grid>
+    </Box>
+  )
+}
+
+const getMainColumnSize = (showFiltersPanel: boolean, showPreviewPanel: boolean) => {
+  if (!showFiltersPanel && !showPreviewPanel) {
+    return 12;
+  } else if (showFiltersPanel && !showPreviewPanel) {
+    return 10;
+  } else if (!showFiltersPanel && showPreviewPanel) {
+    return 8;
+  } else if (showFiltersPanel && showPreviewPanel) {
+    return 6;
+  }
+}
+
+export default DataExplorer;
\ No newline at end of file
diff --git a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_config/taskflow.config.ts b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_config/taskflow.config.ts
new file mode 100644
index 00000000..ef9e387d
--- /dev/null
+++ b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_config/taskflow.config.ts
@@ -0,0 +1,14 @@
+import { MonitorActivitiesConfig } from "./taskflow.types";
+
+export const taskflow: MonitorActivitiesConfig = {
+  data: {
+    items: {
+      source: "default/monitor-activities/experiments.json",
+      idField: "id"
+    },
+    detail: {
+      source: "default/monitor-activities/experiment_detail.json",
+      idField: "id"
+    }
+  },
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_config/taskflow.types.ts b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_config/taskflow.types.ts
new file mode 100644
index 00000000..cb294593
--- /dev/null
+++ b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_config/taskflow.types.ts
@@ -0,0 +1,18 @@
+/**
+ * Type definitions for the Monitor Activities Task Flow config object
+ */
+export interface MonitorActivitiesConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: any
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  pages?: any
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/monitor-activities/_layout.tsx b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_layout.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/monitor-activities/_layout.tsx
rename to strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/_layout.tsx
diff --git a/strudel-demo-app/src/pages/monitor-activities/calendar.tsx b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/calendar.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/monitor-activities/calendar.tsx
rename to strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/calendar.tsx
diff --git a/strudel-demo-app/src/pages/monitor-activities/detail.tsx b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/detail.tsx
similarity index 97%
rename from strudel-demo-app/src/pages/monitor-activities/detail.tsx
rename to strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/detail.tsx
index e2098760..62fff53b 100644
--- a/strudel-demo-app/src/pages/monitor-activities/detail.tsx
+++ b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/detail.tsx
@@ -9,14 +9,14 @@ import { Link as RouterLink } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
 import { useDataFromSource } from '../../utils/useDataFromSource';
 import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Detail view of the selected activity from `<ActivityList>` in monitor-activities Task Flow.
  * The two components are not currently hooked together.
  */
 const ActivityDetail: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const experiment = useDataFromSource('default/monitor-activities/experiment_detail.json', basename);
+  const experiment = useDataFromSource(taskflow.data.detail.source, basename);
 
   const getNoteRows = (notes: any[]) => {
     return notes.map((note) => {
diff --git a/strudel-demo-app/src/pages/monitor-activities/index.tsx b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/index.tsx
similarity index 93%
rename from strudel-demo-app/src/pages/monitor-activities/index.tsx
rename to strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/index.tsx
index 6ed6ed29..9624a4a1 100644
--- a/strudel-demo-app/src/pages/monitor-activities/index.tsx
+++ b/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/index.tsx
@@ -5,13 +5,13 @@ import React from 'react';
 import { useNavigate } from 'react-router-dom';
 import { useDataFromSource } from '../../utils/useDataFromSource';
 import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * List view of all activities in the monitor-activites Task Flow.
  */
 const ActivityList: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const experiments = useDataFromSource('default/monitor-activities/experiments.json', basename);
+  const experiments = useDataFromSource(taskflow.data.items.source, basename);
   const navigate = useNavigate();
   
   /**
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/ComputationsList.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/ComputationsList.tsx
deleted file mode 100644
index d37ed18d..00000000
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/ComputationsList.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Box, Button, Container, Paper, Stack, Typography } from '@mui/material';
-import React, { useState } from 'react';
-import { DataGrid } from '@mui/x-data-grid';
-import { NewScenarioModal } from './NewScenarioModal';
-import { useRunComputation } from './context/ContextProvider';
-
-/**
- * List page for all compuation runs in the run-computation Task Flow.
- * Can open the `<NewScenarioModal>` from here and then proceed to the 
- * `<DataInputs>` component for the next step.
- */
-export const ComputationsList: React.FC = () => {
-  const { state, dispatch } = useRunComputation();
-  const [modalOpen, setModalOpen] = useState(false);
-
-  const handleNewScenario = () => {
-    setModalOpen(true);
-  };
-
-  /**
-   * Content to render on the page for this component
-   */
-  return (
-    <Box>
-      <Container 
-        maxWidth="xl"
-        sx={{
-          mt: 4
-        }}
-      >
-        <Paper>
-          <Stack
-            direction="row"
-            spacing={2}
-            alignItems="center"
-            sx={{
-              padding: 2
-            }}
-          >
-            <Box flex={1}>
-              <Typography 
-                variant="h6" 
-                component="h1" 
-              >
-                {@ cookiecutter.pages.list.pageTitle @}
-              </Typography>
-              <Typography 
-                variant="subtitle1" 
-                component="p" 
-              >
-                {@ cookiecutter.pages.list.pageDescription @}
-              </Typography>
-            </Box>
-            <Button
-              variant="contained"
-              onClick={handleNewScenario}
-            >
-              New {@ cookiecutter.computeItem @}
-            </Button>
-            <NewScenarioModal modalOpen={modalOpen} setModalOpen={setModalOpen} />
-          </Stack>
-          <DataGrid
-            rows={state.list.table.data}
-            getRowId={(row) => row[state.list.table.dataIdField]}
-            columns={state.list.table.columns}
-            disableColumnSelector
-            disableRowSelectionOnClick
-          />
-        </Paper>  
-      </Container>
-    </Box>
-  )
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/run-computation/[id]/_layout.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/_layout.tsx
similarity index 92%
rename from strudel-demo-app/src/pages/run-computation/[id]/_layout.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/_layout.tsx
index 4bfd2be5..f943084c 100644
--- a/strudel-demo-app/src/pages/run-computation/[id]/_layout.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/_layout.tsx
@@ -2,6 +2,7 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
 import { AppBar, Box, Breadcrumbs, Link, Stack, Toolbar, Typography } from '@mui/material';
 import React from 'react';
 import { Outlet, Link as RouterLink } from 'react-router-dom';
+import { taskflow } from '../_config/taskflow.config';
   
 /**
  * Wrapper component for the steps involved in a computational run.
@@ -25,8 +26,7 @@ const ComputationLayout: React.FC = () => {
             <Stack direction="row" alignItems="center" sx={{ flexGrow: 1 }}>
               <Breadcrumbs aria-label="breadcrumb" separator={<ChevronRightIcon fontSize="small" />}>
                 <Link underline="hover" color="inherit" to=".." component={RouterLink}>
-                  {/* strudel-kit-variable-next-line */}
-                  Scenario List
+                  {taskflow.properties.itemName} List
                 </Link>
                 <Typography color="text.primary">Optimization Scenario 1</Typography>
               </Breadcrumbs>
diff --git a/strudel-demo-app/src/pages/run-computation/[id]/data-inputs.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/data-inputs.tsx
similarity index 89%
rename from strudel-demo-app/src/pages/run-computation/[id]/data-inputs.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/data-inputs.tsx
index 6609d9da..ea98ea55 100644
--- a/strudel-demo-app/src/pages/run-computation/[id]/data-inputs.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/data-inputs.tsx
@@ -6,6 +6,7 @@ import { useDataFromSource } from '../../../utils/useDataFromSource';
 import { basename } from '../../../main';
 import { useRunComputation } from '../_context/ContextProvider';
 import { setInputsTableData } from '../_context/actions';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Page to display input data after creating or selecting an item from 
@@ -14,8 +15,7 @@ import { setInputsTableData } from '../_context/actions';
  */
 const DataInputsPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
-  // strudel-kit-variable-next-line
-  const inputsData = useDataFromSource('default/run-computation/inputs.json', basename);
+  const inputsData = useDataFromSource(taskflow.data.inputs.source, basename);
   
   /**
    * Set data for the inputs table when the data loads
@@ -43,24 +43,21 @@ const DataInputsPage: React.FC = () => {
           <Step key="Data Inputs">
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Data Inputs
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Optimization Settings">
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Optimization Settings
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Results">
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Results
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -154,8 +151,7 @@ const DataInputsPage: React.FC = () => {
         }}
       >
         <Link component={RouterLink} to="../settings">
-          {/* strudel-kit-variable-next-line */}
-          <Button variant="contained">Continue to optimization settings</Button>
+          <Button variant="contained">Continue to {taskflow.pages.settings.title}</Button>
         </Link>
       </Box>
     </Stack>
diff --git a/strudel-demo-app/src/pages/run-computation/[id]/results.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/results.tsx
similarity index 87%
rename from strudel-demo-app/src/pages/run-computation/[id]/results.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/results.tsx
index 2d6f2c19..81f89c31 100644
--- a/strudel-demo-app/src/pages/run-computation/[id]/results.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/results.tsx
@@ -7,6 +7,7 @@ import { useDataFromSource } from '../../../utils/useDataFromSource';
 import { basename } from '../../../main';
 import { useRunComputation } from '../_context/ContextProvider';
 import { setResultsBarChartData, setResultsLineChartData, setResultsTableData } from '../_context/actions';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Results page to display after a computation completes in the run-computation Task Flow.
@@ -14,12 +15,9 @@ import { setResultsBarChartData, setResultsLineChartData, setResultsTableData }
  */
 const ResultsPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
-  // strudel-kit-variable-next-line
-  const tableData = useDataFromSource('default/run-computation/results_table.json', basename);
-  // strudel-kit-variable-next-line
-  const lineData = useDataFromSource('default/run-computation/results_line_chart.json', basename);
-  // strudel-kit-variable-next-line
-  const barData = useDataFromSource('default/run-computation/results_bar_chart.json', basename);
+  const tableData = useDataFromSource(taskflow.data.results.source, basename);
+  const lineData = useDataFromSource(taskflow.data.lineChart.source, basename);
+  const barData = useDataFromSource(taskflow.data.barChart.source, basename);
 
   /**
    * Set data for the results table when the data loads
@@ -65,24 +63,21 @@ const ResultsPage: React.FC = () => {
           <Step key="Data Inputs">
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line   */}
-                Data Inputs
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Optimization Settings">
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Optimization Settings
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Results">
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Results
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -195,8 +190,7 @@ const ResultsPage: React.FC = () => {
         }}
       >
         <Link component={RouterLink} to="../settings">
-          {/* strudel-kit-variable-next-line */}
-          <Button variant="contained">Back to optimization settings</Button>
+          <Button variant="contained">Back to {taskflow.pages.settings.title}</Button>
         </Link>
       </Box>
     </Stack>
diff --git a/strudel-demo-app/src/pages/run-computation/[id]/running.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/running.tsx
similarity index 89%
rename from strudel-demo-app/src/pages/run-computation/[id]/running.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/running.tsx
index 2a6a4be2..e2c33089 100644
--- a/strudel-demo-app/src/pages/run-computation/[id]/running.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/running.tsx
@@ -2,6 +2,7 @@ import { Box, Button, Container, LinearProgress, Link, Paper, Stack, Step, StepL
 import React, { useEffect, useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { useInterval } from '../../../utils/useInterval';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Page to show while a computation is running and after it completes.
@@ -33,27 +34,24 @@ const RunningComputationPage: React.FC = () => {
         }}
       >
         <Stepper activeStep={1} sx={{ maxWidth: 850 }}>
-          <Step key="Data Inputs">
+          <Step key={taskflow.pages.dataInputs.title}>
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Data Inputs
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Optimization Settings">
+          <Step key={taskflow.pages.settings.title}>
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Optimization Settings
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Results">
+          <Step key={taskflow.pages.results.title}>
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Results
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
diff --git a/strudel-demo-app/src/pages/run-computation/[id]/settings.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/settings.tsx
similarity index 90%
rename from strudel-demo-app/src/pages/run-computation/[id]/settings.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/settings.tsx
index c7328533..426e5acc 100644
--- a/strudel-demo-app/src/pages/run-computation/[id]/settings.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/[id]/settings.tsx
@@ -1,6 +1,7 @@
 import { Box, Button, Container, FormControl, Grid, Link, MenuItem, Paper, Select, Stack, Step, StepLabel, Stepper, TextField, Typography } from '@mui/material';
 import React, { useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Page to configure settings for a computational run.
@@ -28,27 +29,24 @@ const SettingsPage: React.FC = () => {
         }}
       >
         <Stepper activeStep={1} sx={{ maxWidth: 850 }}>
-          <Step key="Data Inputs">
+          <Step key={taskflow.pages.dataInputs.title}>
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Data Inputs
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Optimization Settings">
+          <Step key={taskflow.pages.settings.title}>
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Optimization Settings
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Results">
+          <Step key={taskflow.pages.results.title}>
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {/* strudel-kit-variable-next-line */}
-                Results
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -63,8 +61,7 @@ const SettingsPage: React.FC = () => {
         <Paper sx={{ padding: 3 }}>
           <Stack>
             <Typography variant="h6" component="h2">
-              {/* strudel-kit-variable-next-line */}
-              Optimization Settings
+              {taskflow.pages.settings.title}
             </Typography>
             <Grid container rowSpacing={2} alignItems="center">
               <Grid item md={3}>
@@ -132,7 +129,7 @@ const SettingsPage: React.FC = () => {
             <Box textAlign="right">
               <Link component={RouterLink} to="../running">
                 <Button variant="contained" sx={{ marginTop: 4 }}>
-                  Run optimization
+                  Run {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
diff --git a/strudel-demo-app/src/pages/run-computation/_components/NewScenarioModal.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_components/NewScenarioModal.tsx
similarity index 96%
rename from strudel-demo-app/src/pages/run-computation/_components/NewScenarioModal.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_components/NewScenarioModal.tsx
index 6efe49ae..7a2c9347 100644
--- a/strudel-demo-app/src/pages/run-computation/_components/NewScenarioModal.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_components/NewScenarioModal.tsx
@@ -1,6 +1,7 @@
 import { Box, Button, FormControl, InputLabel, Link, MenuItem, Modal, Select, Stack, TextField, Typography } from '@mui/material';
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
+import { taskflow } from '../_config/taskflow.config';
 
 interface Props {
   modalOpen: boolean;
@@ -47,7 +48,7 @@ export const NewScenarioModal: React.FC<Props> = ({
       >
         <Typography id="modal-modal-title" variant="h6" component="h2">
           {/* strudel-kit-variable-next-line */}
-          New Analysis Scenario
+          New Analysis {taskflow.properties.itemName}
         </Typography>
         <FormControl fullWidth>
           <InputLabel id="analysis-type-select-label">Analysis Type</InputLabel>
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_config/taskflow.config.ts b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_config/taskflow.config.ts
new file mode 100644
index 00000000..bdc1eef5
--- /dev/null
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_config/taskflow.config.ts
@@ -0,0 +1,123 @@
+import { RunComputationConfig } from "./taskflow.types";
+
+export const taskflow: RunComputationConfig = {
+  properties: {
+    itemName: "scenario",
+    itemNamePlural: "scenarios",
+  },
+  data: {
+    items: {
+      source: "default/run-computation/list.json",
+      idField: "id"
+    },
+    inputs: {
+      source: "default/run-computation/inputs.json",
+      idField: ""
+    },
+    lineChart: {
+      source: "default/run-computation/results_line_chart.json"
+    },
+    barChart: {
+      source: "default/run-computation/results_bar_chart.json"
+    },
+    results: {
+      source: "default/run-computation/results_table.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Scenario List",
+      description: "Scenarios represent a set of analysis inputs / parameters / settings and the results of that analysis.",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Scenario Name",
+          width: 200
+        },
+        {
+          field: "analysisType",
+          headerName: "Analysis Type",
+          width: 200
+        },
+        {
+          field: "createdDate",
+          headerName: "Date Created",
+          width: 200
+        },
+        {
+          field: "status",
+          headerName: "Status",
+          width: 200
+        }
+      ]
+    },
+    dataInputs: {
+      title: "Data Inputs",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Unit Name",
+          width: 200
+        },
+        {
+          field: "unitType",
+          headerName: "Unit Type",
+          width: 200
+        },
+        {
+          field: "constraints",
+          headerName: "Constraints",
+          width: 200
+        },
+        {
+          field: "lowerBound",
+          headerName: "Lower Bound",
+          width: 200,
+          type: "number"
+        },
+        {
+          field: "upperBound",
+          headerName: "Upper Bound",
+          width: 200,
+          type: "number"
+        }
+      ]
+    },
+    settings: {
+      title: "Optimization Settings"
+    },
+    results: {
+      title: "Results",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Unit Name",
+          width: 200
+        },
+        {
+          field: "unitType",
+          headerName: "Unit Type",
+          width: 200
+        },
+        {
+          field: "constraints",
+          headerName: "Constraints",
+          width: 200
+        },
+        {
+          field: "lowerBound",
+          headerName: "Lower Bound",
+          width: 200,
+          type: "number"
+        },
+        {
+          field: "upperBound",
+          headerName: "Upper Bound",
+          width: 200,
+          type: "number"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_config/taskflow.types.ts b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_config/taskflow.types.ts
new file mode 100644
index 00000000..dbdc4812
--- /dev/null
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_config/taskflow.types.ts
@@ -0,0 +1,58 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface RunComputationConfig {
+  /** Attributes that are used across the Task Flow */
+  properties: {
+    itemName: string,
+    itemNamePlural: string
+  },
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    inputs: {
+      source: string,
+      idField: string
+    },
+    results: {
+      source: string,
+      idField: string
+    },
+    barChart: {
+      source: string,
+      idField?: string
+    },
+    lineChart: {
+      source: string,
+      idField?: string
+    },
+    [key: string]: {
+      source: string,
+      idField?: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      tableColumns: GridColDef[]
+    },
+    dataInputs: {
+      title: string,
+      tableColumns: GridColDef[]
+    },
+    settings: {
+      title: string,
+    },
+    results: {
+      title: string,
+      tableColumns: GridColDef[]
+    },
+
+  }
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/run-computation/_context/ContextProvider.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/run-computation/_context/ContextProvider.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_context/ContextProvider.tsx
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/context/actions.ts b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_context/actions.ts
similarity index 100%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/context/actions.ts
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_context/actions.ts
diff --git a/strudel-demo-app/src/pages/run-computation/_layout.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_layout.tsx
similarity index 76%
rename from strudel-demo-app/src/pages/run-computation/_layout.tsx
rename to strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_layout.tsx
index 122e476c..9e918432 100644
--- a/strudel-demo-app/src/pages/run-computation/_layout.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/_layout.tsx
@@ -9,15 +9,14 @@ import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
 import { basename } from '../../main';
 import { RunComputationProvider } from './_context/ContextProvider';
-import definitions from './_config/definitions.json';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the run-computation Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
 const RunComputationLayout: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const listItems = useDataFromSource('default/run-computation/list.json', basename)
+  const listItems = useDataFromSource(taskflow.data.items.source, basename)
 
   /**
    * Content to render on the page for this component
@@ -32,10 +31,9 @@ const RunComputationLayout: React.FC = () => {
           list={{
             table: {
               data: listItems,
-              // strudel-kit-variable-next-line
-              dataIdField: 'id',
+              dataIdField: taskflow.data.items.idField,
               columns: [
-                ...definitions.list.table.columns,
+                ...taskflow.pages.index.tableColumns,
                 { 
                   field: 'actions', 
                   headerName: 'Actions', 
@@ -53,17 +51,15 @@ const RunComputationLayout: React.FC = () => {
           inputs={{
             table: {
               data: [],
-              // strudel-kit-variable-next-line
-              dataIdField: 'id',
-              columns: definitions.inputs.table.columns,
+              dataIdField: taskflow.data.inputs.idField,
+              columns: taskflow.pages.dataInputs.tableColumns,
             }
           }}
           results={{
             table: {
               data: [],
-              // strudel-kit-variable-next-line
-              dataIdField: 'id',
-              columns: definitions.results.table.columns,
+              dataIdField: taskflow.data.results.idField,
+              columns: taskflow.pages.results.tableColumns,
             }
           }}
         >
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/definitions.json b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/definitions.json
deleted file mode 100644
index c3a882ef..00000000
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/definitions.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
-  "list": {
-    "table": {
-      "columns": [
-        { 
-          "field": "name", 
-          "headerName": "Scenario Name", 
-          "width": 200 
-        },
-        { 
-          "field": "analysisType", 
-          "headerName": "Analysis Type", 
-          "width": 200 
-        },
-        { 
-          "field": "createdDate", 
-          "headerName": "Date Created", 
-          "width": 200 
-        },
-        { 
-          "field": "status", 
-          "headerName": "Status", 
-          "width": 200 
-        }
-      ]
-    }
-  },
-  "inputs": {
-    "table": {
-      "columns": [
-        { 
-          "field": "name", 
-          "headerName": "Unit Name", 
-          "width": 200 
-        },
-        { 
-          "field": "unitType", 
-          "headerName": "Unit Type", 
-          "width": 200 
-        },
-        { 
-          "field": "constraints", 
-          "headerName": "Constraints", 
-          "width": 200
-        },
-        { 
-          "field": "lowerBound", 
-          "headerName": "Lower Bound", 
-          "width": 200,
-          "type": "number"
-        },
-        { 
-          "field": "upperBound", 
-          "headerName": "Upper Bound", 
-          "width": 200,
-          "type": "number"
-        }
-      ]
-    }
-  },
-  "results": {
-    "table": {
-      "columns": [
-        { 
-          "field": "name", 
-          "headerName": "Unit Name", 
-          "width": 200 
-        },
-        { 
-          "field": "unitType", 
-          "headerName": "Unit Type", 
-          "width": 200 
-        },
-        { 
-          "field": "constraints", 
-          "headerName": "Constraints", 
-          "width": 200
-        },
-        { 
-          "field": "lowerBound", 
-          "headerName": "Lower Bound", 
-          "width": 200,
-          "type": "number"
-        },
-        { 
-          "field": "upperBound", 
-          "headerName": "Upper Bound", 
-          "width": 200,
-          "type": "number"
-        }
-      ]
-    }
-  }
-}
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/index.tsx b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/index.tsx
index 7c56f7c6..519296e4 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/index.tsx
+++ b/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/index.tsx
@@ -1,1 +1,76 @@
-export { RunComputationWrapper } from './RunComputationWrapper';
\ No newline at end of file
+import { Box, Button, Container, Paper, Stack, Typography } from '@mui/material';
+import React, { useState } from 'react';
+import { DataGrid } from '@mui/x-data-grid';
+import { NewScenarioModal } from './_components/NewScenarioModal';
+import { useRunComputation } from './_context/ContextProvider';
+import { taskflow } from './_config/taskflow.config';
+
+/**
+ * List page for all compuation runs in the run-computation Task Flow.
+ * Can open the `<NewScenarioModal>` from here and then proceed to the 
+ * `<DataInputs>` component for the next step.
+ */
+export const ListPage: React.FC = () => {
+  const { state, dispatch } = useRunComputation();
+  const [modalOpen, setModalOpen] = useState(false);
+
+  const handleNewScenario = () => {
+    setModalOpen(true);
+  };
+
+  /**
+   * Content to render on the page for this component
+   */
+  return (
+    <Box>
+      <Container 
+        maxWidth="xl"
+        sx={{
+          mt: 4
+        }}
+      >
+        <Paper>
+          <Stack
+            direction="row"
+            spacing={2}
+            alignItems="center"
+            sx={{
+              padding: 2
+            }}
+          >
+            <Box flex={1}>
+              <Typography 
+                variant="h6" 
+                component="h1" 
+              >
+                {taskflow.pages.index.title}
+              </Typography>
+              <Typography 
+                variant="subtitle1" 
+                component="p" 
+              >
+                {taskflow.pages.index.description}
+              </Typography>
+            </Box>
+            <Button
+              variant="contained"
+              onClick={handleNewScenario}
+            >
+              New {taskflow.properties.itemName}
+            </Button>
+            <NewScenarioModal modalOpen={modalOpen} setModalOpen={setModalOpen} />
+          </Stack>
+          <DataGrid
+            rows={state.list.table.data}
+            getRowId={(row) => row[state.list.table.dataIdField]}
+            columns={state.list.table.columns}
+            disableColumnSelector
+            disableRowSelectionOnClick
+          />
+        </Paper>  
+      </Container>
+    </Box>
+  )
+}
+
+export default ListPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/search-data-repositories/[id].tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/[id].tsx
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/[id].tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/[id].tsx
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_components/DataListCard.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/DataListCard.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/_components/DataListCard.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/DataListCard.tsx
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_components/DataListPanel.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/DataListPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/_components/DataListPanel.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/DataListPanel.tsx
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_components/FiltersPanel.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/FiltersPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/_components/FiltersPanel.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/FiltersPanel.tsx
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_components/PreviewPanel.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/PreviewPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/_components/PreviewPanel.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_components/PreviewPanel.tsx
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_config/taskflow.config.ts b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_config/taskflow.config.ts
new file mode 100644
index 00000000..e2da70eb
--- /dev/null
+++ b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_config/taskflow.config.ts
@@ -0,0 +1,74 @@
+import { SearchDataRepositoriesConfig } from "./taskflow.types";
+
+export const taskflow: SearchDataRepositoriesConfig = {
+  data: {
+    items: {
+      source: "default/search-data-repositories/datasets.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Search Data Repositories App",
+      description: "Description of this app section",
+      cardFields: {
+        title: "title",
+        content: "summary",
+        tags: "tags"
+      },
+      cardFilters: [
+        {
+          field: "category",
+          displayName: "Category",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "Groundwater",
+                value: "Groundwater"
+              },
+              {
+                label: "Fires",
+                value: "Fires"
+              },
+              {
+                label: "Floods",
+                value: "Floods"
+              },
+              {
+                label: "Earthquakes",
+                value: "Earthquakes"
+              }
+            ]
+          }
+        },
+        {
+          field: "tags",
+          displayName: "Tags",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "Boreal forest",
+                value: "Boreal forest"
+              },
+              {
+                label: "Carbon and greenhouse gas emissions",
+                value: "Carbon and greenhouse gas emissions"
+              },
+              {
+                label: "Ecology",
+                value: "Ecology"
+              }
+            ]
+          }
+        },
+        {
+          field: "publication_date",
+          displayName: "Publication Date",
+          filterType: "date range"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_config/taskflow.types.ts b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_config/taskflow.types.ts
new file mode 100644
index 00000000..f3117167
--- /dev/null
+++ b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_config/taskflow.types.ts
@@ -0,0 +1,37 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface SearchDataRepositoriesConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: Record<string, any>,
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      cardFields: {
+        title: string,
+        content: string,
+        tags: string
+      },
+      cardFilters: {
+        field: string,
+        displayName: string,
+        filterType: 'CheckboxList' | 'Slider' | 'date range',
+        props?: object
+      }[]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/context/ContextProvider.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/context/ContextProvider.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_context/ContextProvider.tsx
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/context/actions.ts b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_context/actions.ts
similarity index 100%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/context/actions.ts
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_context/actions.ts
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_layout.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_layout.tsx
similarity index 72%
rename from strudel-demo-app/src/pages/search-data-repositories/_layout.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_layout.tsx
index a3c9d11e..ce088df1 100644
--- a/strudel-demo-app/src/pages/search-data-repositories/_layout.tsx
+++ b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/_layout.tsx
@@ -4,16 +4,15 @@ import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
 import { SearchDataRepositoriesProvider } from './_context/ContextProvider';
-import definitions from './_config/definitions.json';
 import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the search-data-repositories Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
 const SearchDataRepositoriesLayout: React.FC = () => {
-  // strudel-kit-variable-next-line
-  const datasets = useDataFromSource('default/search-data-repositories/datasets.json', basename)
+  const datasets = useDataFromSource(taskflow.data.items.source, basename)
 
   /**
    * Content to render on the page for this component
@@ -26,10 +25,9 @@ const SearchDataRepositoriesLayout: React.FC = () => {
       <Box>
         <SearchDataRepositoriesProvider 
           data={datasets} 
-          // strudel-kit-variable-next-line
-          dataIdField='id' 
-          filters={definitions.filters.main} 
-          cardFields={definitions.columns.main.card}
+          dataIdField={taskflow.data.items.idField} 
+          filters={taskflow.pages.index.cardFilters} 
+          cardFields={taskflow.pages.index.cardFields}
         >
           <Outlet />
         </SearchDataRepositoriesProvider>
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/definitions.json b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/definitions.json
deleted file mode 100644
index 0a3dea6b..00000000
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/definitions.json
+++ /dev/null
@@ -1,70 +0,0 @@
-{
-  "columns": {
-    "main": {
-      "card": {
-        "title": "title",
-        "content": "summary",
-        "tags": "tags"
-      }
-    }
-  },
-  "filters": {
-    "main": [
-      {
-        "field": "category",
-        "displayName": "Category",
-        "filterType": "CheckboxList",
-        "props": {
-          "options": [
-            {
-              "label": "Fires",
-              "value": "Fires"
-            },
-            {
-              "label": "Floods",
-              "value": "Floods"
-            },
-            {
-              "label": "Earthquakes",
-              "value": "Earthquakes"
-            },
-            {
-              "label": "Groundwater",
-              "value": "Groundwater"
-            },
-            {
-              "label": "Biology",
-              "value": "Biology"
-            }
-          ]
-        }
-      },
-      {
-        "field": "tags",
-        "displayName": "Tags",
-        "filterType": "CheckboxList",
-        "props": {
-          "options": [
-            {
-              "label": "Boreal forest",
-              "value": "Boreal forest"
-            },
-            {
-              "label": "Carbon and greenhouse gas emissions",
-              "value": "Carbon and greenhouse gas emissions"
-            },
-            {
-              "label": "Ecology",
-              "value": "Ecology"
-            }
-          ]
-        }
-      },
-      {
-        "field": "publication_date",
-        "displayName": "Publication Date",
-        "filterType": "date range"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/search-data-repositories/index.tsx b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/index.tsx
similarity index 93%
rename from strudel-demo-app/src/pages/search-data-repositories/index.tsx
rename to strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/index.tsx
index 1f51c9f6..a891e633 100644
--- a/strudel-demo-app/src/pages/search-data-repositories/index.tsx
+++ b/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/index.tsx
@@ -6,6 +6,7 @@ import { FiltersPanel } from './_components/FiltersPanel';
 import { PreviewPanel } from './_components/PreviewPanel';
 import { useSearchDataRepositories } from './_context/ContextProvider';
 import { setPreviewItem } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * The main explore page for the search-data-repositories Task Flow.
@@ -33,10 +34,8 @@ const DatasetExplorer: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        // strudel-kit-variable-next-line
-        pageTitle="Dataset Releases"
-        // strudel-kit-variable-next-line
-        description="Datasets"
+        pageTitle={taskflow.pages.index.title}
+        description={taskflow.pages.index.description}
         sx={{
           marginBottom: 1,
           padding: 2,
diff --git a/strudel-cookiecutter/templates/cookiecutter.json b/strudel-cookiecutter/templates/cookiecutter.json
new file mode 100644
index 00000000..03b216c1
--- /dev/null
+++ b/strudel-cookiecutter/templates/cookiecutter.json
@@ -0,0 +1,1 @@
+{"name": "my-taskflow", "_jinja2_env_vars": {"variable_start_string": "{@", "variable_end_string": "@}"}}
\ No newline at end of file
diff --git a/strudel-demo-app/.gitignore b/strudel-demo-app/.gitignore
deleted file mode 100644
index a547bf36..00000000
--- a/strudel-demo-app/.gitignore
+++ /dev/null
@@ -1,24 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
diff --git a/strudel-demo-app/public/data/default/compare-data/scenarios.json b/strudel-demo-app/public/data/default/compare-data/scenarios.json
deleted file mode 100644
index 736180de..00000000
--- a/strudel-demo-app/public/data/default/compare-data/scenarios.json
+++ /dev/null
@@ -1,178 +0,0 @@
-[
-  {
-    "id": 0,
-    "name": "Scenario Dandelion",
-    "description": "Ex aliquip consectetur laboris occaecat deserunt nulla aliqua est ut.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 25000000,
-    "tss_concentration": 500,
-    "cod_concentration": 700,
-    "tkn_concentration": 65,
-    "acetic_acid_concentration": 0,
-    "metric6": 15,
-    "metric7": 245,
-    "metric8": 9852,
-    "metric9": 4,
-    "metric10": 0
-  },
-  {
-    "id": 1,
-    "name": "Scenario Tulip",
-    "description": "Occaecat consequat exercitation minim do ullamco laborum aliquip.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 28000000,
-    "tss_concentration": 700,
-    "cod_concentration": 300,
-    "tkn_concentration": 85,
-    "acetic_acid_concentration": 2,
-    "metric6": 65,
-    "metric7": 290,
-    "metric8": 8002,
-    "metric9": 1,
-    "metric10": 1
-  },
-  {
-    "id": 2,
-    "name": "Scenario Orchid",
-    "description": "Cupidatat est magna commodo elit ad laborum esse adipisicing cupidatat.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 33000000,
-    "tss_concentration": 500,
-    "cod_concentration": 700,
-    "tkn_concentration": 65,
-    "acetic_acid_concentration": 1,
-    "metric6": 15,
-    "metric7": 209,
-    "metric8": 7089,
-    "metric9": 3,
-    "metric10": 1
-  },
-  {
-    "id": 3,
-    "name": "Scenario Sunflower",
-    "description": "Irure pariatur fugiat culpa cillum non veniam adipisicing amet.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 28000000,
-    "tss_concentration": 400,
-    "cod_concentration": 870,
-    "tkn_concentration": 77,
-    "acetic_acid_concentration": 1,
-    "metric6": 20,
-    "metric7": 300,
-    "metric8": 8076,
-    "metric9": 2,
-    "metric10": 1
-  },
-  {
-    "id": 4,
-    "name": "Scenario Rose",
-    "description": "Et ullamco cillum ipsum in culpa irure nostrud.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 31000000,
-    "tss_concentration": 650,
-    "cod_concentration": 820,
-    "tkn_concentration": 40,
-    "acetic_acid_concentration": 2,
-    "metric6": 18,
-    "metric7": 380,
-    "metric8": 8000,
-    "metric9": 5,
-    "metric10": 7
-  },
-  {
-    "id": 5,
-    "name": "Scenario Daisy",
-    "description": "Mollit veniam occaecat ea velit et officia ut voluptate.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 29000000,
-    "tss_concentration": 550,
-    "cod_concentration": 900,
-    "tkn_concentration": 95,
-    "acetic_acid_concentration": 1,
-    "metric6": 15,
-    "metric7": 270,
-    "metric8": 9800,
-    "metric9": 3,
-    "metric10": 0
-  },
-  {
-    "id": 6,
-    "name": "Scenario Azalea",
-    "description": "Enim cupidatat nisi enim in ex cillum.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 45000000,
-    "tss_concentration": 570,
-    "cod_concentration": 490,
-    "tkn_concentration": 60,
-    "acetic_acid_concentration": 0,
-    "metric6": 20,
-    "metric7": 300,
-    "metric8": 8003,
-    "metric9": 2,
-    "metric10": 1
-  },
-  {
-    "id": 7,
-    "name": "Scenario Zinnia",
-    "description": "Esse consequat mollit nulla enim esse duis excepteur.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 23000000,
-    "tss_concentration": 480,
-    "cod_concentration": 900,
-    "tkn_concentration": 68,
-    "acetic_acid_concentration": 0,
-    "metric6": 44,
-    "metric7": 479,
-    "metric8": 6809,
-    "metric9": 5,
-    "metric10": 2
-  },
-  {
-    "id": 8,
-    "name": "Scenario Daffodil",
-    "description": "Irure nulla veniam elit laborum dolor non culpa deserunt veniam laborum voluptate qui esse.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 39000000,
-    "tss_concentration": 200,
-    "cod_concentration": 880,
-    "tkn_concentration": 48,
-    "acetic_acid_concentration": 3,
-    "metric6": 20,
-    "metric7": 450,
-    "metric8": 9052,
-    "metric9": 2,
-    "metric10": 0
-  },
-  {
-    "id": 9,
-    "name": "Scenario Amaryllis",
-    "description": "Incididunt sit nisi proident ex.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 43000000,
-    "tss_concentration": 600,
-    "cod_concentration": 860,
-    "tkn_concentration": 70,
-    "acetic_acid_concentration": 1,
-    "metric6": 22,
-    "metric7": 400,
-    "metric8": 7080,
-    "metric9": 4,
-    "metric10": 2
-  },
-  {
-    "id": 10,
-    "name": "Scenario Carnation",
-    "description": "Officia excepteur eiusmod anim elit excepteur ad non dolor.",
-    "analysis_type": "Sensitivity Analysis",
-    "volumetric_flow_rate": 41000000,
-    "tss_concentration": 450,
-    "cod_concentration": 700,
-    "tkn_concentration": 65,
-    "acetic_acid_concentration": 1,
-    "metric6": 30,
-    "metric7": 445,
-    "metric8": 6872,
-    "metric9": 3,
-    "metric10": 1
-  }
-]
diff --git a/strudel-demo-app/public/data/default/contribute-data/contributor_datasets.json b/strudel-demo-app/public/data/default/contribute-data/contributor_datasets.json
deleted file mode 100644
index c6af0d1a..00000000
--- a/strudel-demo-app/public/data/default/contribute-data/contributor_datasets.json
+++ /dev/null
@@ -1,167 +0,0 @@
-[
-  {
-    "id": 0,
-    "title": "Data from Decadal Change in Groundwater Quality Web Site (1988 - 2012)",
-    "publication_date": "2018-02-02",
-    "start_date": "1988-01-01",
-    "end_date": "2010-12-31",
-    "citation": "Lindsey, B.D., Johnson, T.D., and Belitz, Kenneth, 2018, Data from Decadal Change in Groundwater Quality Web Site, 1988-2012: U.S. Geological Survey data release, https://doi.org/10.5066/F7PC30WD.",
-    "doi": "10.5066/F7PC30WD",
-    "summary": "Groundwater quality data were collected in 5,000 wells between 1988-2001 by the National Water-Quality Assessment Project. About 1,500 of these wells were sampled again between 2002-2012 to evaluate decadal changes in groundwater quality. Monitoring wells, domestic supply wells, and some public supply wells were included in this study.",
-    "tags": [ "NAWQA", "National Water Quality Program", "USGS Science Data Catalog (SDC)", "Cycle 3", "groundwater quality", "trends", "water quality"],
-    "category": "Groundwater",
-    "purpose": "Data were collected as a part of the NAWQA Land use studies or Major aquifer studies. Initially data were collected to characterize the quality of shallow and medium depths of groundwater in the nation. Subsequent samples were collected to evaluate changes in concentrations over a decadal time span. Mapping criteria and benchmarks: The NAWQA Project has developed an interactive mapping tool that displays decadal changes in concentrations.",
-    "point_of_contact": "Bruce  D. Lindsey",
-    "originator": "Bruce  D. Lindsey, Tyler D Johnson",
-    "metadata_contact": "Jennifer B Sharpe",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 1,
-    "title": "Fire perimeter data for the 1987 Black Dragon Fire in China (1987)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Geospatial data were developed to characterize pre-fire biomass, burn severity, and biomass consumed for the Black Dragon Fire that burned in northern China in 1987. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Fires",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 2,
-    "title": "Flood Inundation Extent and Depth in Selected Areas of Southern U.S. (March 2016)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Heavy rainfall occurred across Louisiana, Texas, Arkansas, and Mississippi in March 2016 as a result of a slow-moving southward dip in the jetstream , funneling tropical moisture into Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Louisiana", "Mississippi", "Texas", "Floods", "USGS Data Catalog (SDC)"],
-    "category": "Floods",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 3,
-    "title": "Bathymetric survey of the Brandon Road Dam Spillway, Joliet, Illinois  (May 2015)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Bathymetric survey data of the Brandon Road Dam spillway was collected on May 27 and May 28, 2015 by the US Geological Survey (USGS) using Trimble Real-Time Kinematic Global Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Dam", "GPS measurement", "Brandon Road Lock and Dam", "Floods", "USGS Data Catalog (SDC)"],
-    "category": "Groundwater",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 4,
-    "title": "Data from mercury intrusion porosimetry conducted on samples of a mudstone underlying the Naval Air Warfare Center, West Trenton, New Jersey",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "This data set presents the results of conducting Mercury Intrusion Porosimetry on core samples of the mudstone underlying the Naval Air Warfare Center, West Trenton, New Jersey Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Sedimentary rock", "porosity", "West Trenton",  "USGS Data Catalog (SDC)"],
-    "category": "Groundwater",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/explore-data/genomes.tsv b/strudel-demo-app/public/data/default/explore-data/genomes.tsv
deleted file mode 100644
index 71e33836..00000000
--- a/strudel-demo-app/public/data/default/explore-data/genomes.tsv
+++ /dev/null
@@ -1,319 +0,0 @@
-﻿Organism	Proteome_ID	Assembly	Annotation	Common Name	Euk. BUSCO %	Emb. BUSCO %	Data Usage Policy
-Acorus americanus v1.1	586	JGI	JGI	sweetflag	98.0	94.2	restricted
-Agave tequilana var. Weber's Blue HAP1 v2.1	790	JGI	JGI	Weber's Blue agave	98.3	97.0	restricted
-Agave tequilana var. Weber's Blue HAP2 v2.1	791	JGI	JGI	Weber's Blue agave	98.3	97.4	restricted
-Alyssum linifolium v1.1	472	JGI	JGI		98.7	98.5	restricted
-Amaranthus hypochondriacus v2.1	459	BYU	BYU	grain amaranth	91.7	77.6	unrestricted
-Amborella trichopoda v1.0	291	AGP	AGP	amborella	94.4	84.5	unrestricted
-Amborella trichopoda var. SantaCruz_75 HAP1 v2.1	727	JGI	JGI		99.0	93.5	restricted
-Amborella trichopoda var. SantaCruz_75 HAP2 v2.1	728	JGI	JGI		99.0	94.0	restricted
-Anacardium occidentale v0.9	449	JGI	JGI	cashew	96.7	91.1	restricted
-Ananas comosus v3	321	FAFU/UIUC	FAFU/UIUC	pineapple	92.1	89.3	unrestricted
-Andropogon gerardi HAP1 v1.1	783	JGI	JGI	Big bluestem	98.7	99.7	restricted
-Andropogon gerardi HAP2 v1.1	784	JGI	JGI	Big bluestem	98.7	99.7	restricted
-Aquilegia coerulea v3.1	322	JGI	JGI	Colorado blue columbine	98.7	95.8	unrestricted
-Arabidopsis halleri v1.1	264	JGI	JGI		89.4	85.9	restricted
-Arabidopsis halleri v2.1.0	765	MPIB	MGPP-RUB		98.3	94.0	unrestricted
-Arabidopsis lyrata v2.1	384	JGI	MPIPZ	Lyrate rockcress	98.0	98.2	unrestricted
-Arabidopsis thaliana TAIR10	167	TAIR	TAIR	thale cress	99.0	99.3	unrestricted
-Arabidopsis thaliana Araport11	447	TAIR	Araport	thale cress	98.7	99.3	unrestricted
-Arachis hypogaea v1.0	530	PeanutBase	PeanutBase	peanut	97.0	92.8	unrestricted
-Asparagus officinalis V1.1	498	UGA	UGA	garden asparagus	88.8	79.0	unrestricted
-Beta vulgaris EL10_1.0	548	MSU	MSU	beet	94.7	89.2	unrestricted
-Beta vulgaris ssp. vulgaris EL10.2_2	782	USDA	JGI	sugar beet EL10	96.7	92.2	unrestricted
-Betula platyphylla v1.1	679	NEFU	NEFU	Asian white birch	93.7	92.4	unrestricted
-Boechera stricta v1.2	278	JGI	JGI	Drummond's rockcress	96.0	97.4	unrestricted
-Botryococcus braunii v2.1	502	JGI	JGI		80.5	22.6	restricted
-Brachypodium arbuscula BARB1 v3.1	720	JGI	JGI		98.3	99.0	restricted
-Brachypodium distachyon v3.1	314	JGI	JGI	purple false brome	98.3	98.6	unrestricted
-Brachypodium distachyon v2.1	283	JGI	JGI	purple false brome	98.3	98.3	unrestricted
-Brachypodium distachyon v3.2	556	JGI	JGI	purple false brome	98.3	99.3	restricted
-Brachypodium distachyon ABR2 v1	337	JGI	JGI	purple false brome	98.0	97.4	unrestricted
-Brachypodium distachyon ABR3 v1	343	JGI	JGI	purple false brome	98.7	97.4	unrestricted
-Brachypodium distachyon ABR4 v1	364	JGI	JGI	purple false brome	97.7	97.2	unrestricted
-Brachypodium distachyon ABR5 v1	379	JGI	JGI	purple false brome	97.4	96.4	unrestricted
-Brachypodium distachyon ABR6 v1	336	JGI	JGI	purple false brome	98.0	97.9	unrestricted
-Brachypodium distachyon ABR7 v1	369	JGI	JGI	purple false brome	97.7	98.1	unrestricted
-Brachypodium distachyon ABR8 v1	356	JGI	JGI	purple false brome	93.1	91.5	unrestricted
-Brachypodium distachyon ABR9 v1	333	JGI	JGI	purple false brome	97.7	96.8	unrestricted
-Brachypodium distachyon Adi-10 v1	381	JGI	JGI	purple false brome	94.4	84.7	unrestricted
-Brachypodium distachyon Adi-12 v1	359	JGI	JGI	purple false brome	96.7	95.8	unrestricted
-Brachypodium distachyon Adi-2 v1	372	JGI	JGI	purple false brome	98.0	97.6	unrestricted
-Brachypodium distachyon Arn1 v1	355	JGI	JGI	purple false brome	97.4	97.8	unrestricted
-Brachypodium distachyon Bd1-1 v1	349	JGI	JGI	purple false brome	94.1	96.1	unrestricted
-Brachypodium distachyon Bd1-1 v1.1	549	JGI	JGI	purple false brome	98.7	99.4	restricted
-Brachypodium distachyon Bd18-1 v1	362	JGI	JGI	purple false brome	98.0	98.4	unrestricted
-Brachypodium distachyon Bd2-3 v1	353	JGI	JGI	purple false brome	97.7	95.4	unrestricted
-Brachypodium distachyon Bd21 AnntCtrl v1	331	JGI	JGI	purple false brome	97.7	98.1	unrestricted
-Brachypodium distachyon Bd21 AsmbCtrl v1	361	JGI	JGI	purple false brome	97.7	97.1	unrestricted
-Brachypodium distachyon Bd21-3 v1.1	460	JGI	JGI	purple false brome	98.0	98.0	restricted
-Brachypodium distachyon Bd21-3 v1	378	JGI	JGI	purple false brome	97.4	96.7	unrestricted
-Brachypodium distachyon Bd21-3 v1.2	537	JGI	JGI	purple false brome	98.3	99.0	restricted
-Brachypodium distachyon Bd29-1 v1	346	JGI	JGI	purple false brome	94.7	93.6	unrestricted
-Brachypodium distachyon Bd3-1 v1	328	JGI	JGI	purple false brome	86.8	85.6	unrestricted
-Brachypodium distachyon Bd30-1 v1	344	JGI	JGI	purple false brome	97.0	96.0	unrestricted
-Brachypodium distachyon Bd30-1 v1.1	515	JGI	JGI	purple false brome	98.1	98.1	restricted
-Brachypodium distachyon BdTR10c v1	374	JGI	JGI	purple false brome	97.0	93.3	unrestricted
-Brachypodium distachyon BdTR11a v1	380	JGI	JGI	purple false brome	98.0	95.9	unrestricted
-Brachypodium distachyon BdTR11g v1	357	JGI	JGI	purple false brome	98.3	97.6	unrestricted
-Brachypodium distachyon BdTR11i v1	363	JGI	JGI	purple false brome	98.0	97.4	unrestricted
-Brachypodium distachyon BdTR12c v1	352	JGI	JGI	purple false brome	96.7	94.8	unrestricted
-Brachypodium distachyon BdTR13a v1	334	JGI	JGI	purple false brome	98.7	97.8	unrestricted
-Brachypodium distachyon BdTR13c v1	365	JGI	JGI	purple false brome	96.7	95.1	unrestricted
-Brachypodium distachyon BdTR1i v1	345	JGI	JGI	purple false brome	98.3	97.8	unrestricted
-Brachypodium distachyon BdTR2b v1	376	JGI	JGI	purple false brome	96.7	96.7	unrestricted
-Brachypodium distachyon BdTR2g v1	367	JGI	JGI	purple false brome	95.7	95.3	unrestricted
-Brachypodium distachyon BdTR3c v1	354	JGI	JGI	purple false brome	98.0	98.0	unrestricted
-Brachypodium distachyon BdTR5i v1	370	JGI	JGI	purple false brome	96.4	94.1	unrestricted
-Brachypodium distachyon BdTR7a v1	329	JGI	JGI	purple false brome	98.0	97.2	unrestricted
-Brachypodium distachyon BdTR8i v1	348	JGI	JGI	purple false brome	97.7	97.2	unrestricted
-Brachypodium distachyon BdTR9k v1	358	JGI	JGI	purple false brome	96.0	94.5	unrestricted
-Brachypodium distachyon Bis-1 v1	338	JGI	JGI	purple false brome	97.7	96.9	unrestricted
-Brachypodium distachyon Foz1 v1	366	JGI	JGI	purple false brome	98.3	96.8	unrestricted
-Brachypodium distachyon Gaz-8 v1	339	JGI	JGI	purple false brome	88.8	75.0	unrestricted
-Brachypodium distachyon Jer1 v1	375	JGI	JGI	purple false brome	97.7	96.5	unrestricted
-Brachypodium distachyon Kah-1 v1	351	JGI	JGI	purple false brome	89.4	86.5	unrestricted
-Brachypodium distachyon Kah-5 v1	342	JGI	JGI	purple false brome	98.3	97.8	unrestricted
-Brachypodium distachyon Koz-1 v1	330	JGI	JGI	purple false brome	98.0	98.3	unrestricted
-Brachypodium distachyon Koz-3 v1	368	JGI	JGI	purple false brome	94.7	95.6	unrestricted
-Brachypodium distachyon Luc1 v1	347	JGI	JGI	purple false brome	98.3	97.8	unrestricted
-Brachypodium distachyon Mig3 v1	377	JGI	JGI	purple false brome	98.0	97.4	unrestricted
-Brachypodium distachyon Mon3 v1	350	JGI	JGI	purple false brome	98.0	96.6	unrestricted
-Brachypodium distachyon Mur1 v1	373	JGI	JGI	purple false brome	95.7	96.2	unrestricted
-Brachypodium distachyon Pangenome v1	335	JGI	JGI	purple false brome	98.7	98.7	unrestricted
-Brachypodium distachyon Per1 v1	326	JGI	JGI	purple false brome	98.3	97.8	unrestricted
-Brachypodium distachyon Ron2 v1	360	JGI	JGI	purple false brome	97.4	97.6	unrestricted
-Brachypodium distachyon S8iic v1	340	JGI	JGI	purple false brome	97.7	97.5	unrestricted
-Brachypodium distachyon Sig2 v1	371	JGI	JGI	purple false brome	98.7	96.7	unrestricted
-Brachypodium distachyon Tek-2 v1	341	JGI	JGI	purple false brome	96.7	96.7	unrestricted
-Brachypodium distachyon Tek-4 v1	332	JGI	JGI	purple false brome	82.2	75.1	unrestricted
-Brachypodium distachyon Uni2 v1	327	JGI	JGI	purple false brome	97.7	97.5	unrestricted
-Brachypodium hybridum v1.1	463	JGI	JGI		97.7	99.4	unrestricted
-Brachypodium hybridum Bhyb26 v2.1	693	JGI	JGI		98.3	99.7	restricted
-Brachypodium mexicanum v2.1	813		JGI		98.7	99.5	unrestricted
-Brachypodium mexicanum v1.1	577	JGI	JGI		98.7	99.5	unrestricted
-Brachypodium stacei v1.1	316	JGI	JGI		98.0	98.6	unrestricted
-Brachypodium sylvaticum v1.1	490	JGI	JGI		98.3	97.2	restricted
-Brachypodium sylvaticum Ain-1 v2.1	721	JGI	JGI		97.7	99.1	restricted
-Brassica oleracea capitata v1.0	446	OCRI	OCRI	cabbage	88.8	78.4	unrestricted
-Brassica rapa FPsc v1.3	277	JGI	JGI	turnip mustard	97.7	96.9	unrestricted
-Brassica rapa ssp. trilocularis R500 v2.1	795	JGI	JGI		98.3	99.0	restricted
-Cakile maritima v1.1	481	JGI	JGI	european searocket	98.3	97.9	restricted
-Capsella grandiflora v1.1	266	JGI	JGI		94.1	94.6	unrestricted
-Capsella rubella v1.1	474	JGI	JGI	Pink shepherd's purse	98.3	97.2	unrestricted
-Carica papaya ASGPBv0.4	113	ASGPB	ASGPB	papaya	82.5	72.0	unrestricted
-Carya illinoinensis v1.1	573	HA	JGI	pecan	98.7	97.8	unrestricted
-Carya illinoinensis Elliott v1.1	562	HA	HA	pecan	96.4	94.4	unrestricted
-Carya illinoinensis Lakota v1.1	561	HA	HA	pecan	98.3	97.5	unrestricted
-Carya illinoinensis Pawnee v1.1	588	HA	HA	pecan	98.0	95.9	unrestricted
-Castanea dentata v1.1	673	HA	HA	American chestnut	98.3	98.7	restricted
-Castanea mollissima Mahogany HAP1 v1.1	733	HA	HA	Chinese chestnut	97.7	97.6	restricted
-Castanea mollissima Mahogany HAP2 v1.1	729	HA	HA	Chinese chestnut	97.7	97.4	restricted
-Castanea mollissima Nanking HAP1 v1.1	732	HA	HA	Chinese chestnut	98.0	98.1	restricted
-Castanea mollissima Nanking HAP2 v1.1	731	HA	HA	Chinese chestnut	97.7	97.9	restricted
-Caulanthus amplexicaulis v1.1	470	JGI	JGI	claspingleaf wild cabbage	98.7	98.8	restricted
-Ceratodon purpureus GG1 v1.1	539	JGI	JGI		96.7	69.1	unrestricted
-Ceratodon purpureus R40 v1.1	538	JGI	JGI		96.4	69.0	unrestricted
-Ceratopteris richardii v2.1	676	JGI	JGI		98.0	71.0	unrestricted
-Cercis canadensis V3.1	705	JGI	JGI	eastern redbud	98.3	99.0	restricted
-Cercis canadensis HAP2 V3.1	706	JGI	JGI	eastern redbud	98.0	99.0	restricted
-Chasmanthium laxum v1.1	690	JGI	JGI	Slender wood oats	97.4	98.1	restricted
-Chenopodium quinoa v1.0	392	KAUST	KAUST	quinoa	96.0	90.6	unrestricted
-Chlamydomonas reinhardtii v5.6	281	JGI	JGI	green algae	96.0	27.6	unrestricted
-Chlamydomonas reinhardtii CC-4532 v6.1	707	JGI	JGI	green algae	97.0	28.5	unrestricted
-Chromochloris zofingiensis v5.2.3.2	461	UCLA	UCLA		84.5	27.0	unrestricted
-Cicer arietinum v1.0	492	CGIAR	CGIAR	chickpea	95.4	89.6	unrestricted
-Cinnamomum kanehirae v3	531	BRCAS	BRCAS	stout camphor tree	93.1	89.8	unrestricted
-Citrus clementina v1.0	182	JGI	JGI	clementine	96.7	94.6	unrestricted
-Citrus sinensis v1.1	154	JGI	JGI	sweet orange	91.4	87.2	unrestricted
-Cleome violacea v1.1	484	JGI	JGI	spider plant	98.7	97.6	restricted
-Cleome violacea v2.1	585	JGI	JGI	spider plant	98.7	98.1	restricted
-Coccomyxa subellipsoidea C-169 v2.0	227	JGI.FG	JGI.FG		89.1	26.5	unrestricted
-Coffea arabica v0.5	453	UCD	UCD	coffee	98.3	94.8	restricted
-Corylus americana var. rush v1.1	835	JGI	JGI	American hazelnut Rush	98.7	98.8	unrestricted
-Corylus americana var. winkler v1.1	834	JGI	JGI	American hazelnut Winkler	96.7	95.1	unrestricted
-Corymbia citriodora v2.1	507	JGI	JGI	lemon-scented gum	95.1	95.1	unrestricted
-Crambe hispanica v1.1	488	JGI	JGI		98.3	97.8	restricted
-Cucumis sativus v1.0	122	Roche/JGI	Roche/JGI	cucumber	95.0	89.0	unrestricted
-Daucus carota v2.0	388	WISC	WISC	carrot	92.0	83.2	unrestricted
-Descurainia sophioides v1.1	482	JGI	JGI	northern tansymustard	98.0	96.5	restricted
-Dioscorea alata v1.1	504	JGI	JGI	yam	88.1	79.5	unrestricted
-Dioscorea alata v2.1	550	UC Berkeley	JGI	yam	96.4	94.9	restricted
-Diphasiastrum complanatum v3.1	723	JGI	JGI		97.4	76.5	restricted
-Diptychocarpus strictus v1.1	489	JGI	JGI		98.0	93.9	restricted
-Diptychocarpus strictus v2.1	582	JGI	JGI		98.7	94.6	restricted
-Dunaliella salina v1.0	325	JGI	JGI		77.6	18.6	unrestricted
-Eleusine coracana v1.1	560	UGA/MIPS	UGA/MIPS	finger millet	97.0	94.9	unrestricted
-Eruca vesicaria v1.1	487	JGI	JGI	arugula	99.3	98.3	restricted
-Eschscholzia californica var. Aurantiaca Orange King Plant1.1 HAP1 v1.1	828	JGI	JGI	California poppy	98.7	97.6	restricted
-Eschscholzia californica var. Aurantiaca Orange King Plant1.1 HAP2 v1.1	827	JGI	JGI	California poppy	98.7	97.4	restricted
-Eucalyptus grandis v2.0	297	JGI	JGI	eucalyptus	95.0	92.3	unrestricted
-Euclidium syriacum v1.1	483	JGI	JGI	syrian mustard	97.7	96.7	restricted
-Eutrema salsugineum v1.0	173	JGI	JGI	salt cress	98.7	98.4	unrestricted
-Fragaria vesca v2.0.a2	501	HAU	HAU	woodland strawberry	96.4	95.8	unrestricted
-Fragaria vesca v4.0.a2	677	UCD	HAU	woodland strawberry	97.7	97.8	unrestricted
-Fragaria x ananassa v1.0.a1	675	UCD	UCD	strawberry	98.3	96.9	unrestricted
-Fragaria x ananassa Royal Royce v1.0	701	UCDAVIS	UCDAVIS	allo-octoploid strawberry	97.7	97.4	restricted
-Glycine max Wm82.a2.v1	275	JGI	JGI	soybean	98.3	97.4	unrestricted
-Glycine max Wm82.a4.v1	508	JGI	JGI	soybean	98.3	97.6	unrestricted
-Glycine max Fiskeby v1.1	678	HA	JGI	soybean	98.3	97.7	restricted
-Glycine max Lee v1.1	510	JGI	JGI	soybean	97.3	97.3	restricted
-Glycine max Wm82 ISU-01 v2.1	724	JGI	JGI		98.3	97.7	restricted
-Glycine soja v1.1	509	JGI	JGI	soybean	98.3	96.7	unrestricted
-Gossypium barbadense v1.1	526	HA	HA	cotton	99.0	98.2	unrestricted
-Gossypium darwinii v1.1	529	HA	HA	cotton	99.3	98.5	unrestricted
-Gossypium hirsutum v3.1	578	HA	HA	upland cotton	99.0	98.6	restricted
-Gossypium hirsutum v1.1	458	HA	HA	upland cotton	99.0	97.8	unrestricted
-Gossypium hirsutum v2.1	527	HA	HA	upland cotton	99.0	98.5	unrestricted
-Gossypium hirsutum CSX8308 v1.1	570	HA	HA	upland cotton	98.7	98.5	restricted
-Gossypium hirsutum Coker v1.1	698	HA	HA	upland cotton	99.0	98.7	restricted
-Gossypium hirsutum DeltaPearl v1.1	695	HA	HA	upland cotton	99.3	98.7	restricted
-Gossypium hirsutum FM958 v1.1	697	HA	HA	upland cotton	99.0	98.5	restricted
-Gossypium hirsutum UA48 v1.1	569	HA	HA	upland cotton	99.0	98.3	restricted
-Gossypium hirsutum UGA230 v1.1	568	HA	HA	upland cotton	99.0	98.5	restricted
-Gossypium mustelinum v1.1	524	HA	HA	cotton	99.3	98.3	unrestricted
-Gossypium raimondii v2.1	221	JGI	JGI	cotton	98.3	97.4	unrestricted
-Gossypium tomentosum v1.1	525	HA	HA	cotton	99.3	98.6	unrestricted
-Helianthus annuus r1.2	494	INRA	INRA	sunflower	98.0	93.6	unrestricted
-Hordeum vulgare r1	462	IBSC	IBSC	barley	91.4	84.7	unrestricted
-Hordeum vulgare Morex V3	702	HA	IPK	Morex barley	92.1	94.7	unrestricted
-Hydrangea quercifolia v1.1	589	HA	HA	oakleaf hydrangea	96.7	93.1	restricted
-Iberis amara v1.1	485	JGI	JGI	rocket candytuft	98.3	96.5	restricted
-Isatis tinctoria v1.1	475	JGI	JGI	dyer's woad	98.0	96.8	restricted
-Joinvillea ascendens v1.1	587	JGI	JGI	`ohe	98.0	98.0	restricted
-Kalanchoe fedtschenkoi v1.1	382	JGI	JGI	lavender scallops	96.7	89.5	unrestricted
-Kalanchoe laxiflora v1.1	309	JGI	JGI		98.7	94.3	restricted
-Kalanchoe laxiflora FTBG2000359A v3.1	699	JGI	JGI		98.7	93.2	restricted
-Lactuca sativa V8	467	UCDavis	UCDavis	lettuce	96.4	92.5	unrestricted
-Lens culinaris v1	718	USask	USask		97.4	92.4	unrestricted
-Lens ervoides v1	719	USask	USask		96.7	92.7	unrestricted
-Lepidium sativum v1.1	478	JGI	JGI	garden cress	98.7	98.0	restricted
-Lindenbergia philippensis v1.1	689	JGI	JGI		98.0	96.7	restricted
-Linum usitatissimum v1.0	200	BGI	BGI	flax	97.7	92.1	unrestricted
-Liriodendron tulipifera YP108A v1.1	766	JGI	JGI		98.0	98.3	restricted
-Liriodendron tulipifera YP108A alt v1.1	767	JGI	JGI		95.7	94.7	restricted
-Lotus japonicus Lj1.0v1	571	HAU	HAU		98.3	96.7	unrestricted
-Lunaria annua v1.1	476	JGI	JGI	annual honesty	98.7	97.6	restricted
-Lupinus albus v1	567	PeretLab	PeretLab	white lupine	97.0	95.0	unrestricted
-Malcolmia maritima v1.1	477	JGI	JGI	Virginia stock	99.0	98.8	restricted
-Malus domestica v1.1	491	AGE	AGE	apple	96.4	96.8	restricted
-Manihot esculenta v6.1	305	JGI	JGI	cassava	97.7	95.3	unrestricted
-Manihot esculenta v7.1	520	UC Berkeley	JGI	cassava	98.7	98.1	unrestricted
-Manihot esculenta v8.1	671	UCB	JGI	cassava	98.3	98.7	unrestricted
-Marchantia polymorpha v3.1	320	JGI	JGI	common liverwort	97.7	66.7	unrestricted
-Medicago truncatula Mt4.0v1	285	JCVI	JCVI	barrel medic	97.4	93.6	unrestricted
-Micromonas pusilla CCMP1545 v3.0	228	JGI.FG	JGI.FG		87.5	21.1	unrestricted
-Micromonas sp RCC299 v3.0	229	JGI.FG	JGI.FG		89.8	22.7	unrestricted
-Mimulus guttatus v2.0	256	JGI	JGI	monkey flower	98.0	94.5	unrestricted
-Mimulus guttatus NONTOL v3.1	506	JGI	JGI	monkey flower	91.9	91.9	restricted
-Mimulus guttatus NONTOL v4.0	553	JGI	JGI	monkey flower	95.7	91.8	restricted
-Mimulus guttatus TOL v3.1	505	JGI	JGI	monkey flower	93.0	93.0	restricted
-Mimulus guttatus TOL v5.0	551	JGI	JGI	monkey flower	96.0	93.1	restricted
-Miscanthus sinensis v7.1	497	JGI	JGI	Chinese silver grass	97.7	97.6	unrestricted
-Musa acuminata v1	304	Genoscope/Cirad	Genoscope/Cirad	banana	94.1	86.7	unrestricted
-Myagrum perfoliatum v1.1	471	JGI	JGI	bird's eye cress	98.3	97.6	restricted
-Myagrum perfoliatum v2.1	583	JGI	JGI	bird's eye cress	98.0	98.5	restricted
-Nymphaea colorata v1.2	566	Zhejiang University	JGI	water lilly	98.0	92.5	unrestricted
-Olea europaea v1.0	451	OGC	OGC	olive	90.1	79.9	unrestricted
-Oropetium thomaeum v1.0	386	DanforthCenter	DanforthCenter		84.2	70.2	unrestricted
-Oryza sativa v7.0	323	MSU	MSU	rice	95.0	95.6	unrestricted
-Oryza sativa Kitaake v3.1	499	JGI	JGI	rice	98.3	99.1	unrestricted
-Ostreococcus lucimarinus v2.0	231	JGI.FG	JGI.FG		84.5	20.2	unrestricted
-Panicum hallii v2.0	308	JGI	JGI	Hall's panicgrass	97.4	96.6	unrestricted
-Panicum hallii v3.1	495	JGI	JGI	Hall's panicgrass	97.4	98.1	unrestricted
-Panicum hallii v3.2	590	JGI	JGI	Hall's panicgrass	97.7	99.3	unrestricted
-Panicum hallii HAL v2.1	496	JGI	JGI	Hall's panicgrass	98.0	98.9	unrestricted
-Panicum hallii HAL v2.2	591	JGI	JGI	Hall's panicgrass	98.3	99.2	unrestricted
-Panicum virgatum v4.1	450	JGI	JGI	switchgrass	98.0	98.3	unrestricted
-Panicum virgatum v5.1	516	JGI	JGI	switchgrass	98.3	99.4	unrestricted
-Panicum virgatum var. AP13 HAP1 v6.1	772	JGI	JGI	switchgrass	98.3	99.7	restricted
-Panicum virgatum var. AP13 HAP2 v6.1	773	JGI	JGI	switchgrass	98.3	99.7	restricted
-Paspalum vaginatum v3.1	672	HA	JGI	biscuit grass	97.7	98.9	unrestricted
-Pharus latifolius v1.1	680	JGI	JGI		98.0	98.4	restricted
-Phaseolus acutifolius v1.0	580	HA	MSU	tepary bean	97.4	94.7	unrestricted
-Phaseolus acutifolius WLD v2.0	581	U Saskatchewan	MSU	tepary bean	96.4	95.8	unrestricted
-Phaseolus lunatus V1	563	Uniandes	Uniandes	lima bean	96.4	93.7	restricted
-Phaseolus vulgaris v2.1	442	JGI	JGI	common bean	97.7	96.4	restricted
-Phaseolus vulgaris 5-593 v1.1	696	HA	HA	common bean	98.3	96.0	restricted
-Phaseolus vulgaris Labor Ovalle v1.1	670	HA	HA	common bean	98.3	95.1	restricted
-Phaseolus vulgaris UI111 v1.1	534	JGI	JGI	common bean	98.3	96.9	restricted
-Physcomitrium patens v3.3	318	COSMOSS	COSMOSS	moss Physcomitrella patens	98.7	67.9	unrestricted
-Poncirus trifoliata v1.3.1	565	JGI	JGI	hardy orange	97.4	97.2	unrestricted
-Populus deltoides WV94 v2.1	445	JGI	JGI	eastern cottonwood	97.7	94.4	restricted
-Populus nigra x maximowiczii NM6 v1.1	688	JGI	JGI	hybrid poplar	99.3	98.6	restricted
-Populus tremula x Populus alba HAP1 v5.1	717	JGI	JGI		98.7	98.7	restricted
-Populus tremula x Populus alba HAP2 v5.1	716	JGI	JGI		98.3	98.7	restricted
-Populus trichocarpa v3.0	210	JGI	JGI	western poplar	98.7	97.6	unrestricted
-Populus trichocarpa v3.1	444	JGI	JGI	western poplar	98.7	97.4	unrestricted
-Populus trichocarpa v4.1	533	JGI	JGI	western poplar	98.7	98.9	unrestricted
-Populus trichocarpa Stettler14 v1.1	532	JGI	JGI	western poplar	98.3	98.3	restricted
-Porphyra umbilicalis v1.5	456	JGI	JGI	seaweed	62.0	7.7	unrestricted
-Portulaca amilis v1.0	692	Yale	Yale	Paraguayan purslane	93.7	89.0	unrestricted
-Prunus persica v2.1	298	JGI	JGI	peach	99.0	98.8	unrestricted
-Quercus rubra v2.1	687	HA	HA	northern red oak	97.7	97.4	restricted
-Ricinus communis v0.1	119	TIGR	TIGR	castor bean plant	94.1	90.0	unrestricted
-Rorippa islandica v1.1	473	JGI	JGI	marsh cress	98.7	98.1	restricted
-Saccharum officinarum x spontaneum R570 v2.1	771	JGI	JGI	sugarcane	98.7	99.8	restricted
-Salix purpurea v1.0	289	JGI/JCVI	JGI/JCVI	shrub willow	98.7	97.0	unrestricted
-Salix purpurea v5.1	519	JGI	JGI	shrub willow	98.3	97.0	unrestricted
-Salix purpurea Fish Creek v3.1	518	JGI	JGI	shrub willow	98.3	97.2	unrestricted
-Schrenkiella parvula v2.2	574	LSU	LSU		98.3	96.8	unrestricted
-Selaginella moellendorffii v1.0	91	JGI	JGI	spikemoss	95.7	61.8	unrestricted
-Setaria italica v2.2	312	JGI	JGI	foxtail millet	98.7	98.4	unrestricted
-Setaria viridis v1.1	311	JGI	JGI	green foxtail millet	97.7	97.5	unrestricted
-Setaria viridis v2.1	500	JGI	JGI	green foxtail millet	98.0	99.0	unrestricted
-Setaria viridis v4.1	726	JGI	JGI	green foxtail millet	98.3	99.2	restricted
-Sinapis alba v1.1	480	JGI	JGI	white mustard	98.0	98.2	restricted
-Sinapis alba v3.1	584	JGI	JGI	white mustard	98.3	98.1	restricted
-Solanum lycopersicum ITAG2.4	390	iTAG	iTAG	tomato	96.3	95.5	unrestricted
-Solanum lycopersicum ITAG3.2	514	iTAG	ITAG	tomato	95.7	94.5	unrestricted
-Solanum lycopersicum ITAG4.0	691	SGN	ITAG	tomato	95.4	93.6	unrestricted
-Solanum lycopersicum ITAG5.0	796	AGI_CAAS_Shenzhen	AGI_CAAS_Shenzhen	tomato	94.7	93.5	restricted
-Solanum tuberosum v4.03	448	MSU	MSU	potato	87.1	85.5	unrestricted
-Solanum tuberosum v6.1	686	MSU	MSU	potato	98.0	95.1	unrestricted
-Sorghum bicolor v3.1.1	454	JGI	JGI	sorghum	98.3	98.3	unrestricted
-Sorghum bicolor v5.1	730	JGI	JGI	sorghum	98.7	99.7	restricted
-Sorghum bicolor BTx642 v1.1	564	JGI	JGI	sorghum	98.3	99.4	restricted
-Sorghum bicolor RTx430 v2.1	552	JGI	JGI	sorghum	98.0	99.2	restricted
-Sorghum bicolor Rio v2.1	468	JGI	JGI	sorghum	97.0	97.0	unrestricted
-Sorghum bicolor SC187 v1.1	694	JGI	JGI	sorghum	98.0	99.4	restricted
-Sorghum bicolor Wray v1.1	781	JGI	JGI		98.0	99.4	restricted
-Sphagnum fallax v0.5	310	JGI	JGI	flat-topped bogmoss	98.0	71.1	unrestricted
-Sphagnum fallax v1.1	522	JGI	JGI	flat-topped bogmoss	98.3	71.3	unrestricted
-Sphagnum magellanicum v1.1	521	JGI	JGI	magellanic bogmoss Sphagnum divinum	98.3	71.6	unrestricted
-Spinacia oleracea Spov3	575	UCD	UCD	spinach	92.7	83.9	unrestricted
-Spirodela polyrhiza v2	290	MIPS	MIPS	greater duckweed	92.7	79.6	unrestricted
-Stanleya pinnata v1.1	486	JGI	JGI	Princes Plume	82.8	71.2	restricted
-Theobroma cacao v1.1	233	CGD	CGD	cocoa bean	98.3	97.7	unrestricted
-Theobroma cacao v2.1	523	JGI	JGI	cocoa bean	98.3	98.1	restricted
-Thinopyrum intermedium v2.1	503	JGI	JGI	intermediate wheatgrass	97.7	97.4	restricted
-Thinopyrum intermedium v3.1	770	JGI	JGI	intermediate wheatgrass	98.3	99.7	unrestricted
-Thlaspi arvense v1.1	479	JGI	JGI	field pennycress	98.3	97.5	restricted
-Thuja plicata v3.1	572	UBC	JGI	western red-cedar	96.7	84.5	unrestricted
-Trifolium pratense v2	385	TGAC	TGAC	red clover	93.4	87.6	unrestricted
-Triticum aestivum v2.2	296	MIPS	MIPS	wheat	92.7	89.4	unrestricted
-Triticum aestivum cv. Chinese Spring v2.1	725	IWGSC	IWGSC	bread wheat	98.0	99.3	unrestriced
-Urochloa fusca v1.1	669	JGI	JGI		95.4	97.2	restricted
-Vaccinium darrowii v1.2	700	USDA/UTK	USDA/UTK	evergreen blueberry	93.7	90.1	unrestricted
-Vigna unguiculata v1.1	469	UCR	JGI	cowpea	98.3	95.9	unrestricted
-Vigna unguiculata v1.2	540	UCR	JGI	cowpea	98.7	96.0	restricted
-Vigna unguiculata CB5-2 v1.1	545	UCR	JGI	cowpea	97.7	95.1	restricted
-Vigna unguiculata Sanzi v1.1	544	UCR	JGI	cowpea	96.7	95.6	restricted
-Vigna unguiculata Suvita2 v1.1	541	UCR	JGI	cowpea	98.0	95.3	restricted
-Vigna unguiculata TZ30 v1.2	558	UCR	JGI	cowpea	97.7	95.3	restricted
-Vigna unguiculata UCR779 v1.1	543	UCR	JGI	cowpea	97.0	95.6	restricted
-Vigna unguiculata ZN016 v1.2	559	UCR	JGI	cowpea	96.7	95.6	restricted
-Vitis vinifera v2.1	457	CRIBI	CRIBI	grape vine	97.7	95.8	unrestricted
-Volvox carteri v2.1	317	JGI	JGI	volvox	94.7	26.0	unrestricted
-Zea mays RefGen_V4	493	CSHL	CSHL	maize	97.7	95.2	unrestricted
-Zea mays Zm-B73-REFERENCE-NAM-5.0.55	833	MaizeGDB	nam-genomes	maize	97.0	96.4	unrestricted
-Zea mays B84 v1.2	681	JGI	JGI	maize	98.7	99.2	unrestricted
-Zea mays LH145 v1.2	682	JGI	JGI	maize	99.0	99.1	unrestricted
-Zea mays NKH8431 v1.2	683	JGI	JGI	maize	98.7	99.2	unrestricted
-Zea mays PH207 v1.1	443	UMN	UMN	maize	95.0	87.9	unrestricted
-Zea mays PHB47 v1.1	513	JGI	JGI	maize	97.7	97.4	unrestricted
-Zea mays PHB47 v1.2	684	JGI	JGI	maize	98.7	99.0	unrestricted
-Zea mays PHJ40 v1.1	512	JGI	JGI	maize	98.7	98.0	unrestricted
-Zea mays PHJ40 v1.2	685	JGI	JGI	maize	98.7	99.2	unrestricted
-Zostera marina v2.2	324	ORCAE	ORCAE	common eelgrass	97.0	85.0	unrestricted
-Zostera marina v3.1	668	JGI	Ghent	common eelgrass	96.0	85.3	unrestricted
diff --git a/strudel-demo-app/public/data/default/monitor-activities/experiment_detail.json b/strudel-demo-app/public/data/default/monitor-activities/experiment_detail.json
deleted file mode 100644
index f8625cd7..00000000
--- a/strudel-demo-app/public/data/default/monitor-activities/experiment_detail.json
+++ /dev/null
@@ -1,92 +0,0 @@
-{
-  "id": 45,
-  "experiment_name": "Experiment Cricket",
-  "start_time": "12/16/2023, 3:01:07 PM",
-  "end_time": null,
-  "status": "Running",
-  "events": [
-    {
-      "id": 10,
-      "event_type": "Chirp",
-      "event_time": "12/17/2023, 9:09:15 PM",
-      "confidence": 97
-    },
-    {
-      "id": 9,
-      "event_type": "Chirp",
-      "event_time": "12/17/2023, 8:11:19 PM",
-      "confidence": 98
-    },
-    {
-      "id": 8,
-      "event_type": "Hop",
-      "event_time": "12/17/2023, 4:33:53 PM",
-      "confidence": 64
-    },
-    {
-      "id": 7,
-      "event_type": "Hop",
-      "event_time": "12/17/2023, 11:09:15 AM",
-      "confidence": 97
-    },
-    {
-      "id": 6,
-      "event_type": "Hop",
-      "event_time": "12/17/2023, 11:02:05 AM",
-      "confidence": 99
-    },
-    {
-      "id": 5,
-      "event_type": "Hop",
-      "event_time": "12/17/2023, 7:23:32 AM",
-      "confidence": 75
-    },
-    {
-      "id": 4,
-      "event_type": "Chirp",
-      "event_time": "12/17/2023, 7:03:44 PM",
-      "confidence": 99
-    },
-    {
-      "id": 3,
-      "event_type": "Chirp",
-      "event_time": "12/17/2023, 3:26:42 PM",
-      "confidence": 97
-    },
-    {
-      "id": 2,
-      "event_type": "Chirp",
-      "event_time": "12/16/2023, 11:55:21 PM",
-      "confidence": 97
-    },
-    {
-      "id": 1,
-      "event_type": "Chirp",
-      "event_time": "12/16/2023, 10:08:10 PM",
-      "confidence": 98
-    },
-    {
-      "id": 0,
-      "event_type": "Chirp",
-      "event_time": "12/16/2023, 9:45:10 PM",
-      "confidence": 97
-    }
-  ],
-  "notes": [
-    {
-      "id": 2,
-      "created_time": "12/17/2023, 4:00:29 PM",
-      "content": "Aute amet aute irure in consectetur enim incididunt aute consectetur."
-    },
-    {
-      "id": 1,
-      "created_time": "12/17/2023, 7:00:25 AM",
-      "content": "Labore elit cupidatat eiusmod sunt aliquip pariatur quis."
-    },
-    {
-      "id": 0,
-      "created_time": "12/16/2023, 11:59:20 PM",
-      "content": "Culpa id officia laborum cupidatat dolor mollit."
-    }
-  ]
-}
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/monitor-activities/experiments.json b/strudel-demo-app/public/data/default/monitor-activities/experiments.json
deleted file mode 100644
index 649a52fb..00000000
--- a/strudel-demo-app/public/data/default/monitor-activities/experiments.json
+++ /dev/null
@@ -1,303 +0,0 @@
-[
-  {
-    "id": 45,
-    "experiment_name": "Experiment Cricket",
-    "start_time": "12/16/2023, 3:01:07 PM",
-    "end_time": null,
-    "status": "Running"
-  },
-  {
-    "id": 44,
-    "experiment_name": "Experiment Mouse",
-    "start_time": "12/15/2023, 1:40:10 PM",
-    "end_time": null,
-    "status": "Running"
-  },
-  {
-    "id": 8,
-    "experiment_name": "Experiment Fox",
-    "start_time": "6/27/2023, 1:42:11 PM",
-    "end_time": "6/30/2023, 5:15:29 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 14,
-    "experiment_name": "Experiment Elephant",
-    "start_time": "10/15/2023, 1:42:11 PM",
-    "end_time": "10/18/2023, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 30,
-    "experiment_name": "Experiment Hippo",
-    "start_time": "6/27/2023, 1:42:11 PM",
-    "end_time": "6/30/2023, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 43,
-    "experiment_name": "Experiment Cherry",
-    "start_time": "5/20/2023, 9:34:45 AM",
-    "end_time": "5/22/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 23,
-    "experiment_name": "Experiment Lime",
-    "start_time": "4/22/2023, 9:34:45 AM",
-    "end_time": "4/24/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 39,
-    "experiment_name": "Experiment Giraffe",
-    "start_time": "4/28/2023, 9:34:45 AM",
-    "end_time": "4/30/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 26,
-    "experiment_name": "Experiment Horse",
-    "start_time": "2/17/2023, 1:42:11 PM",
-    "end_time": "2/20/2023, 5:15:29 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 11,
-    "experiment_name": "Experiment Sloth",
-    "start_time": "1/14/2023, 9:34:45 AM",
-    "end_time": "1/16/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 35,
-    "experiment_name": "Experiment Gargoyle",
-    "start_time": "7/10/2023, 9:34:45 AM",
-    "end_time": "7/12/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 19,
-    "experiment_name": "Experiment Lion",
-    "start_time": "8/6/2023, 9:34:45 AM",
-    "end_time": "8/8/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 3,
-    "experiment_name": "Experiment Jasmine",
-    "start_time": "6/5/2023, 9:34:45 AM",
-    "end_time": "6/7/2023, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 6,
-    "experiment_name": "Experiment Crocodile",
-    "start_time": "9/20/2022, 1:42:11 PM",
-    "end_time": "9/23/2022, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 32,
-    "experiment_name": "Experiment Lemon",
-    "start_time": "9/6/2022, 11:27:18 AM",
-    "end_time": "9/8/2022, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 17,
-    "experiment_name": "Experiment Iceberg",
-    "start_time": "7/8/2022, 9:11:04 AM",
-    "end_time": "7/10/2022, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 41,
-    "experiment_name": "Experiment Diamond",
-    "start_time": "6/19/2022, 9:11:04 AM",
-    "end_time": "6/21/2022, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 21,
-    "experiment_name": "Experiment Ruby",
-    "start_time": "6/12/2022, 9:11:04 AM",
-    "end_time": "6/14/2022, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 27,
-    "experiment_name": "Experiment Pigeon",
-    "start_time": "5/29/2022, 11:27:18 AM",
-    "end_time": "5/31/2022, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 13,
-    "experiment_name": "Experiment Oregono",
-    "start_time": "5/9/2022, 9:11:04 AM",
-    "end_time": "5/11/2022, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 28,
-    "experiment_name": "Experiment Otter",
-    "start_time": "5/1/2022, 11:27:18 AM",
-    "end_time": "5/3/2022, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 0,
-    "experiment_name": "Experiment Gecko",
-    "start_time": "3/14/2022, 11:27:18 AM",
-    "end_time": "3/16/2022, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 25,
-    "experiment_name": "Experiment Banana",
-    "start_time": "3/5/2022, 9:11:04 AM",
-    "end_time": "3/7/2022, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 34,
-    "experiment_name": "Experiment Thunder",
-    "start_time": "12/7/2021, 1:42:11 PM",
-    "end_time": "12/10/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 15,
-    "experiment_name": "Experiment Tornado",
-    "start_time": "12/1/2021, 9:34:45 AM",
-    "end_time": "12/3/2021, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 9,
-    "experiment_name": "Experiment Wind",
-    "start_time": "11/13/2021, 1:42:11 PM",
-    "end_time": "11/16/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 29,
-    "experiment_name": "Experiment Pufferfish",
-    "start_time": "10/14/2021, 9:11:04 AM",
-    "end_time": "10/16/2021, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 42,
-    "experiment_name": "Experiment Strawberry",
-    "start_time": "9/26/2021, 1:42:11 PM",
-    "end_time": "9/29/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 22,
-    "experiment_name": "Experiment Grape",
-    "start_time": "9/19/2021, 1:42:11 PM",
-    "end_time": "9/22/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 10,
-    "experiment_name": "Experiment Lightning",
-    "start_time": "8/30/2021, 1:42:11 PM",
-    "end_time": "9/2/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 2,
-    "experiment_name": "Experiment Leopard",
-    "start_time": "8/22/2021, 1:42:11 PM",
-    "end_time": "8/25/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 38,
-    "experiment_name": "Experiment Donkey",
-    "start_time": "8/15/2021, 1:42:11 PM",
-    "end_time": "8/18/2021, 5:15:29 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 7,
-    "experiment_name": "Experiment Walrus",
-    "start_time": "5/15/2021, 9:34:45 AM",
-    "end_time": "5/17/2021, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 16,
-    "experiment_name": "Experiment Basil",
-    "start_time": "3/24/2020, 11:27:18 AM",
-    "end_time": "3/26/2020, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 18,
-    "experiment_name": "Experiment Guava",
-    "start_time": "11/28/2020, 9:11:04 AM",
-    "end_time": "11/30/2020, 11:27:39 AM",
-    "status": "Interrupted"
-  },
-  {
-    "id": 5,
-    "experiment_name": "Experiment Kiwi",
-    "start_time": "11/7/2020, 9:11:04 AM",
-    "end_time": "11/9/2020, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 12,
-    "experiment_name": "Experiment Shark",
-    "start_time": "6/25/2020, 11:27:18 AM",
-    "end_time": "6/27/2020, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 33,
-    "experiment_name": "Experiment Turtle",
-    "start_time": "5/2/2020, 9:11:04 AM",
-    "end_time": "5/4/2020, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 1,
-    "experiment_name": "Experiment Dragon",
-    "start_time": "4/28/2020, 9:11:04 AM",
-    "end_time": "4/30/2020, 11:27:39 AM",
-    "status": "Failed"
-  },
-  {
-    "id": 31,
-    "experiment_name": "Experiment Lizard",
-    "start_time": "2/20/2020, 9:34:45 AM",
-    "end_time": "2/22/2020, 2:46:01 PM",
-    "status": "Complete"
-  },
-  {
-    "id": 20,
-    "experiment_name": "Experiment Apple",
-    "start_time": "1/29/2020, 11:27:18 AM",
-    "end_time": "1/31/2020, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 36,
-    "experiment_name": "Experiment Avocado",
-    "start_time": "1/15/2020, 11:27:18 AM",
-    "end_time": "1/17/2020, 3:48:52 AM",
-    "status": "Complete"
-  },
-  {
-    "id": 4,
-    "experiment_name": "Experiment Watermelon",
-    "start_time": "1/11/2020, 11:27:18 AM",
-    "end_time": "1/13/2020, 3:48:52 AM",
-    "status": "Complete"
-  }
-]
diff --git a/strudel-demo-app/public/data/default/run-computation/inputs.json b/strudel-demo-app/public/data/default/run-computation/inputs.json
deleted file mode 100644
index 1aa06d36..00000000
--- a/strudel-demo-app/public/data/default/run-computation/inputs.json
+++ /dev/null
@@ -1,42 +0,0 @@
-[
-  {
-    "id": 0,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 1,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 2,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 3,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 4,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/run-computation/list.json b/strudel-demo-app/public/data/default/run-computation/list.json
deleted file mode 100644
index 246fa9cc..00000000
--- a/strudel-demo-app/public/data/default/run-computation/list.json
+++ /dev/null
@@ -1,37 +0,0 @@
-[
-  {
-    "id": 0,
-    "name": "Scenario 1",
-    "analysisType": "Sensitivity Analysis",
-    "createdDate": "05/24/2023",
-    "status": "Running"
-  },
-  {
-    "id": 1,
-    "name": "Scenario 2",
-    "analysisType": "Sensitivity Analysis",
-    "createdDate": "05/24/2023",
-    "status": "Draft"
-  },
-  {
-    "id": 2,
-    "name": "Scenario 3",
-    "analysisType": "Optimization",
-    "createdDate": "05/24/2023",
-    "status": "Completed"
-  },
-  {
-    "id": 3,
-    "name": "Scenario 4",
-    "analysisType": "Optimization",
-    "createdDate": "05/23/2023",
-    "status": "Completed"
-  },
-  {
-    "id": 4,
-    "name": "Scenario 5",
-    "analysisType": "Optimization",
-    "createdDate": "05/23/2023",
-    "status": "Completed"
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/run-computation/results_bar_chart.json b/strudel-demo-app/public/data/default/run-computation/results_bar_chart.json
deleted file mode 100644
index 743c8e93..00000000
--- a/strudel-demo-app/public/data/default/run-computation/results_bar_chart.json
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-  {
-    "x": ["Category 1", "Category 2", "Category 3"],
-    "y": [20, 14, 23],
-    "name": "Group 1",
-    "type": "bar"
-  },
-  {
-    "x": ["Category 1", "Category 2", "Category 3"],
-    "y": [12, 18, 29],
-    "name": "Group 2",
-    "type": "bar"
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/run-computation/results_line_chart.json b/strudel-demo-app/public/data/default/run-computation/results_line_chart.json
deleted file mode 100644
index 2e72bae1..00000000
--- a/strudel-demo-app/public/data/default/run-computation/results_line_chart.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
-  {
-    "x": [1, 2, 3, 4],
-    "y": [10, 15, 13, 17],
-    "type": "scatter"
-  },
-  {
-    "x": [1, 2, 3, 4],
-    "y": [16, 5, 11, 9],
-    "type": "scatter"
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/run-computation/results_table.json b/strudel-demo-app/public/data/default/run-computation/results_table.json
deleted file mode 100644
index 1aa06d36..00000000
--- a/strudel-demo-app/public/data/default/run-computation/results_table.json
+++ /dev/null
@@ -1,42 +0,0 @@
-[
-  {
-    "id": 0,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 1,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 2,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 3,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  },
-  {
-    "id": 4,
-    "name": "value",
-    "unitType": "value",
-    "lowerBound": 0,
-    "upperBound": 1,
-    "constraints": "value"
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/public/data/default/search-data-repositories/datasets.json b/strudel-demo-app/public/data/default/search-data-repositories/datasets.json
deleted file mode 100644
index a74ca7bf..00000000
--- a/strudel-demo-app/public/data/default/search-data-repositories/datasets.json
+++ /dev/null
@@ -1,933 +0,0 @@
-[
-  {
-    "id": 0,
-    "title": "Data from Decadal Change in Groundwater Quality Web Site (1988 - 2012)",
-    "publication_date": "2018-02-02",
-    "start_date": "1988-01-01",
-    "end_date": "2010-12-31",
-    "citation": "Lindsey, B.D., Johnson, T.D., and Belitz, Kenneth, 2018, Data from Decadal Change in Groundwater Quality Web Site, 1988-2012: U.S. Geological Survey data release, https://doi.org/10.5066/F7PC30WD.",
-    "doi": "10.5066/F7PC30WD",
-    "summary": "Groundwater quality data were collected in 5,000 wells between 1988-2001 by the National Water-Quality Assessment Project. About 1,500 of these wells were sampled again between 2002-2012 to evaluate decadal changes in groundwater quality. Monitoring wells, domestic supply wells, and some public supply wells were included in this study.",
-    "tags": [ "NAWQA", "National Water Quality Program", "USGS Science Data Catalog (SDC)", "Cycle 3", "groundwater quality", "trends", "water quality"],
-    "category": "Groundwater",
-    "purpose": "Data were collected as a part of the NAWQA Land use studies or Major aquifer studies. Initially data were collected to characterize the quality of shallow and medium depths of groundwater in the nation. Subsequent samples were collected to evaluate changes in concentrations over a decadal time span. Mapping criteria and benchmarks: The NAWQA Project has developed an interactive mapping tool that displays decadal changes in concentrations.",
-    "point_of_contact": "Bruce  D. Lindsey",
-    "originator": "Bruce  D. Lindsey, Tyler D Johnson",
-    "metadata_contact": "Jennifer B Sharpe",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "USGS Data Release Products",
-        "url": "#"
-      },
-      {
-        "title": "National Water Quality Assessment Project",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 1,
-    "title": "Fire perimeter data for the 1987 Black Dragon Fire in China (1987)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Geospatial data were developed to characterize pre-fire biomass, burn severity, and biomass consumed for the Black Dragon Fire that burned in northern China in 1987. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Fires",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 2,
-    "title": "Flood Inundation Extent and Depth in Selected Areas of Southern U.S. (March 2016)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Heavy rainfall occurred across Louisiana, Texas, Arkansas, and Mississippi in March 2016 as a result of a slow-moving southward dip in the jetstream , funneling tropical moisture into Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Louisiana", "Mississippi", "Texas", "Floods", "USGS Data Catalog (SDC)"],
-    "category": "Floods",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 3,
-    "title": "Bathymetric survey of the Brandon Road Dam Spillway, Joliet, Illinois  (May 2015)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Bathymetric survey data of the Brandon Road Dam spillway was collected on May 27 and May 28, 2015 by the US Geological Survey (USGS) using Trimble Real-Time Kinematic Global Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Dam", "GPS measurement", "Brandon Road Lock and Dam", "Floods", "USGS Data Catalog (SDC)"],
-    "category": "Groundwater",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 4,
-    "title": "Data from mercury intrusion porosimetry conducted on samples of a mudstone underlying the Naval Air Warfare Center, West Trenton, New Jersey",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "This data set presents the results of conducting Mercury Intrusion Porosimetry on core samples of the mudstone underlying the Naval Air Warfare Center, West Trenton, New Jersey Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Sedimentary rock", "porosity", "West Trenton",  "USGS Data Catalog (SDC)"],
-    "category": "Groundwater",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 5,
-    "title": "Discovery of Two Biological Mechanisms for Acetylene Metabolism in a Single Organism",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Acetylene fermentation assays, nitrogen fixation assays, and growth studies were performed with Pelobacter sp. strain SFB93 and Pelobacter acetylenicus DSM3246. Data includes concentrations of acetylene and ethylene over time, and growth measured with OD680 and cell counts.",
-    "tags": ["Genetics", "Ecology", "Environmental health", "Geo Chemistry",  "USGS Data Catalog (SDC)"],
-    "category": "Biology",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 6,
-    "title": "Annual California Sea Otter Census - Spring Census Summary (Apr - Jul 2017)",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "The spring 2017 mainland sea otter count began on April 30, and although the shore-based counts were completed by May 12, 2017, the aerial counts were not completed until July 12. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["California", "Sea Otters", "Census", "Coastal", "USGS Data Catalog (SDC)"],
-    "category": "Biology",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 7,
-    "title": "Ullamco nulla eiusmod tempor nisi aute",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Earthquakes",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 8,
-    "title": "Sint aliqua ut ut laboris eiusmod incididunt duis laboris anim",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Fires",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 9,
-    "title": "Magna ea aliqua et id laboris incididunt pariatur veniam",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Fires",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 10,
-    "title": "Anim ea laborum elit ad fugiat deserunt fugiat veniam cillum id",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Groundwater",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 11,
-    "title": "Consectetur proident sint adipisicing velit aliquip laborum nostrud non",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Floods",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 12,
-    "title": "Id duis ut ullamco amet occaecat duis fugiat nostrud laboris ipsum esse cupidatat ad",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Biology",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 13,
-    "title": "Non magna officia voluptate proident sunt occaecat dolore eiusmod duis deserunt",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Earthquakes",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 14,
-    "title": "Fugiat in Lorem fugiat sunt et sit quis elit amet cillum reprehenderit aliqua occaecat do",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Fires",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 15,
-    "title": "Tempor adipisicing incididunt laboris sit dolore commodo ea qui deserunt nostrud veniam eu est",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Floods",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 16,
-    "title": "Et adipisicing laboris mollit officia non esse consequat in commodo do Lorem",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Earthquakes",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 17,
-    "title": "Veniam ut exercitation proident eiusmod enim ipsum culpa",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Groundwater",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  },
-  {
-    "id": 18,
-    "title": "Cupidatat pariatur commodo sint qui in minim aliquip anim cillum eu sunt amet do aliqua",
-    "publication_date": "2023-01-01",
-    "start_date": "2010-01-01",
-    "end_date": "2021-01-01",
-    "citation": "Labore proident do aute et esse adipisicing veniam eiusmod culpa pariatur sunt officia.",
-    "doi": "0000000000",
-    "summary": "Est deserunt elit aliquip labore duis nostrud mollit sunt proident cillum. Mollit nisi sint laboris esse labore velit laboris. Ipsum id laborum et dolor laboris ut sint officia voluptate. Ipsum consectetur fugiat laboris sit Lorem duis minim fugiat.",
-    "tags": ["Boreal forest", "Carbon and greenhouse gas emissions", "China", "Ecology", "Forestry"],
-    "category": "Fires",
-    "purpose": "Nostrud ullamco ea id consequat ex. Tempor deserunt laborum amet ex quis culpa laborum Lorem Lorem sunt do sit excepteur. Sunt ea velit non magna ut Lorem magna eu aute aliquip.",
-    "point_of_contact": "Ernest O. Lawrence",
-    "originator": "Darleane C. Hoffman",
-    "metadata_contact": "Glenn T. Seaborg",
-    "publisher": "U.S Geological Survey",
-    "distributor": "U.S Geological Survey - ScienceBase",
-    "usgs_mission_area": "Water Resources",
-    "communities": [
-      {
-        "title": "Veniam fugiat commodo esse ex sunt sit enim.",
-        "url": "#"
-      },
-      {
-        "title": "Duis amet voluptate adipisicing irure officia excepteur consectetur.",
-        "url": "#"
-      }
-    ],
-    "associated_projects": [
-      {
-        "title": "Data from Decadal Change in Groundwater Quality Web Site, 1988-2016",
-        "url": "#"
-      }
-    ],
-    "attached_files": [
-      {
-        "file_id": 0,
-        "file_name": "Laborum-velit-in-minim.zip",
-        "file_size": "42.7 MB",
-        "description": "Ad qui Lorem ea voluptate"
-      },
-      {
-        "file_id": 1,
-        "file_name": "Duis-qui-consectetur.xml",
-        "file_size": "8 KB",
-        "description": "Adipisicing deserunt anim reprehenderit"
-      }
-    ]
-  }
-]
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/compare-data/_config/definitions.json b/strudel-demo-app/src/pages/compare-data/_config/definitions.json
deleted file mode 100644
index b6e9e0ed..00000000
--- a/strudel-demo-app/src/pages/compare-data/_config/definitions.json
+++ /dev/null
@@ -1,83 +0,0 @@
-{
-  "columns": {
-    "list": {
-      "table": [
-        { 
-          "field": "name", 
-          "headerName": "Scenario Name", 
-          "width": 200 
-        },
-        { 
-          "field": "description", 
-          "headerName": "Description", 
-          "width": 200 
-        },
-        { 
-          "field": "analysis_type", 
-          "headerName": "Analysis Type", 
-          "width": 200
-        },
-        { 
-          "field": "volumetric_flow_rate", 
-          "headerName": "Volumetric Flow Rate", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "tss_concentration", 
-          "headerName": "TSS Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "cod_concentration", 
-          "headerName": "COD Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "tkn_concentration", 
-          "headerName": "TKN Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "acetic_acid_concentration", 
-          "headerName": "Acetic Acid Concentration", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric6", 
-          "headerName": "metric6", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric7", 
-          "headerName": "metric7", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric8", 
-          "headerName": "metric8", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric9", 
-          "headerName": "metric9", 
-          "width": 200,
-          "isComparisonMetric": true
-        },
-        { 
-          "field": "metric10", 
-          "headerName": "metric10", 
-          "width": 200,
-          "isComparisonMetric": true
-        }
-      ]
-    }
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/contribute-data/_config/definitions.json b/strudel-demo-app/src/pages/contribute-data/_config/definitions.json
deleted file mode 100644
index 85886ed4..00000000
--- a/strudel-demo-app/src/pages/contribute-data/_config/definitions.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "columns": {
-    "portal": {
-      "table": [
-        { 
-          "field": "title", 
-          "headerName": "Dataset Title", 
-          "width": 200 
-        },
-        { 
-          "field": "category", 
-          "headerName": "Category", 
-          "width": 200 
-        },
-        { 
-          "field": "summary", 
-          "headerName": "Summary", 
-          "width": 200 
-        },
-        { 
-          "field": "doi", 
-          "headerName": "DOI", 
-          "width": 200 
-        },
-        { 
-          "field": "publication_date", 
-          "headerName": "Created Date", 
-          "width": 200 
-        },
-        { 
-          "field": "status", 
-          "headerName": "Status", 
-          "width": 200 
-        }
-      ]
-    }
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/explore-data/_config/definitions.json b/strudel-demo-app/src/pages/explore-data/_config/definitions.json
deleted file mode 100644
index 84b140d2..00000000
--- a/strudel-demo-app/src/pages/explore-data/_config/definitions.json
+++ /dev/null
@@ -1,100 +0,0 @@
-{
-  "columns": {
-    "main": {
-      "table": [
-        {
-          "field":"Organism",
-          "headerName":"Organism",
-          "width":200
-        },
-        {
-          "field":"Common Name",
-          "headerName":"Common Name",
-          "width":200
-        },
-        {
-          "field":"Assembly",
-          "headerName":"Assembly",
-          "width":150
-        },
-        {
-          "field":"Data Usage Policy",
-          "headerName":"Data Usage Policy",
-          "width":220
-        },
-        {
-          "field":"Euk. BUSCO %",
-          "headerName":"Euk. BUSCO %",
-          "type":"number",
-          "width":200
-        },
-        {
-          "field":"Emb. BUSCO %",
-          "headerName":"Emb. BUSCO %",
-          "type":"number",
-          "width":200
-        }
-      ]
-    }
-  },
-  "filters": {
-    "main": [
-      {
-        "field": "Assembly",
-        "displayName":"Assembly",
-        "filterType":"CheckboxList",
-        "props":{
-          "options":[
-            {
-              "label":"JGI",
-              "value":"JGI"
-            },
-            {
-              "label":"BYU",
-              "value":"BYU"
-            },
-            {
-              "label":"AGP",
-              "value":"AGP"
-            }
-          ]
-        }
-      },
-      {
-        "field":"Data Usage Policy",
-        "displayName":"Data Usage Policy",
-        "filterType":"CheckboxList",
-        "props":{
-          "options":[
-            {
-              "label":"restricted",
-              "value":"restricted"
-            },
-            {
-              "label":"unrestricted",
-              "value":"unrestricted"
-            }
-          ]
-        }
-      },
-      {
-        "field":"Euk. BUSCO %",
-        "displayName":"Euk. BUSCO %",
-        "filterType":"Slider",
-        "props":{
-          "min":0,
-          "max":100
-        }
-      },
-      {
-        "field":"Emb. BUSCO %",
-        "displayName":"Emb. BUSCO %",
-        "filterType":"Slider",
-        "props":{
-          "min":0,
-          "max":100
-        }
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/run-computation/_config/definitions.json b/strudel-demo-app/src/pages/run-computation/_config/definitions.json
deleted file mode 100644
index c3a882ef..00000000
--- a/strudel-demo-app/src/pages/run-computation/_config/definitions.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
-  "list": {
-    "table": {
-      "columns": [
-        { 
-          "field": "name", 
-          "headerName": "Scenario Name", 
-          "width": 200 
-        },
-        { 
-          "field": "analysisType", 
-          "headerName": "Analysis Type", 
-          "width": 200 
-        },
-        { 
-          "field": "createdDate", 
-          "headerName": "Date Created", 
-          "width": 200 
-        },
-        { 
-          "field": "status", 
-          "headerName": "Status", 
-          "width": 200 
-        }
-      ]
-    }
-  },
-  "inputs": {
-    "table": {
-      "columns": [
-        { 
-          "field": "name", 
-          "headerName": "Unit Name", 
-          "width": 200 
-        },
-        { 
-          "field": "unitType", 
-          "headerName": "Unit Type", 
-          "width": 200 
-        },
-        { 
-          "field": "constraints", 
-          "headerName": "Constraints", 
-          "width": 200
-        },
-        { 
-          "field": "lowerBound", 
-          "headerName": "Lower Bound", 
-          "width": 200,
-          "type": "number"
-        },
-        { 
-          "field": "upperBound", 
-          "headerName": "Upper Bound", 
-          "width": 200,
-          "type": "number"
-        }
-      ]
-    }
-  },
-  "results": {
-    "table": {
-      "columns": [
-        { 
-          "field": "name", 
-          "headerName": "Unit Name", 
-          "width": 200 
-        },
-        { 
-          "field": "unitType", 
-          "headerName": "Unit Type", 
-          "width": 200 
-        },
-        { 
-          "field": "constraints", 
-          "headerName": "Constraints", 
-          "width": 200
-        },
-        { 
-          "field": "lowerBound", 
-          "headerName": "Lower Bound", 
-          "width": 200,
-          "type": "number"
-        },
-        { 
-          "field": "upperBound", 
-          "headerName": "Upper Bound", 
-          "width": 200,
-          "type": "number"
-        }
-      ]
-    }
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_config/definitions.json b/strudel-demo-app/src/pages/search-data-repositories/_config/definitions.json
deleted file mode 100644
index 0a3dea6b..00000000
--- a/strudel-demo-app/src/pages/search-data-repositories/_config/definitions.json
+++ /dev/null
@@ -1,70 +0,0 @@
-{
-  "columns": {
-    "main": {
-      "card": {
-        "title": "title",
-        "content": "summary",
-        "tags": "tags"
-      }
-    }
-  },
-  "filters": {
-    "main": [
-      {
-        "field": "category",
-        "displayName": "Category",
-        "filterType": "CheckboxList",
-        "props": {
-          "options": [
-            {
-              "label": "Fires",
-              "value": "Fires"
-            },
-            {
-              "label": "Floods",
-              "value": "Floods"
-            },
-            {
-              "label": "Earthquakes",
-              "value": "Earthquakes"
-            },
-            {
-              "label": "Groundwater",
-              "value": "Groundwater"
-            },
-            {
-              "label": "Biology",
-              "value": "Biology"
-            }
-          ]
-        }
-      },
-      {
-        "field": "tags",
-        "displayName": "Tags",
-        "filterType": "CheckboxList",
-        "props": {
-          "options": [
-            {
-              "label": "Boreal forest",
-              "value": "Boreal forest"
-            },
-            {
-              "label": "Carbon and greenhouse gas emissions",
-              "value": "Carbon and greenhouse gas emissions"
-            },
-            {
-              "label": "Ecology",
-              "value": "Ecology"
-            }
-          ]
-        }
-      },
-      {
-        "field": "publication_date",
-        "displayName": "Publication Date",
-        "filterType": "date range"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/router.ts b/strudel-demo-app/src/router.ts
deleted file mode 100644
index 588c0466..00000000
--- a/strudel-demo-app/src/router.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-// Generouted, changes to this file will be overriden
-/* eslint-disable */
-
-import { components, hooks, utils } from '@generouted/react-router/client'
-
-export type Path =
-  | `/`
-  | `/compare-data`
-  | `/compare-data/compare`
-  | `/compare-data/new`
-  | `/contribute-data`
-  | `/contribute-data/new`
-  | `/contribute-data/portal`
-  | `/contribute-data/review`
-  | `/explore-data`
-  | `/explore-data/:id`
-  | `/monitor-activities`
-  | `/monitor-activities/calendar`
-  | `/monitor-activities/detail`
-  | `/run-computation`
-  | `/run-computation/:id/data-inputs`
-  | `/run-computation/:id/results`
-  | `/run-computation/:id/running`
-  | `/run-computation/:id/settings`
-  | `/search-data-repositories`
-  | `/search-data-repositories/:id`
-
-export type Params = {
-  '/explore-data/:id': { id: string }
-  '/run-computation/:id/data-inputs': { id: string }
-  '/run-computation/:id/results': { id: string }
-  '/run-computation/:id/running': { id: string }
-  '/run-computation/:id/settings': { id: string }
-  '/search-data-repositories/:id': { id: string }
-}
-
-export type ModalPath = never
-
-export const { Link, Navigate } = components<Path, Params>()
-export const { useModals, useNavigate, useParams } = hooks<Path, Params, ModalPath>()
-export const { redirect } = utils<Path, Params>()
diff --git a/strudel-demo-app/.eslintrc.cjs b/strudel-taskflows/.eslintrc.cjs
similarity index 100%
rename from strudel-demo-app/.eslintrc.cjs
rename to strudel-taskflows/.eslintrc.cjs
diff --git a/strudel-taskflows/COPYRIGHT.md b/strudel-taskflows/COPYRIGHT.md
new file mode 100644
index 00000000..89d64663
--- /dev/null
+++ b/strudel-taskflows/COPYRIGHT.md
@@ -0,0 +1,15 @@
+Copyright Notice
+================
+
+Scientific sofTware Research for User experience, Design, Engagement, and Learning (STRUDEL) Copyright (c) 2024, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved.
+
+If you have questions about your rights to use or distribute this software,
+please contact Berkeley Lab's Intellectual Property Office at
+IPO@lbl.gov.
+
+NOTICE.  This Software was developed under funding from the U.S. Department
+of Energy and the U.S. Government consequently retains certain rights.  As
+such, the U.S. Government has been granted for itself and others acting on
+its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the
+Software to reproduce, distribute copies to the public, prepare derivative 
+works, and perform publicly and display publicly, and to permit others to do so.
\ No newline at end of file
diff --git a/strudel-taskflows/LICENSE b/strudel-taskflows/LICENSE
new file mode 100644
index 00000000..a77dc941
--- /dev/null
+++ b/strudel-taskflows/LICENSE
@@ -0,0 +1,35 @@
+License Agreement
+=================
+
+Scientific sofTware Research for User experience, Design, Engagement, and Learning (STRUDEL) Copyright (c) 2024, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+(1) Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+(2) Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+(3) Neither the name of the University of California, Lawrence Berkeley
+National Laboratory, U.S. Dept. of Energy nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You are under no obligation whatsoever to provide any bug fixes, patches,
+or upgrades to the features, functionality or performance of the source
+code ("Enhancements") to anyone; however, if you choose to make your
+Enhancements available either publicly, or directly to Lawrence Berkeley
+National Laboratory, without imposing a separate written license agreement
+for such Enhancements, then you hereby grant the following license: a
+non-exclusive, royalty-free perpetual license to install, use, modify,
+prepare derivative works, incorporate into other computer software,
+distribute, and sublicense such enhancements or derivative works thereof,
+in binary and source code form.
\ No newline at end of file
diff --git a/strudel-demo-app/README.md b/strudel-taskflows/README.md
similarity index 100%
rename from strudel-demo-app/README.md
rename to strudel-taskflows/README.md
diff --git a/strudel-demo-app/index.html b/strudel-taskflows/index.html
similarity index 100%
rename from strudel-demo-app/index.html
rename to strudel-taskflows/index.html
diff --git a/strudel-demo-app/package-lock.json b/strudel-taskflows/package-lock.json
similarity index 99%
rename from strudel-demo-app/package-lock.json
rename to strudel-taskflows/package-lock.json
index 25a54aeb..081d8b69 100644
--- a/strudel-demo-app/package-lock.json
+++ b/strudel-taskflows/package-lock.json
@@ -11,7 +11,7 @@
       "dependencies": {
         "@emotion/react": "^11.11.4",
         "@emotion/styled": "^11.11.0",
-        "@generouted/react-router": "^1.18.8",
+        "@generouted/react-router": "^1.19.3",
         "@mui/icons-material": "^5.15.14",
         "@mui/lab": "^5.0.0-alpha.169",
         "@mui/material": "^5.15.14",
@@ -37,7 +37,7 @@
         "eslint-plugin-react-hooks": "^4.6.0",
         "eslint-plugin-react-refresh": "^0.4.6",
         "typescript": "^5.2.2",
-        "vite": "^5.2.0"
+        "vite": "^5.2.10"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1054,12 +1054,12 @@
       "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
     },
     "node_modules/@generouted/react-router": {
-      "version": "1.18.8",
-      "resolved": "https://registry.npmjs.org/@generouted/react-router/-/react-router-1.18.8.tgz",
-      "integrity": "sha512-LN1mIF1Akj7t3mvTeDlBZCa8a0wFPooLHDKam9YcG/UcgOEMTn+Y63tV5AGdcM33xtOYeE+pTX+ud+GR3yNe0g==",
+      "version": "1.19.3",
+      "resolved": "https://registry.npmjs.org/@generouted/react-router/-/react-router-1.19.3.tgz",
+      "integrity": "sha512-3/Y0j302HnwxyU0KOoCOFSVwJZN4eD73+CVtSaOKUnLbGW1ftP1dT8Eur14PALGvZOVR4gsFrUJFq6a4c/kWvQ==",
       "dependencies": {
         "fast-glob": "^3.3.2",
-        "generouted": "^1.18.8"
+        "generouted": "^1.19.3"
       },
       "peerDependencies": {
         "react": ">=18",
@@ -3789,9 +3789,9 @@
       }
     },
     "node_modules/generouted": {
-      "version": "1.18.8",
-      "resolved": "https://registry.npmjs.org/generouted/-/generouted-1.18.8.tgz",
-      "integrity": "sha512-iaKHLtR/ziZEJT+0n0+6RMTRFSkPsnF7GvnDvM5xYOjKvFjFZO/u5HRQpKIlBhNC0IGu6JSdHlcu9EJcLOQXvA==",
+      "version": "1.19.3",
+      "resolved": "https://registry.npmjs.org/generouted/-/generouted-1.19.3.tgz",
+      "integrity": "sha512-1KnOmPYBNryAm+9PvvOTOzKiWvuBKRwK+Xr4dP/I9U00y7UAv/VrFGeb0v7mYMiny6jpWBmfBFKaUvq4Q0Z4dQ==",
       "peerDependencies": {
         "vite": ">=3"
       }
@@ -5975,12 +5975,12 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
     "node_modules/vite": {
-      "version": "5.2.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
-      "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==",
+      "version": "5.2.10",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
+      "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
       "dependencies": {
         "esbuild": "^0.20.1",
-        "postcss": "^8.4.36",
+        "postcss": "^8.4.38",
         "rollup": "^4.13.0"
       },
       "bin": {
diff --git a/strudel-demo-app/package.json b/strudel-taskflows/package.json
similarity index 95%
rename from strudel-demo-app/package.json
rename to strudel-taskflows/package.json
index a948d8bc..66b6d5d6 100644
--- a/strudel-demo-app/package.json
+++ b/strudel-taskflows/package.json
@@ -13,7 +13,7 @@
   "dependencies": {
     "@emotion/react": "^11.11.4",
     "@emotion/styled": "^11.11.0",
-    "@generouted/react-router": "^1.18.8",
+    "@generouted/react-router": "^1.19.3",
     "@mui/icons-material": "^5.15.14",
     "@mui/lab": "^5.0.0-alpha.169",
     "@mui/material": "^5.15.14",
@@ -39,7 +39,7 @@
     "eslint-plugin-react-hooks": "^4.6.0",
     "eslint-plugin-react-refresh": "^0.4.6",
     "typescript": "^5.2.2",
-    "vite": "^5.2.0"
+    "vite": "^5.2.10"
   },
   "engines": {
     "node": "^18.18.0 || >=20.0.0"
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/compare-data/scenarios.json b/strudel-taskflows/public/data/default/compare-data/scenarios.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/compare-data/scenarios.json
rename to strudel-taskflows/public/data/default/compare-data/scenarios.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/contribute-data/contributor_datasets.json b/strudel-taskflows/public/data/default/contribute-data/contributor_datasets.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/contribute-data/contributor_datasets.json
rename to strudel-taskflows/public/data/default/contribute-data/contributor_datasets.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/explore-data/genomes.tsv b/strudel-taskflows/public/data/default/explore-data/genomes.tsv
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/explore-data/genomes.tsv
rename to strudel-taskflows/public/data/default/explore-data/genomes.tsv
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/monitor-activities/experiment_detail.json b/strudel-taskflows/public/data/default/monitor-activities/experiment_detail.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/monitor-activities/experiment_detail.json
rename to strudel-taskflows/public/data/default/monitor-activities/experiment_detail.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/monitor-activities/experiments.json b/strudel-taskflows/public/data/default/monitor-activities/experiments.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/monitor-activities/experiments.json
rename to strudel-taskflows/public/data/default/monitor-activities/experiments.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/inputs.json b/strudel-taskflows/public/data/default/run-computation/inputs.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/inputs.json
rename to strudel-taskflows/public/data/default/run-computation/inputs.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/list.json b/strudel-taskflows/public/data/default/run-computation/list.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/list.json
rename to strudel-taskflows/public/data/default/run-computation/list.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/results_bar_chart.json b/strudel-taskflows/public/data/default/run-computation/results_bar_chart.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/results_bar_chart.json
rename to strudel-taskflows/public/data/default/run-computation/results_bar_chart.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/results_line_chart.json b/strudel-taskflows/public/data/default/run-computation/results_line_chart.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/results_line_chart.json
rename to strudel-taskflows/public/data/default/run-computation/results_line_chart.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/results_table.json b/strudel-taskflows/public/data/default/run-computation/results_table.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/run-computation/results_table.json
rename to strudel-taskflows/public/data/default/run-computation/results_table.json
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/search-data-repositories/datasets.json b/strudel-taskflows/public/data/default/search-data-repositories/datasets.json
similarity index 100%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/public/data/default/search-data-repositories/datasets.json
rename to strudel-taskflows/public/data/default/search-data-repositories/datasets.json
diff --git a/strudel-demo-app/public/favicon.ico b/strudel-taskflows/public/favicon.ico
similarity index 100%
rename from strudel-demo-app/public/favicon.ico
rename to strudel-taskflows/public/favicon.ico
diff --git a/strudel-demo-app/public/robots.txt b/strudel-taskflows/public/robots.txt
similarity index 100%
rename from strudel-demo-app/public/robots.txt
rename to strudel-taskflows/public/robots.txt
diff --git a/strudel-demo-app/public/vite.svg b/strudel-taskflows/public/vite.svg
similarity index 100%
rename from strudel-demo-app/public/vite.svg
rename to strudel-taskflows/public/vite.svg
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.tsx b/strudel-taskflows/src/App.tsx
similarity index 58%
rename from strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.tsx
rename to strudel-taskflows/src/App.tsx
index 193dc205..31a4a816 100644
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.tsx
+++ b/strudel-taskflows/src/App.tsx
@@ -1,24 +1,22 @@
 import React from 'react';
-import './App.css';
 import { ThemeProvider } from '@mui/material/styles';
 import { CssBaseline } from '@mui/material';
-import { RouterProvider } from 'react-router-dom';
 import { LocalizationProvider } from '@mui/x-date-pickers';
 import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
-import { router } from './routes';
+import { routes } from '@generouted/react-router'
 import { theme } from './theme';
-import { AppProvider } from '../context/ContextProvider';
-import { ApiModal } from '../components/ApiModal';
-
-export const basename = document.querySelector('base')?.getAttribute('href') ?? '/';
+import { AppProvider } from './context/ContextProvider';
+import { ApiModal } from './components/ApiModal';
+import { RouterProvider, createBrowserRouter } from 'react-router-dom';
 
 const App: React.FC = () => {
   return (
     <LocalizationProvider dateAdapter={AdapterDayjs}>
       <ThemeProvider theme={theme}>
         <CssBaseline />
-        <AppProvider appTitle="{@ cookiecutter.appTitle @}">
-          <RouterProvider router={router} />
+        {/* strudel-kit-variable-next-line */}
+        <AppProvider appTitle="Demo App">
+          <RouterProvider router={createBrowserRouter(routes)} />
           <ApiModal />
         </AppProvider>
       </ThemeProvider>
diff --git a/strudel-taskflows/src/assets/react.svg b/strudel-taskflows/src/assets/react.svg
new file mode 100644
index 00000000..6c87de9b
--- /dev/null
+++ b/strudel-taskflows/src/assets/react.svg
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
\ No newline at end of file
diff --git a/strudel-demo-app/src/components/ApiModal.tsx b/strudel-taskflows/src/components/ApiModal.tsx
similarity index 100%
rename from strudel-demo-app/src/components/ApiModal.tsx
rename to strudel-taskflows/src/components/ApiModal.tsx
diff --git a/strudel-demo-app/src/components/AppLink.tsx b/strudel-taskflows/src/components/AppLink.tsx
similarity index 100%
rename from strudel-demo-app/src/components/AppLink.tsx
rename to strudel-taskflows/src/components/AppLink.tsx
diff --git a/strudel-demo-app/src/components/CheckboxList.tsx b/strudel-taskflows/src/components/CheckboxList.tsx
similarity index 100%
rename from strudel-demo-app/src/components/CheckboxList.tsx
rename to strudel-taskflows/src/components/CheckboxList.tsx
diff --git a/strudel-demo-app/src/components/Collapsible.tsx b/strudel-taskflows/src/components/Collapsible.tsx
similarity index 100%
rename from strudel-demo-app/src/components/Collapsible.tsx
rename to strudel-taskflows/src/components/Collapsible.tsx
diff --git a/strudel-demo-app/src/components/FilterField.tsx b/strudel-taskflows/src/components/FilterField.tsx
similarity index 100%
rename from strudel-demo-app/src/components/FilterField.tsx
rename to strudel-taskflows/src/components/FilterField.tsx
diff --git a/strudel-demo-app/src/components/FilterGroup.tsx b/strudel-taskflows/src/components/FilterGroup.tsx
similarity index 100%
rename from strudel-demo-app/src/components/FilterGroup.tsx
rename to strudel-taskflows/src/components/FilterGroup.tsx
diff --git a/strudel-demo-app/src/components/Filters.tsx b/strudel-taskflows/src/components/Filters.tsx
similarity index 100%
rename from strudel-demo-app/src/components/Filters.tsx
rename to strudel-taskflows/src/components/Filters.tsx
diff --git a/strudel-demo-app/src/components/FiltersPanel.tsx b/strudel-taskflows/src/components/FiltersPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/components/FiltersPanel.tsx
rename to strudel-taskflows/src/components/FiltersPanel.tsx
diff --git a/strudel-demo-app/src/components/ImageWrapper.css b/strudel-taskflows/src/components/ImageWrapper.css
similarity index 100%
rename from strudel-demo-app/src/components/ImageWrapper.css
rename to strudel-taskflows/src/components/ImageWrapper.css
diff --git a/strudel-demo-app/src/components/ImageWrapper.tsx b/strudel-taskflows/src/components/ImageWrapper.tsx
similarity index 100%
rename from strudel-demo-app/src/components/ImageWrapper.tsx
rename to strudel-taskflows/src/components/ImageWrapper.tsx
diff --git a/strudel-demo-app/src/components/LabelValueTable.tsx b/strudel-taskflows/src/components/LabelValueTable.tsx
similarity index 100%
rename from strudel-demo-app/src/components/LabelValueTable.tsx
rename to strudel-taskflows/src/components/LabelValueTable.tsx
diff --git a/strudel-demo-app/src/components/LinearMeter.tsx b/strudel-taskflows/src/components/LinearMeter.tsx
similarity index 100%
rename from strudel-demo-app/src/components/LinearMeter.tsx
rename to strudel-taskflows/src/components/LinearMeter.tsx
diff --git a/strudel-demo-app/src/components/PageHeader.tsx b/strudel-taskflows/src/components/PageHeader.tsx
similarity index 100%
rename from strudel-demo-app/src/components/PageHeader.tsx
rename to strudel-taskflows/src/components/PageHeader.tsx
diff --git a/strudel-demo-app/src/components/StrudelSlider.tsx b/strudel-taskflows/src/components/StrudelSlider.tsx
similarity index 100%
rename from strudel-demo-app/src/components/StrudelSlider.tsx
rename to strudel-taskflows/src/components/StrudelSlider.tsx
diff --git a/strudel-demo-app/src/components/TopBar.tsx b/strudel-taskflows/src/components/TopBar.tsx
similarity index 100%
rename from strudel-demo-app/src/components/TopBar.tsx
rename to strudel-taskflows/src/components/TopBar.tsx
diff --git a/strudel-demo-app/src/context/ContextProvider.tsx b/strudel-taskflows/src/context/ContextProvider.tsx
similarity index 93%
rename from strudel-demo-app/src/context/ContextProvider.tsx
rename to strudel-taskflows/src/context/ContextProvider.tsx
index 4577b46d..e036720b 100644
--- a/strudel-demo-app/src/context/ContextProvider.tsx
+++ b/strudel-taskflows/src/context/ContextProvider.tsx
@@ -65,7 +65,8 @@ export const AppProvider: React.FC<AppProviderProps> = (props) => {
 export const useAppState = () => {
   const context = useContext(AppContext)
   if (context === undefined) {
-    throw new Error('useAppState must be used within an AppProvider')
+    console.log('useAppState must be used within an AppProvider')
+    return { state: { appTitle: 'Test' } };
   }
   return context
 }
\ No newline at end of file
diff --git a/strudel-demo-app/src/context/actions.ts b/strudel-taskflows/src/context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/context/actions.ts
rename to strudel-taskflows/src/context/actions.ts
diff --git a/strudel-demo-app/src/declarations.d.ts b/strudel-taskflows/src/declarations.d.ts
similarity index 100%
rename from strudel-demo-app/src/declarations.d.ts
rename to strudel-taskflows/src/declarations.d.ts
diff --git a/strudel-demo-app/src/index.css b/strudel-taskflows/src/index.css
similarity index 100%
rename from strudel-demo-app/src/index.css
rename to strudel-taskflows/src/index.css
diff --git a/strudel-demo-app/src/main.tsx b/strudel-taskflows/src/main.tsx
similarity index 82%
rename from strudel-demo-app/src/main.tsx
rename to strudel-taskflows/src/main.tsx
index 990f430b..0a66e3b7 100644
--- a/strudel-demo-app/src/main.tsx
+++ b/strudel-taskflows/src/main.tsx
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'
 import './index.css'
 import App from './App'
 
+// TODO: might need to move this into the useDataFromSource hook
 export const basename = document.querySelector('base')?.getAttribute('href') ?? '';
 
 ReactDOM.createRoot(document.getElementById('root')!).render(
diff --git a/strudel-taskflows/src/pages/compare-data/_config/taskflow.config.ts b/strudel-taskflows/src/pages/compare-data/_config/taskflow.config.ts
new file mode 100644
index 00000000..cc748d0d
--- /dev/null
+++ b/strudel-taskflows/src/pages/compare-data/_config/taskflow.config.ts
@@ -0,0 +1,105 @@
+import { CompareDataConfig } from "./taskflow.types";
+
+export const taskflow: CompareDataConfig = {
+  properties: {
+    itemName: "scenario",
+    itemNamePlural: "scenarios",
+  },
+  data: {
+    items: {
+      source: "default/compare-data/scenarios.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Compare Data App",
+      description: "Description of this app section",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Scenario Name",
+          width: 200
+        },
+        {
+          field: "description",
+          headerName: "Description",
+          width: 200
+        },
+        {
+          field: "analysis_type",
+          headerName: "Analysis Type",
+          width: 200
+        },
+        {
+          field: "volumetric_flow_rate",
+          headerName: "Volumetric Flow Rate",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "tss_concentration",
+          headerName: "TSS Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "cod_concentration",
+          headerName: "COD Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "tkn_concentration",
+          headerName: "TKN Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "acetic_acid_concentration",
+          headerName: "Acetic Acid Concentration",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric6",
+          headerName: "metric6",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric7",
+          headerName: "metric7",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric8",
+          headerName: "metric8",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric9",
+          headerName: "metric9",
+          width: 200,
+          isComparisonMetric: true
+        },
+        {
+          field: "metric10",
+          headerName: "metric10",
+          width: 200,
+          isComparisonMetric: true
+        }
+      ]
+    },
+    new: {
+      title: "New Scenario",
+      description: "Description of this app section"
+    },
+    compare: {
+      title: "Compare Scenarios",
+      description: "Description of this app section"
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/compare-data/_config/taskflow.types.ts b/strudel-taskflows/src/pages/compare-data/_config/taskflow.types.ts
new file mode 100644
index 00000000..73b8965f
--- /dev/null
+++ b/strudel-taskflows/src/pages/compare-data/_config/taskflow.types.ts
@@ -0,0 +1,44 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+export type CompareDataConfigColDef = GridColDef & {
+  isComparisonMetric?: boolean;
+}
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface CompareDataConfig {
+  /** Attributes that are used across the Task Flow */
+  properties: {
+    /** Name of entity being compared */
+    itemName: string,
+    /** Pluralized version of itemName */
+    itemNamePlural: string,
+  },
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      tableColumns: CompareDataConfigColDef[]
+    },
+    new: {
+      title: string,
+      description: string
+    },
+    compare: {
+      title: string,
+      description: string
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/compare-data/_context/ContextProvider.tsx b/strudel-taskflows/src/pages/compare-data/_context/ContextProvider.tsx
similarity index 93%
rename from strudel-demo-app/src/pages/compare-data/_context/ContextProvider.tsx
rename to strudel-taskflows/src/pages/compare-data/_context/ContextProvider.tsx
index 3e3bd237..8d43ba51 100644
--- a/strudel-demo-app/src/pages/compare-data/_context/ContextProvider.tsx
+++ b/strudel-taskflows/src/pages/compare-data/_context/ContextProvider.tsx
@@ -1,10 +1,6 @@
 import React, { useEffect, useReducer, useContext } from 'react';
 import { CompareDataAction, CompareDataActionType, setComparisonData, setData } from './actions';
-import { GridColDef } from '@mui/x-data-grid';
-
-export type CompareDataGridColDef = GridColDef & {
-  isComparisonMetric?: boolean;
-}
+import { CompareDataConfigColDef } from '../_config/taskflow.types';
 
 interface ComparisonRow {
   [key: string]: number | string | null | undefined;
@@ -13,8 +9,8 @@ interface ComparisonRow {
 export interface CompareDataState {
   data: any[];
   selectedRows: any[];
-  columns: CompareDataGridColDef[];
-  comparisonColumns: CompareDataGridColDef[];
+  columns: CompareDataConfigColDef[];
+  comparisonColumns: CompareDataConfigColDef[];
   dataIdField: string;
   comparing?: boolean;
   comparisonData: ComparisonRow[]
@@ -108,7 +104,7 @@ export const CompareDataProvider: React.FC<CompareDataProviderProps> = (props) =
     if (state.comparing && state.selectedRows.length > 1) {
       const metrics = state.columns.filter((c) => c.isComparisonMetric);
       const scenarios = state.data.filter((d) => state.selectedRows.indexOf(d.id) > -1);
-      const comparisonColumns: CompareDataGridColDef[] = [
+      const comparisonColumns: CompareDataConfigColDef[] = [
         {
           field: 'metric',
           headerName: 'Metric',
diff --git a/strudel-demo-app/src/pages/compare-data/_context/actions.ts b/strudel-taskflows/src/pages/compare-data/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/pages/compare-data/_context/actions.ts
rename to strudel-taskflows/src/pages/compare-data/_context/actions.ts
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/CompareDataWrapper.tsx b/strudel-taskflows/src/pages/compare-data/_layout.tsx
similarity index 51%
rename from strudel-cookiecutter/compare-data/{@cookiecutter.name@}/CompareDataWrapper.tsx
rename to strudel-taskflows/src/pages/compare-data/_layout.tsx
index 786be686..7d6d1130 100644
--- a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/CompareDataWrapper.tsx
+++ b/strudel-taskflows/src/pages/compare-data/_layout.tsx
@@ -1,20 +1,19 @@
 import { Box } from '@mui/material';
-import React, { useEffect, useState } from 'react';
+import React from 'react';
 import { Outlet } from 'react-router-dom';
-import { basename } from '../App';
 import { TopBar } from '../../components/TopBar';
-import { CompareDataProvider } from './context/ContextProvider';
-import { GridColDef } from '@mui/x-data-grid';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import definitions from './definitions.json'
+import { CompareDataProvider } from './_context/ContextProvider';
+import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the compare-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const CompareDataWrapper: React.FC = () => {
-  const scenarios = useDataFromSource('{@ cookiecutter.data.list.table.dataSource @}', basename);
-        
+const CompareDataWrapper: React.FC = () => {
+  const scenarios = useDataFromSource(taskflow.data.items.source, basename);
+
   /**
    * Content to render on the page for this component
    */
@@ -26,8 +25,8 @@ export const CompareDataWrapper: React.FC = () => {
       <Box>
         <CompareDataProvider 
           data={scenarios || []} 
-          columns={definitions.columns.list.table} 
-          dataIdField='{@ cookiecutter.data.list.table.dataIdField @}'
+          columns={taskflow.pages.index.tableColumns} 
+          dataIdField='id'
         >
           <Outlet />
         </CompareDataProvider>
@@ -36,6 +35,4 @@ export const CompareDataWrapper: React.FC = () => {
   )
 }
 
-export type CompareDataGridColDef = GridColDef & {
-  isComparisonMetric?: boolean;
-}
\ No newline at end of file
+export default CompareDataWrapper;
\ No newline at end of file
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/ScenarioComparison.tsx b/strudel-taskflows/src/pages/compare-data/compare.tsx
similarity index 83%
rename from strudel-cookiecutter/compare-data/{@cookiecutter.name@}/ScenarioComparison.tsx
rename to strudel-taskflows/src/pages/compare-data/compare.tsx
index bd255283..b65bc877 100644
--- a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/ScenarioComparison.tsx
+++ b/strudel-taskflows/src/pages/compare-data/compare.tsx
@@ -3,16 +3,17 @@ import { Box, Button, Container, Link, Paper, Stack } from '@mui/material';
 import React, { useEffect } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
-import { useCompareData } from './context/ContextProvider';
-import { setComparing } from './context/actions';
+import { useCompareData } from './_context/ContextProvider';
+import { setComparing } from './_context/actions';
 import { DataGrid } from '@mui/x-data-grid';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Comparison page for the compare-data Task Flow.
  * Displays a table with the selected items from `<ScenarioList>`
  * as the columns and the metrics as the rows.
  */
-export const ScenarioComparison: React.FC = () => {
+const ScenarioComparison: React.FC = () => {
   const { state, dispatch } = useCompareData();
 
   /**
@@ -32,21 +33,21 @@ export const ScenarioComparison: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        pageTitle="Compare {@ cookiecutter.compareItemPlural @}"
-        description="{@ cookiecutter.pages.comparison.pageDescription @}"
+        pageTitle={taskflow.pages.compare.title}
+        description={taskflow.pages.compare.description}
         actions={
           <Stack direction="row">
             <Box>
               <Link component={RouterLink} to="..">
                 <Button variant="contained" startIcon={<ArrowBackIcon />}>
-                  Back to {@ cookiecutter.compareItemPlural @}
+                  Back to {taskflow.properties.itemNamePlural}
                 </Button>
               </Link>
             </Box>
             <Box>
               <Link component={RouterLink} to="../new">
                 <Button variant="contained">
-                  New {@ cookiecutter.compareItem @}
+                  New {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
@@ -90,4 +91,6 @@ export const ScenarioComparison: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+export default ScenarioComparison;
\ No newline at end of file
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/ScenarioList.tsx b/strudel-taskflows/src/pages/compare-data/index.tsx
similarity index 79%
rename from strudel-cookiecutter/compare-data/{@cookiecutter.name@}/ScenarioList.tsx
rename to strudel-taskflows/src/pages/compare-data/index.tsx
index ac9fa5c8..11524798 100644
--- a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/ScenarioList.tsx
+++ b/strudel-taskflows/src/pages/compare-data/index.tsx
@@ -4,15 +4,16 @@ import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
 import { PageHeader } from '../../components/PageHeader';
-import { useCompareData } from './context/ContextProvider';
-import { setSelectedRows } from './context/actions';
+import { useCompareData } from './_context/ContextProvider';
+import { setSelectedRows } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
   
 /**
  * List page to show comparable items in the compare-data Task Flow.
  * Items in this table are selectable and can be sent to the `<ScenarioComparison>` 
  * page to be rendered in the comparison table. 
  */
-export const ScenarioList: React.FC = () => {
+const ScenarioList: React.FC = () => {
   const { state, dispatch } = useCompareData();
   
   /**
@@ -21,8 +22,8 @@ export const ScenarioList: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        pageTitle="{@ cookiecutter.pages.list.pageTitle @}"
-        description="{@ cookiecutter.pages.list.pageDescription @}"
+        pageTitle={taskflow.pages.index.title}
+        description={taskflow.pages.index.description}
         actions={
           <Stack direction="row">
             <Box>
@@ -31,14 +32,14 @@ export const ScenarioList: React.FC = () => {
                   <Button 
                     variant="outlined"
                   >
-                    Compare {@ cookiecutter.compareItemPlural @}
+                    Compare {taskflow.properties.itemNamePlural}
                   </Button>
                 )}
                 {state.selectedRows.length > 0 && (
                   <Button 
                     variant={state.selectedRows.length > 1 ? 'contained' : 'outlined' }
                   >
-                    Compare {@ cookiecutter.compareItemPlural @} ({state.selectedRows.length})
+                    Compare {taskflow.properties.itemNamePlural} ({state.selectedRows.length})
                   </Button>
                 )}
               </Link>
@@ -46,7 +47,7 @@ export const ScenarioList: React.FC = () => {
             <Box>
               <Link component={RouterLink} to="new">
                 <Button variant="contained">
-                  New {@ cookiecutter.compareItem @}
+                  New {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
@@ -92,4 +93,13 @@ export const ScenarioList: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+// const ScenarioList: React.FC = () => {
+//   const { state, dispatch } = useCompareData();
+//   return (
+//     <h1>Compare Data List</h1>
+//   )
+// }
+
+export default ScenarioList;
\ No newline at end of file
diff --git a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/NewScenario.tsx b/strudel-taskflows/src/pages/compare-data/new.tsx
similarity index 82%
rename from strudel-cookiecutter/compare-data/{@cookiecutter.name@}/NewScenario.tsx
rename to strudel-taskflows/src/pages/compare-data/new.tsx
index bd773f7f..7b7c9574 100644
--- a/strudel-cookiecutter/compare-data/{@cookiecutter.name@}/NewScenario.tsx
+++ b/strudel-taskflows/src/pages/compare-data/new.tsx
@@ -2,8 +2,9 @@ import { Box, Button, Container, Link, Paper, Stack } from '@mui/material';
 import React, { useEffect } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
-import { useCompareData } from './context/ContextProvider';
-import { setComparing } from './context/actions';
+import { useCompareData } from './_context/ContextProvider';
+import { setComparing } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page for filling out a form for adding a new item to 
@@ -29,8 +30,8 @@ export const NewScenario: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        pageTitle="New {@ cookiecutter.compareItem @}"
-        description="{@ cookiecutter.pages.newItem.pageDescription @}"
+        pageTitle={taskflow.pages.new.title}
+        description={taskflow.pages.new.description}
         actions={
           <Stack direction="row">
             <Box>
@@ -43,7 +44,7 @@ export const NewScenario: React.FC = () => {
             <Box>
               <Link component={RouterLink} to="..">
                 <Button variant="contained">
-                  Save Scenario
+                  Save {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
@@ -72,4 +73,6 @@ export const NewScenario: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+export default NewScenario;
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/contribute-data/_components/DataFilesPanel.tsx b/strudel-taskflows/src/pages/contribute-data/_components/DataFilesPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_components/DataFilesPanel.tsx
rename to strudel-taskflows/src/pages/contribute-data/_components/DataFilesPanel.tsx
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/DatasetView.tsx b/strudel-taskflows/src/pages/contribute-data/_components/DatasetView.tsx
similarity index 97%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/DatasetView.tsx
rename to strudel-taskflows/src/pages/contribute-data/_components/DatasetView.tsx
index abf0e98b..47a48b1a 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/DatasetView.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/_components/DatasetView.tsx
@@ -2,7 +2,7 @@ import { Box, Paper, Stack, Typography } from '@mui/material';
 import { GridColDef } from '@mui/x-data-grid';
 import React from 'react';
 import { DataGrid } from '@mui/x-data-grid';
-import { LabelValueTable } from '../../components/LabelValueTable';
+import { LabelValueTable } from '../../../components/LabelValueTable';
 import { files } from './DataFilesPanel';
 
 /**
diff --git a/strudel-demo-app/src/pages/contribute-data/_components/MetadataPanel.tsx b/strudel-taskflows/src/pages/contribute-data/_components/MetadataPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_components/MetadataPanel.tsx
rename to strudel-taskflows/src/pages/contribute-data/_components/MetadataPanel.tsx
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ValidationChecks.tsx b/strudel-taskflows/src/pages/contribute-data/_components/ValidationChecks.tsx
similarity index 97%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ValidationChecks.tsx
rename to strudel-taskflows/src/pages/contribute-data/_components/ValidationChecks.tsx
index 8c0353bd..b8f5a332 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ValidationChecks.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/_components/ValidationChecks.tsx
@@ -1,7 +1,7 @@
 import { Box, LinearProgress, Paper, Stack, Typography } from '@mui/material';
 import React from 'react';
-import { LinearMeter } from '../../components/LinearMeter';
-import { useContributeData } from './context/ContextProvider';
+import { LinearMeter } from '../../../components/LinearMeter';
+import { useContributeData } from '../_context/ContextProvider';
   
 /**
  * Panel to display information and status of validation checks in the contribute-data Task Flow.
diff --git a/strudel-taskflows/src/pages/contribute-data/_config/taskflow.config.ts b/strudel-taskflows/src/pages/contribute-data/_config/taskflow.config.ts
new file mode 100644
index 00000000..fc25e122
--- /dev/null
+++ b/strudel-taskflows/src/pages/contribute-data/_config/taskflow.config.ts
@@ -0,0 +1,57 @@
+import { ContributeDataConfig } from "./taskflow.types";
+
+export const taskflow: ContributeDataConfig = {
+  data: {
+    datasets: {
+      source: "default/contribute-data/contributor_datasets.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Register as a data contributor"
+    },
+    portal: {
+      title: "Your Dataset Uploads",
+      tableColumns: [
+        {
+          field: "title",
+          headerName: "Dataset Title",
+          width: 200
+        },
+        {
+          field: "category",
+          headerName: "Category",
+          width: 200
+        },
+        {
+          field: "summary",
+          headerName: "Summary",
+          width: 200
+        },
+        {
+          field: "doi",
+          headerName: "DOI",
+          width: 200
+        },
+        {
+          field: "publication_date",
+          headerName: "Created Date",
+          width: 200
+        },
+        {
+          field: "status",
+          headerName: "Status",
+          width: 200
+        }
+      ]
+    },
+    new: {
+      title: "Upload a new dataset",
+      description: "Description of this app section"
+    },
+    review: {
+      title: "Review your new dataset"
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/contribute-data/_config/taskflow.types.ts b/strudel-taskflows/src/pages/contribute-data/_config/taskflow.types.ts
new file mode 100644
index 00000000..948df04e
--- /dev/null
+++ b/strudel-taskflows/src/pages/contribute-data/_config/taskflow.types.ts
@@ -0,0 +1,36 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface ContributeDataConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: Record<string, any>,
+  data: {
+    datasets: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+    },
+    portal: {
+      title: string,
+      tableColumns: GridColDef[]
+    },
+    new: {
+      title: string,
+      description: string
+    },
+    review: {
+      title: string,
+    },
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/ContextProvider.tsx b/strudel-taskflows/src/pages/contribute-data/_context/ContextProvider.tsx
similarity index 94%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/ContextProvider.tsx
rename to strudel-taskflows/src/pages/contribute-data/_context/ContextProvider.tsx
index a6c23fe4..37e5bdf3 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/ContextProvider.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/_context/ContextProvider.tsx
@@ -1,6 +1,5 @@
-import React, { useEffect, useReducer, useContext } from 'react';
+import React, { useContext } from 'react';
 import { ContributeDataAction, ContributeDataActionType } from './actions';
-import { filterData } from './utils';
 
 export interface DataFilter {
   field: string;
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/actions.ts b/strudel-taskflows/src/pages/contribute-data/_context/actions.ts
similarity index 87%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/actions.ts
rename to strudel-taskflows/src/pages/contribute-data/_context/actions.ts
index f73efbe4..e94576b2 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/context/actions.ts
+++ b/strudel-taskflows/src/pages/contribute-data/_context/actions.ts
@@ -1,5 +1,3 @@
-import { ContributeDataState } from './ContextProvider';
-
 export enum ContributeDataActionType {
   RUN_CHECKS = 'RUN_CHECKS',
   FINISH_CHECKS = 'FINISH_CHECKS',
diff --git a/strudel-demo-app/src/pages/contribute-data/_context/utils.ts b/strudel-taskflows/src/pages/contribute-data/_context/utils.ts
similarity index 100%
rename from strudel-demo-app/src/pages/contribute-data/_context/utils.ts
rename to strudel-taskflows/src/pages/contribute-data/_context/utils.ts
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ContributeDataWrapper.tsx b/strudel-taskflows/src/pages/contribute-data/_layout.tsx
similarity index 76%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ContributeDataWrapper.tsx
rename to strudel-taskflows/src/pages/contribute-data/_layout.tsx
index 981f5af9..714f7723 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ContributeDataWrapper.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/_layout.tsx
@@ -2,13 +2,13 @@ import { Box } from '@mui/material';
 import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
-import { ContributeDataProvider } from './context/ContextProvider';
+import { ContributeDataProvider } from './_context/ContextProvider';
 
 /**
  * Top-level wrapper for the contribute-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const ContributeDataWrapper: React.FC = () => {
+const ContributeDataLayout: React.FC = () => {
   return (
     <Box>
       <Box sx={{ flexGrow: 1 }}>
@@ -21,4 +21,6 @@ export const ContributeDataWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default ContributeDataLayout;
\ No newline at end of file
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/Register.tsx b/strudel-taskflows/src/pages/contribute-data/index.tsx
similarity index 95%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/Register.tsx
rename to strudel-taskflows/src/pages/contribute-data/index.tsx
index 49f148fb..cd166231 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/Register.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/index.tsx
@@ -2,12 +2,13 @@ import { Box, Button, Container, Link, Paper, Stack, TextField, Typography } fro
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Initial page for registering as a contributor in the contribute-data Task Flow.
  * After submitting, users are directed to the `<ContributorPortal>`.
  */
-export const Register: React.FC = () => {
+const Register: React.FC = () => {
   return (
     <Container
         maxWidth="md"
@@ -25,7 +26,7 @@ export const Register: React.FC = () => {
               variant="h6" 
               component="h1" 
             >
-              {@ cookiecutter.pages.register.pageTitle @}
+              {taskflow.pages.index.title}
             </Typography>
             <Box>
               <Typography fontWeight="medium" mb={1}>
@@ -86,4 +87,6 @@ export const Register: React.FC = () => {
         </Paper>
       </Container>
   )
-}
\ No newline at end of file
+}
+
+export default Register;
\ No newline at end of file
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/NewDataset.tsx b/strudel-taskflows/src/pages/contribute-data/new.tsx
similarity index 82%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/NewDataset.tsx
rename to strudel-taskflows/src/pages/contribute-data/new.tsx
index b396e22e..a14a5c1d 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/NewDataset.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/new.tsx
@@ -1,14 +1,15 @@
 import { Box, Button, Container, Grid, Link, Stack, Typography } from '@mui/material';
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { DataFilesPanel } from './DataFilesPanel';
-import { MetadataPanel } from './MetadataPanel';
+import { DataFilesPanel } from './_components/DataFilesPanel';
+import { MetadataPanel } from './_components/MetadataPanel';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page for entering information about a new dataset in the contribute-data Task Flow.
  * Includes the `<MetadataPanel>` and `<DataFilesPanel>` for adding input data.
  */
-export const NewDataset: React.FC = () => {  
+const NewDataset: React.FC = () => {  
   return (
     <Container
       maxWidth="xl"
@@ -21,10 +22,10 @@ export const NewDataset: React.FC = () => {
         <Stack direction="row" justifyContent="space-between">
           <Stack>
             <Typography variant="h6" component="h1">
-              {@ cookiecutter.pages.upload.pageTitle @}
+              {taskflow.pages.new.title}
             </Typography>
             <Typography>
-              {@ cookiecutter.pages.upload.pageDescription @}
+              {taskflow.pages.new.description}
             </Typography>
           </Stack>
           <Stack direction="row">
@@ -57,4 +58,6 @@ export const NewDataset: React.FC = () => {
       </Stack>
     </Container>
   );
-}
\ No newline at end of file
+}
+
+export default NewDataset;
\ No newline at end of file
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ContributorPortal.tsx b/strudel-taskflows/src/pages/contribute-data/portal.tsx
similarity index 80%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ContributorPortal.tsx
rename to strudel-taskflows/src/pages/contribute-data/portal.tsx
index 2db1c2da..c9e76398 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ContributorPortal.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/portal.tsx
@@ -6,16 +6,16 @@ import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import definitions from './definitions.json';
+import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page to show a contributor's uploads in the contribute-data Task Flow.
  * Also allows users to start a new dataset which sends them to the `<NewDataset>` component.
  */
-export const ContributorPortal: React.FC = () => {
-  const datasets = useDataFromSource('{@ cookiecutter.data.portal.table.dataSource @}', basename);
-          
+const ContributorPortal: React.FC = () => {
+  const datasets = useDataFromSource(taskflow.data.datasets.source, basename);
+  
   /**
    * Content to render on the page for this component
    */
@@ -29,7 +29,7 @@ export const ContributorPortal: React.FC = () => {
       <Stack>
         <Stack direction="row" justifyContent="space-between" alignItems="center">
           <Typography variant="h6" component="h1">
-            {@ cookiecutter.pages.portal.pageTitle @}
+            {taskflow.pages.portal.title}
           </Typography>
           <Link component={RouterLink} to="../new">
             <Button variant="contained">
@@ -40,7 +40,7 @@ export const ContributorPortal: React.FC = () => {
         <Paper>
           <DataGrid
             rows={datasets || []}
-            getRowId={(row) => row['{@ cookiecutter.data.portal.table.dataIdField @}']}
+            getRowId={(row) => row[taskflow.data.datasets.idField]}
             columns={columns}
             disableColumnSelector
             disableRowSelectionOnClick
@@ -55,7 +55,7 @@ export const ContributorPortal: React.FC = () => {
  * Define column definitions in-file for prototyping
  */
 const columns: GridColDef[] = [
-  ...definitions.columns.portal.table,
+  ...taskflow.pages.portal.tableColumns,
   { 
     field: 'actions', 
     headerName: 'Actions', 
@@ -66,4 +66,6 @@ const columns: GridColDef[] = [
     ],
     flex: 1,
   },
-];
\ No newline at end of file
+];
+
+export default ContributorPortal;
\ No newline at end of file
diff --git a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ReviewDataset.tsx b/strudel-taskflows/src/pages/contribute-data/review.tsx
similarity index 88%
rename from strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ReviewDataset.tsx
rename to strudel-taskflows/src/pages/contribute-data/review.tsx
index ab57e18f..23774966 100644
--- a/strudel-cookiecutter/contribute-data/{@cookiecutter.name@}/ReviewDataset.tsx
+++ b/strudel-taskflows/src/pages/contribute-data/review.tsx
@@ -2,17 +2,18 @@ import CloseIcon from '@mui/icons-material/Close';
 import { Alert, Box, Button, Collapse, Container, Grid, IconButton, Link, Stack, Typography } from '@mui/material';
 import React, { useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { DatasetView } from './DatasetView';
-import { ValidationChecks } from './ValidationChecks';
-import { useContributeData } from './context/ContextProvider';
-import { finishChecks, runChecks } from './context/actions';
+import { DatasetView } from './_components/DatasetView';
+import { ValidationChecks } from './_components/ValidationChecks';
+import { useContributeData } from './_context/ContextProvider';
+import { finishChecks, runChecks } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Page to review a new dataset before submitting it in the contribute-data Task Flow.
  * Users can see read-only data in the `<DatasetView>`, go back to editing, or run checks 
  * against their uploaded dataset and see validation in the `<ValidationChecks>` component.
  */
-export const ReviewDataset: React.FC = () => {
+const ReviewDataset: React.FC = () => {
   const [alertOpen, setAlertOpen] = useState(true);
   const { state, dispatch } = useContributeData();
 
@@ -65,7 +66,7 @@ export const ReviewDataset: React.FC = () => {
           <Stack direction="row" justifyContent="space-between">
             <Stack>
               <Typography variant="h6" component="h1">
-                {@ cookiecutter.pages.review.pageTitle @}
+                {taskflow.pages.review.title}
               </Typography>
             </Stack>
             <Stack direction="row">
@@ -110,4 +111,6 @@ export const ReviewDataset: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+export default ReviewDataset;
\ No newline at end of file
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataDetailPage.tsx b/strudel-taskflows/src/pages/explore-data/[id].tsx
similarity index 86%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataDetailPage.tsx
rename to strudel-taskflows/src/pages/explore-data/[id].tsx
index b240716b..a5a136e8 100644
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataDetailPage.tsx
+++ b/strudel-taskflows/src/pages/explore-data/[id].tsx
@@ -2,19 +2,19 @@ import { Box, Container, Paper, Stack, Typography } from '@mui/material';
 import React from 'react';
 import { useParams } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
-import { useExploreData } from './context/ContextProvider';
+import { useExploreData } from './_context/ContextProvider';
 
 /**
  * Work in Progress:
  * 
  * Detail view for a selected row from the` <DataExplorer>` in the explore-data Task Flow.
  */
-export const DataDetailPage: React.FC = () => {
+const DataDetailPage: React.FC = () => {
   const {state, dispatch} = useExploreData();
   const params = useParams();
   const entity = state.data?.find((d) => {
-    if (params.datasetId) {
-      return d[state.dataIdField].toString() === params.datasetId.toString();
+    if (params.id) {
+      return d[state.dataIdField].toString() === params.id.toString();
     }
   });
   console.log(state);
@@ -61,4 +61,6 @@ export const DataDetailPage: React.FC = () => {
       </Container>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default DataDetailPage;
\ No newline at end of file
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataTablePanel.tsx b/strudel-taskflows/src/pages/explore-data/_components/DataTablePanel.tsx
similarity index 94%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataTablePanel.tsx
rename to strudel-taskflows/src/pages/explore-data/_components/DataTablePanel.tsx
index a24ca2af..8453cb59 100644
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/DataTablePanel.tsx
+++ b/strudel-taskflows/src/pages/explore-data/_components/DataTablePanel.tsx
@@ -3,8 +3,8 @@ import { Button, Paper, Stack, TextField, Typography } from '@mui/material';
 import { GridEventListener } from '@mui/x-data-grid';
 import React from 'react';
 import { DataGrid } from '@mui/x-data-grid';
-import { useExploreData } from './context/ContextProvider';
-import { setPreviewItem, setSearch } from './context/actions';
+import { useExploreData } from '../_context/ContextProvider';
+import { setPreviewItem, setSearch } from '../_context/actions';
 
 interface DataTablePanelProps {
   onToggleFiltersPanel: () => any
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/FiltersPanel.tsx b/strudel-taskflows/src/pages/explore-data/_components/FiltersPanel.tsx
similarity index 87%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/FiltersPanel.tsx
rename to strudel-taskflows/src/pages/explore-data/_components/FiltersPanel.tsx
index c451eca9..c46fa398 100644
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/FiltersPanel.tsx
+++ b/strudel-taskflows/src/pages/explore-data/_components/FiltersPanel.tsx
@@ -1,11 +1,11 @@
 import React from 'react';
-import { Filters } from '../../components/Filters';
-import { FilterField } from '../../components/FilterField';
-import { CheckboxList, CheckboxOption } from '../../components/CheckboxList';
-import { useExploreData } from './context/ContextProvider';
-import { StrudelSlider } from '../../components/StrudelSlider';
-import { setFilter } from './context/actions';
-import { FilterConfig, FilterOperator } from '../../types/filters.types';
+import { Filters } from '../../../components/Filters';
+import { FilterField } from '../../../components/FilterField';
+import { CheckboxList, CheckboxOption } from '../../../components/CheckboxList';
+import { useExploreData } from '../_context/ContextProvider';
+import { StrudelSlider } from '../../../components/StrudelSlider';
+import { setFilter } from '../_context/actions';
+import { FilterConfig, FilterOperator } from '../../../types/filters.types';
 import { Stack } from '@mui/material';
 import { DatePicker } from '@mui/x-date-pickers';
 
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/PreviewPanel.tsx b/strudel-taskflows/src/pages/explore-data/_components/PreviewPanel.tsx
similarity index 96%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/PreviewPanel.tsx
rename to strudel-taskflows/src/pages/explore-data/_components/PreviewPanel.tsx
index 45fc1093..ae564641 100644
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/PreviewPanel.tsx
+++ b/strudel-taskflows/src/pages/explore-data/_components/PreviewPanel.tsx
@@ -2,9 +2,9 @@ import React from 'react';
 import { Box, Button, IconButton, Link, Paper, Stack, Typography } from '@mui/material';
 import CloseIcon from '@mui/icons-material/Close';
 import { Link as RouterLink } from 'react-router-dom';
-import { LabelValueTable } from '../../components/LabelValueTable';
+import { LabelValueTable } from '../../../components/LabelValueTable';
 import { DataGrid } from '@mui/x-data-grid';
-import { useExploreData } from './context/ContextProvider';
+import { useExploreData } from '../_context/ContextProvider';
 
 interface PreviewPanelProps {
   onClose: () => any
diff --git a/strudel-taskflows/src/pages/explore-data/_config/taskflow.config.ts b/strudel-taskflows/src/pages/explore-data/_config/taskflow.config.ts
new file mode 100644
index 00000000..ed00a6b8
--- /dev/null
+++ b/strudel-taskflows/src/pages/explore-data/_config/taskflow.config.ts
@@ -0,0 +1,108 @@
+import { ExploreDataConfig } from "./taskflow.types";
+
+export const taskflow: ExploreDataConfig = {
+  data: {
+    items: {
+      source: "default/explore-data/genomes.tsv",
+      idField: "Proteome_ID"
+    }
+  },
+  pages: {
+    index: {
+      title: "Explore Data App Test",
+      description: "Description of this app section",
+      tableColumns: [
+        {
+          field: "Organism",
+          headerName: "Organism",
+          width: 200
+        },
+        {
+          field: "Common Name",
+          headerName: "Common Name",
+          width: 200
+        },
+        {
+          field: "Assembly",
+          headerName: "Assembly",
+          width: 150
+        },
+        {
+          field: "Data Usage Policy",
+          headerName: "Data Usage Policy",
+          width: 220
+        },
+        {
+          field: "Euk. BUSCO %",
+          headerName: "Euk. BUSCO %",
+          type: "number",
+          width: 200
+        },
+        {
+          field: "Emb. BUSCO %",
+          headerName: "Emb. BUSCO %",
+          type: "number",
+          width: 200
+        }
+      ],
+      tableFilters: [
+        {
+          field: "Assembly",
+          displayName: "Assembly",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "JGI",
+                value: "JGI"
+              },
+              {
+                label: "BYU",
+                value: "BYU"
+              },
+              {
+                label: "AGP",
+                value: "AGP"
+              }
+            ]
+          }
+        },
+        {
+          field: "Data Usage Policy",
+          displayName: "Data Usage Policy",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "restricted",
+                value: "restricted"
+              },
+              {
+                label: "unrestricted",
+                value: "unrestricted"
+              }
+            ]
+          }
+        },
+        {
+          field: "Euk. BUSCO %",
+          displayName: "Euk. BUSCO %",
+          filterType: "Slider",
+          props: {
+            min: 0,
+            max: 100
+          }
+        },
+        {
+          field: "Emb. BUSCO %",
+          displayName: "Emb. BUSCO %",
+          filterType: "Slider",
+          props: {
+            min: 0,
+            max: 100
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/explore-data/_config/taskflow.types.ts b/strudel-taskflows/src/pages/explore-data/_config/taskflow.types.ts
new file mode 100644
index 00000000..a8a22e23
--- /dev/null
+++ b/strudel-taskflows/src/pages/explore-data/_config/taskflow.types.ts
@@ -0,0 +1,33 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface ExploreDataConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: Record<string, any>,
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      tableColumns: GridColDef[],
+      tableFilters: {
+        field: string,
+        displayName: string,
+        filterType: 'CheckboxList' | 'Slider' | 'date range',
+        props?: object
+      }[]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/explore-data/_context/ContextProvider.tsx b/strudel-taskflows/src/pages/explore-data/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/explore-data/_context/ContextProvider.tsx
rename to strudel-taskflows/src/pages/explore-data/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/pages/explore-data/_context/actions.ts b/strudel-taskflows/src/pages/explore-data/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/pages/explore-data/_context/actions.ts
rename to strudel-taskflows/src/pages/explore-data/_context/actions.ts
diff --git a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/ExploreDataWrapper.tsx b/strudel-taskflows/src/pages/explore-data/_layout.tsx
similarity index 58%
rename from strudel-cookiecutter/explore-data/{@cookiecutter.name@}/ExploreDataWrapper.tsx
rename to strudel-taskflows/src/pages/explore-data/_layout.tsx
index e29d8bc6..aa60b508 100644
--- a/strudel-cookiecutter/explore-data/{@cookiecutter.name@}/ExploreDataWrapper.tsx
+++ b/strudel-taskflows/src/pages/explore-data/_layout.tsx
@@ -1,19 +1,19 @@
 import React from 'react';
-import { basename } from '../App';
 import { Box } from '@mui/material';
 import { Outlet } from 'react-router';
-import { ExploreDataProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { ExploreDataProvider } from './_context/ContextProvider';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
+import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the explore-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const ExploreDataWrapper: React.FC = () => {
-  const entities = useDataFromSource('{@ cookiecutter.data.main.table.dataSource @}', basename);
-        
+const ExploreDataLayout: React.FC = () => {
+  const entities = useDataFromSource(taskflow.data.items.source, basename);
+
   /**
    * Content to render on the page for this component
    */
@@ -25,13 +25,15 @@ export const ExploreDataWrapper: React.FC = () => {
       <Box>
         <ExploreDataProvider 
           data={entities} 
-          columns={definitions.columns.main.table} 
-          filters={definitions.filters.main} 
-          dataIdField='{@ cookiecutter.data.main.table.dataIdField @}'
+          columns={taskflow.pages.index.tableColumns} 
+          filters={taskflow.pages.index.tableFilters} 
+          dataIdField={taskflow.data.items.idField}
         >
           <Outlet />
         </ExploreDataProvider>
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default ExploreDataLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/explore-data/index.tsx b/strudel-taskflows/src/pages/explore-data/index.tsx
similarity index 89%
rename from strudel-demo-app/src/pages/explore-data/index.tsx
rename to strudel-taskflows/src/pages/explore-data/index.tsx
index ae34484c..d61c8c77 100644
--- a/strudel-demo-app/src/pages/explore-data/index.tsx
+++ b/strudel-taskflows/src/pages/explore-data/index.tsx
@@ -6,6 +6,7 @@ import { DataTablePanel } from './_components/DataTablePanel';
 import { PageHeader } from '../../components/PageHeader';
 import { useExploreData } from './_context/ContextProvider';
 import { setPreviewItem } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Main explorer page in the explore-data Task Flow.
@@ -34,10 +35,8 @@ const DataExplorer: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        // strudel-kit-variable-next-line
-        pageTitle="Genome Releases"
-        // strudel-kit-variable-next-line
-        description="All gene sets have been annotated with KOG, KEGG, ENZYME, Pathway and the InterPro family of protein analysis tools."
+        pageTitle={taskflow.pages.index.title}
+        description={taskflow.pages.index.description}
         sx={{
           marginBottom: 1,
           padding: 2,
diff --git a/strudel-taskflows/src/pages/index.tsx b/strudel-taskflows/src/pages/index.tsx
new file mode 100644
index 00000000..ac8bda83
--- /dev/null
+++ b/strudel-taskflows/src/pages/index.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { Box, Container, Paper, Stack, Typography } from '@mui/material';
+import { TopBar } from '../components/TopBar';
+import { routes } from '@generouted/react-router';
+import { AppLink } from '../components/AppLink';
+
+/**
+ * TODO:
+ * - Add instructions about how to rig up the router for task flows
+ * - Add instructions for putting dataset into app
+ */
+const HomePage: React.FC = () => {
+  /**
+   * Sort routes alphabetically by path
+   */
+  routes[0].children?.sort((a, b) => {
+    const pathA = a.path || '';
+    const pathB = b.path || '';
+
+    if (pathA === 'playground' || pathA < pathB) {
+      return -1;
+    } else if (pathA > pathB) {
+      return 1;
+    } else {
+      return 0;
+    }
+  });
+
+  return (
+    <Box>
+      <TopBar />
+      <Container
+        maxWidth="md"
+        sx={{
+          marginTop: 3,
+          marginBottom: 3,
+        }}
+      >
+        <Stack>
+          <Paper
+            sx={{
+              padding: 2
+            }}
+          >
+            <Typography variant="h5" component="h1" fontWeight="bold">Welcome to your app built with STRUDEL!</Typography>
+          </Paper>
+          <Paper
+            sx={{
+              padding: 2
+            }}
+          >
+            <Stack>
+              <Typography variant="h6">Registered Pages</Typography>
+              {routes[0].children?.map((route) => {
+                if (route.path !== '/' && route.path !== '*') {
+                  return (
+                    <Box key={route.path}>
+                      <AppLink to={route.path || ''}>{route.path}</AppLink>
+                    </Box>
+                  )
+                }
+              })}
+            </Stack>
+          </Paper>
+        </Stack>
+      </Container>
+    </Box>
+  )
+}
+
+export default HomePage;
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/monitor-activities/_config/taskflow.config.ts b/strudel-taskflows/src/pages/monitor-activities/_config/taskflow.config.ts
new file mode 100644
index 00000000..ef9e387d
--- /dev/null
+++ b/strudel-taskflows/src/pages/monitor-activities/_config/taskflow.config.ts
@@ -0,0 +1,14 @@
+import { MonitorActivitiesConfig } from "./taskflow.types";
+
+export const taskflow: MonitorActivitiesConfig = {
+  data: {
+    items: {
+      source: "default/monitor-activities/experiments.json",
+      idField: "id"
+    },
+    detail: {
+      source: "default/monitor-activities/experiment_detail.json",
+      idField: "id"
+    }
+  },
+}
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/monitor-activities/_config/taskflow.types.ts b/strudel-taskflows/src/pages/monitor-activities/_config/taskflow.types.ts
new file mode 100644
index 00000000..cb294593
--- /dev/null
+++ b/strudel-taskflows/src/pages/monitor-activities/_config/taskflow.types.ts
@@ -0,0 +1,18 @@
+/**
+ * Type definitions for the Monitor Activities Task Flow config object
+ */
+export interface MonitorActivitiesConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: any
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  pages?: any
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/MonitorActivitiesWrapper.tsx b/strudel-taskflows/src/pages/monitor-activities/_layout.tsx
similarity index 82%
rename from strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/MonitorActivitiesWrapper.tsx
rename to strudel-taskflows/src/pages/monitor-activities/_layout.tsx
index 1c951078..b99a71f5 100644
--- a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/MonitorActivitiesWrapper.tsx
+++ b/strudel-taskflows/src/pages/monitor-activities/_layout.tsx
@@ -7,7 +7,7 @@ import { TopBar } from '../../components/TopBar';
  * Top-level wrapper for the monitor-activities Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const MonitorActivitiesWrapper: React.FC = () => {
+const MonitorActivitiesLayout: React.FC = () => {
   return (
     <Box>
       <Box sx={{ flexGrow: 1 }}>
@@ -18,4 +18,6 @@ export const MonitorActivitiesWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default MonitorActivitiesLayout;
\ No newline at end of file
diff --git a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityCalendar.tsx b/strudel-taskflows/src/pages/monitor-activities/calendar.tsx
similarity index 87%
rename from strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityCalendar.tsx
rename to strudel-taskflows/src/pages/monitor-activities/calendar.tsx
index 883c37f2..3bfdeb24 100644
--- a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityCalendar.tsx
+++ b/strudel-taskflows/src/pages/monitor-activities/calendar.tsx
@@ -9,7 +9,7 @@ import { Link as RouterLink } from 'react-router-dom';
  * 
  * Page to see all activities by day in a calendar view in the monitor-activities Task Flow.
  */
-export const ActivityCalendar: React.FC = () => {
+const ActivityCalendar: React.FC = () => {
   return (
     <Container
       maxWidth="md"
@@ -25,9 +25,11 @@ export const ActivityCalendar: React.FC = () => {
       <LocalizationProvider dateAdapter={AdapterDayjs}>
         <DateCalendar readOnly />
       </LocalizationProvider>
-      <Link component={RouterLink} to="list">
+      <Link component={RouterLink} to="../">
         List
       </Link>
     </Container>
   )
-}
\ No newline at end of file
+}
+
+export default ActivityCalendar;
\ No newline at end of file
diff --git a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityDetail.tsx b/strudel-taskflows/src/pages/monitor-activities/detail.tsx
similarity index 93%
rename from strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityDetail.tsx
rename to strudel-taskflows/src/pages/monitor-activities/detail.tsx
index a7f7bf93..62fff53b 100644
--- a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityDetail.tsx
+++ b/strudel-taskflows/src/pages/monitor-activities/detail.tsx
@@ -8,14 +8,15 @@ import Plot from 'react-plotly.js';
 import { Link as RouterLink } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
+import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Detail view of the selected activity from `<ActivityList>` in monitor-activities Task Flow.
  * The two components are not currently hooked together.
  */
-export const ActivityDetail: React.FC = () => {
-  const experiment = useDataFromSource('default/monitor-activities/experiment_detail.json', basename);
+const ActivityDetail: React.FC = () => {
+  const experiment = useDataFromSource(taskflow.data.detail.source, basename);
 
   const getNoteRows = (notes: any[]) => {
     return notes.map((note) => {
@@ -38,7 +39,7 @@ export const ActivityDetail: React.FC = () => {
     >
       <Stack>
         <Stack direction="row" alignItems="center">
-          <Link component={RouterLink} to="/monitor-activities/list">
+          <Link component={RouterLink} to="../">
             <IconButton>
               <ArrowBackIcon />
             </IconButton>
@@ -156,4 +157,6 @@ const columns: GridColDef[] = [
     type: 'number',
     width: 200 
   }
-];
\ No newline at end of file
+];
+
+export default ActivityDetail;
\ No newline at end of file
diff --git a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityList.tsx b/strudel-taskflows/src/pages/monitor-activities/index.tsx
similarity index 84%
rename from strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityList.tsx
rename to strudel-taskflows/src/pages/monitor-activities/index.tsx
index 854d7d26..9624a4a1 100644
--- a/strudel-cookiecutter/monitor-activities/{@cookiecutter.name@}/ActivityList.tsx
+++ b/strudel-taskflows/src/pages/monitor-activities/index.tsx
@@ -4,16 +4,17 @@ import dayjs from 'dayjs';
 import React from 'react';
 import { useNavigate } from 'react-router-dom';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
+import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * List view of all activities in the monitor-activites Task Flow.
  */
-export const ActivityList: React.FC = () => {
-  const experiments = useDataFromSource('default/monitor-activities/experiments.json', basename);
+const ActivityList: React.FC = () => {
+  const experiments = useDataFromSource(taskflow.data.items.source, basename);
   const navigate = useNavigate();
-
-    /**
+  
+  /**
    * Content to render on the page for this component
    */
   return (
@@ -27,7 +28,7 @@ export const ActivityList: React.FC = () => {
       <Stack>
         <Stack direction="row" justifyContent="space-between" alignItems="center">
           <Typography variant="h6" component="h1">
-            Experiments
+            Experiments test
           </Typography>
         </Stack>
         <Paper>
@@ -40,7 +41,7 @@ export const ActivityList: React.FC = () => {
                 sortModel: [{ field: 'start_time', sort: 'desc' }],
               },
             }}
-            onRowClick={() => navigate('list/detail')}
+            onRowClick={() => navigate('detail')}
             disableColumnSelector
             disableRowSelectionOnClick
           />
@@ -69,7 +70,6 @@ const columns: GridColDef[] = [
   { 
     field: 'end_time', 
     headerName: 'End Time', 
-    type: 'datetime',
     width: 200 
   },
   { 
@@ -77,4 +77,6 @@ const columns: GridColDef[] = [
     headerName: 'Status', 
     width: 200 
   }
-];
\ No newline at end of file
+];
+
+export default ActivityList;
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/playground/index.tsx b/strudel-taskflows/src/pages/playground/index.tsx
new file mode 100644
index 00000000..06ebe08c
--- /dev/null
+++ b/strudel-taskflows/src/pages/playground/index.tsx
@@ -0,0 +1,44 @@
+import { Button, Container, Stack } from '@mui/material';
+import React, { useState } from 'react';
+
+/**
+ * A blank canvas to test out content and components
+ */
+const Playground: React.FC = () => {
+  const [count, setCount] = useState(0);
+
+  const handleIncrement = () => {
+    setCount(count + 1);
+  }
+
+  return (
+    <Container
+      maxWidth="xl"
+      /**
+       * Style STRUDEL and MUI components using the `sx` prop.
+       */
+      sx={{
+        marginBottom: 3,
+        marginTop: 3,
+      }}
+    >
+      {/** 
+       * Add your own components here!
+       * See MUI's component library: https://mui.com/material-ui/all-components/
+       */}
+      <h1>Playground</h1>
+      <Stack 
+        direction="row" 
+        spacing={2}
+        sx={{
+          alignItems: 'center'
+        }}
+      >
+        <Button variant="contained" onClick={handleIncrement}>Increment</Button>
+        <p>{count}</p>
+      </Stack>
+    </Container>
+  );
+}
+
+export default Playground;
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Scenario.tsx b/strudel-taskflows/src/pages/run-computation/[id]/_layout.tsx
similarity index 87%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Scenario.tsx
rename to strudel-taskflows/src/pages/run-computation/[id]/_layout.tsx
index 8fff0d26..f943084c 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Scenario.tsx
+++ b/strudel-taskflows/src/pages/run-computation/[id]/_layout.tsx
@@ -2,13 +2,14 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
 import { AppBar, Box, Breadcrumbs, Link, Stack, Toolbar, Typography } from '@mui/material';
 import React from 'react';
 import { Outlet, Link as RouterLink } from 'react-router-dom';
+import { taskflow } from '../_config/taskflow.config';
   
 /**
  * Wrapper component for the steps involved in a computational run.
  * This goes from `<DataInputs>` to `<Settings>` to `<RunningComputation>` to `<Results>`.
  * These inner steps are rendered by the `<Outlet>` component.
  */
-export const Scenario: React.FC = () => {
+const ComputationLayout: React.FC = () => {
   return (
     <Stack spacing={0} height="100vh">
       <Box>
@@ -25,7 +26,7 @@ export const Scenario: React.FC = () => {
             <Stack direction="row" alignItems="center" sx={{ flexGrow: 1 }}>
               <Breadcrumbs aria-label="breadcrumb" separator={<ChevronRightIcon fontSize="small" />}>
                 <Link underline="hover" color="inherit" to=".." component={RouterLink}>
-                  {@ cookiecutter.pages.list.pageTitle @}
+                  {taskflow.properties.itemName} List
                 </Link>
                 <Typography color="text.primary">Optimization Scenario 1</Typography>
               </Breadcrumbs>
@@ -36,4 +37,6 @@ export const Scenario: React.FC = () => {
       <Outlet />
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default ComputationLayout;
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/DataInputs.tsx b/strudel-taskflows/src/pages/run-computation/[id]/data-inputs.tsx
similarity index 86%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/DataInputs.tsx
rename to strudel-taskflows/src/pages/run-computation/[id]/data-inputs.tsx
index b4b640ff..ea98ea55 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/DataInputs.tsx
+++ b/strudel-taskflows/src/pages/run-computation/[id]/data-inputs.tsx
@@ -2,19 +2,20 @@ import { Box, Button, Container, Link, Paper, Stack, Step, StepLabel, Stepper, T
 import React, { useEffect } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
-import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { useRunComputation } from './context/ContextProvider';
-import { setInputsTableData } from './context/actions';
+import { useDataFromSource } from '../../../utils/useDataFromSource';
+import { basename } from '../../../main';
+import { useRunComputation } from '../_context/ContextProvider';
+import { setInputsTableData } from '../_context/actions';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Page to display input data after creating or selecting an item from 
  * the `<ComputationsList>` page in the run-computation Task Flow.
  * Table columns are configured in `definitions.inputs.table.columns`
  */
-export const DataInputs: React.FC = () => {
+const DataInputsPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
-  const inputsData = useDataFromSource('{@ cookiecutter.data.inputs.table.dataSource @}', basename);
+  const inputsData = useDataFromSource(taskflow.data.inputs.source, basename);
   
   /**
    * Set data for the inputs table when the data loads
@@ -42,21 +43,21 @@ export const DataInputs: React.FC = () => {
           <Step key="Data Inputs">
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.inputs.pageTitle @}
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Optimization Settings">
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.settings.pageTitle @}
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Results">
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.results.pageTitle @}
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -150,9 +151,11 @@ export const DataInputs: React.FC = () => {
         }}
       >
         <Link component={RouterLink} to="../settings">
-          <Button variant="contained">Continue to {@ cookiecutter.pages.settings.pageTitle @}</Button>
+          <Button variant="contained">Continue to {taskflow.pages.settings.title}</Button>
         </Link>
       </Box>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default DataInputsPage;
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Results.tsx b/strudel-taskflows/src/pages/run-computation/[id]/results.tsx
similarity index 86%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Results.tsx
rename to strudel-taskflows/src/pages/run-computation/[id]/results.tsx
index dbcc1d2e..81f89c31 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Results.tsx
+++ b/strudel-taskflows/src/pages/run-computation/[id]/results.tsx
@@ -1,23 +1,24 @@
 import { Box, Button, Container, Grid, Link, Paper, Stack, Step, StepLabel, Stepper, Typography } from '@mui/material';
+import { DataGrid } from '@mui/x-data-grid';
 import React, { useEffect } from 'react';
 import Plot from 'react-plotly.js';
 import { Link as RouterLink } from 'react-router-dom';
-import { DataGrid } from '@mui/x-data-grid';
-import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { useRunComputation } from './context/ContextProvider';
-import { setResultsBarChartData, setResultsLineChartData, setResultsTableData } from './context/actions';
+import { useDataFromSource } from '../../../utils/useDataFromSource';
+import { basename } from '../../../main';
+import { useRunComputation } from '../_context/ContextProvider';
+import { setResultsBarChartData, setResultsLineChartData, setResultsTableData } from '../_context/actions';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Results page to display after a computation completes in the run-computation Task Flow.
  * Displays a line chart, bar chart, and table of results from the computation.
  */
-export const Results: React.FC = () => {
+const ResultsPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
-  const tableData = useDataFromSource('{@ cookiecutter.data.results.table.dataSource @}', basename);
-  const lineData = useDataFromSource('{@ cookiecutter.data.results.lineChart.dataSource @}', basename);
-  const barData = useDataFromSource('{@ cookiecutter.data.results.barChart.dataSource @}', basename);
-  
+  const tableData = useDataFromSource(taskflow.data.results.source, basename);
+  const lineData = useDataFromSource(taskflow.data.lineChart.source, basename);
+  const barData = useDataFromSource(taskflow.data.barChart.source, basename);
+
   /**
    * Set data for the results table when the data loads
    */
@@ -62,21 +63,21 @@ export const Results: React.FC = () => {
           <Step key="Data Inputs">
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.inputs.pageTitle @}
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Optimization Settings">
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.settings.pageTitle @}
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
           <Step key="Results">
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.results.pageTitle @}
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -189,9 +190,11 @@ export const Results: React.FC = () => {
         }}
       >
         <Link component={RouterLink} to="../settings">
-          <Button variant="contained">Back to {@ cookiecutter.pages.settings.pageTitle @}</Button>
+          <Button variant="contained">Back to {taskflow.pages.settings.title}</Button>
         </Link>
       </Box>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default ResultsPage;
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/RunningComputation.tsx b/strudel-taskflows/src/pages/run-computation/[id]/running.tsx
similarity index 82%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/RunningComputation.tsx
rename to strudel-taskflows/src/pages/run-computation/[id]/running.tsx
index 8aa849b4..e2c33089 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/RunningComputation.tsx
+++ b/strudel-taskflows/src/pages/run-computation/[id]/running.tsx
@@ -1,26 +1,24 @@
 import { Box, Button, Container, LinearProgress, Link, Paper, Stack, Step, StepLabel, Stepper, Typography } from '@mui/material';
 import React, { useEffect, useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { useInterval } from '../../utils/useInterval';
+import { useInterval } from '../../../utils/useInterval';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Page to show while a computation is running and after it completes.
  * Continuing after completion, this page takes users to the `<Results>` page.
  */
-export const RunningComputation: React.FC = () => {
+const RunningComputationPage: React.FC = () => {
   const [running, setRunning] = useState(true);
-  const [progress, setProgress] = useState(0);
 
   /**
    * Simulate the time it takes to run the optimization
    */
-  useInterval(() => {
-    if (progress < 100) {
-      setProgress(progress + 10);
-    } else {
+  useEffect(() => {
+    setTimeout(() => {
       setRunning(false);
-    }
-  }, 500);
+    }, 2000)
+  })
 
   /**
    * Content to render on the page for this component
@@ -36,24 +34,24 @@ export const RunningComputation: React.FC = () => {
         }}
       >
         <Stepper activeStep={1} sx={{ maxWidth: 850 }}>
-          <Step key="Data Inputs">
+          <Step key={taskflow.pages.dataInputs.title}>
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.inputs.pageTitle @}
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Optimization Settings">
+          <Step key={taskflow.pages.settings.title}>
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.settings.pageTitle @}
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Results">
+          <Step key={taskflow.pages.results.title}>
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.results.pageTitle @}
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -75,7 +73,7 @@ export const RunningComputation: React.FC = () => {
                 <Typography>This could take several minutes.</Typography>
                 <Typography>You may leave this page and return later. Your progress will not be affected.</Typography>
               </Box>
-              <LinearProgress variant="determinate" value={progress} sx={{ height: 10 }} />
+              <LinearProgress variant="indeterminate" sx={{ height: 10 }} />
               <Typography color="neutral.dark">
                 Started 05/24/2023 12:32:33
               </Typography>
@@ -101,4 +99,6 @@ export const RunningComputation: React.FC = () => {
       </Container>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default RunningComputationPage;
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Settings.tsx b/strudel-taskflows/src/pages/run-computation/[id]/settings.tsx
similarity index 90%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Settings.tsx
rename to strudel-taskflows/src/pages/run-computation/[id]/settings.tsx
index b90f910d..426e5acc 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/Settings.tsx
+++ b/strudel-taskflows/src/pages/run-computation/[id]/settings.tsx
@@ -1,13 +1,14 @@
 import { Box, Button, Container, FormControl, Grid, Link, MenuItem, Paper, Select, Stack, Step, StepLabel, Stepper, TextField, Typography } from '@mui/material';
 import React, { useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
+import { taskflow } from '../_config/taskflow.config';
 
 /**
  * Page to configure settings for a computational run.
  * Completing and submitting the form takes users to the 
  * `<RunningComputation>` component.
  */
-export const Settings: React.FC = () => {
+const SettingsPage: React.FC = () => {
   const [showAdvanced, setShowAdvanced] = useState(false);
 
   const handleAdvancedToggle = () => {
@@ -28,24 +29,24 @@ export const Settings: React.FC = () => {
         }}
       >
         <Stepper activeStep={1} sx={{ maxWidth: 850 }}>
-          <Step key="Data Inputs">
+          <Step key={taskflow.pages.dataInputs.title}>
             <StepLabel>
               <Link component={RouterLink} to="../data-inputs" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.inputs.pageTitle @}
+                {taskflow.pages.dataInputs.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Optimization Settings">
+          <Step key={taskflow.pages.settings.title}>
             <StepLabel>
               <Link component={RouterLink} to="../settings" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.settings.pageTitle @}
+                {taskflow.pages.settings.title}
               </Link>
             </StepLabel>
           </Step>
-          <Step key="Results">
+          <Step key={taskflow.pages.results.title}>
             <StepLabel>
               <Link component={RouterLink} to="../results" sx={{ color: 'inherit', textDecoration: 'none' }}>
-                {@ cookiecutter.pages.results.pageTitle @}
+                {taskflow.pages.results.title}
               </Link>
             </StepLabel>
           </Step>
@@ -60,7 +61,7 @@ export const Settings: React.FC = () => {
         <Paper sx={{ padding: 3 }}>
           <Stack>
             <Typography variant="h6" component="h2">
-              {@ cookiecutter.pages.settings.pageTitle @}
+              {taskflow.pages.settings.title}
             </Typography>
             <Grid container rowSpacing={2} alignItems="center">
               <Grid item md={3}>
@@ -128,7 +129,7 @@ export const Settings: React.FC = () => {
             <Box textAlign="right">
               <Link component={RouterLink} to="../running">
                 <Button variant="contained" sx={{ marginTop: 4 }}>
-                  Run optimization
+                  Run {taskflow.properties.itemName}
                 </Button>
               </Link>
             </Box>
@@ -137,4 +138,6 @@ export const Settings: React.FC = () => {
       </Container>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default SettingsPage;
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/NewScenarioModal.tsx b/strudel-taskflows/src/pages/run-computation/_components/NewScenarioModal.tsx
similarity index 90%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/NewScenarioModal.tsx
rename to strudel-taskflows/src/pages/run-computation/_components/NewScenarioModal.tsx
index 25c45fd1..7a2c9347 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/NewScenarioModal.tsx
+++ b/strudel-taskflows/src/pages/run-computation/_components/NewScenarioModal.tsx
@@ -1,10 +1,7 @@
-import ContentCopyIcon from '@mui/icons-material/ContentCopy';
-import DeleteIcon from '@mui/icons-material/Delete';
-import EditIcon from '@mui/icons-material/Edit';
 import { Box, Button, FormControl, InputLabel, Link, MenuItem, Modal, Select, Stack, TextField, Typography } from '@mui/material';
-import { GridActionsCellItem, GridColDef, GridRowParams } from '@mui/x-data-grid';
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
+import { taskflow } from '../_config/taskflow.config';
 
 interface Props {
   modalOpen: boolean;
@@ -50,7 +47,8 @@ export const NewScenarioModal: React.FC<Props> = ({
         }}
       >
         <Typography id="modal-modal-title" variant="h6" component="h2">
-          New {@ cookiecutter.computeItem @}
+          {/* strudel-kit-variable-next-line */}
+          New Analysis {taskflow.properties.itemName}
         </Typography>
         <FormControl fullWidth>
           <InputLabel id="analysis-type-select-label">Analysis Type</InputLabel>
diff --git a/strudel-taskflows/src/pages/run-computation/_config/taskflow.config.ts b/strudel-taskflows/src/pages/run-computation/_config/taskflow.config.ts
new file mode 100644
index 00000000..bdc1eef5
--- /dev/null
+++ b/strudel-taskflows/src/pages/run-computation/_config/taskflow.config.ts
@@ -0,0 +1,123 @@
+import { RunComputationConfig } from "./taskflow.types";
+
+export const taskflow: RunComputationConfig = {
+  properties: {
+    itemName: "scenario",
+    itemNamePlural: "scenarios",
+  },
+  data: {
+    items: {
+      source: "default/run-computation/list.json",
+      idField: "id"
+    },
+    inputs: {
+      source: "default/run-computation/inputs.json",
+      idField: ""
+    },
+    lineChart: {
+      source: "default/run-computation/results_line_chart.json"
+    },
+    barChart: {
+      source: "default/run-computation/results_bar_chart.json"
+    },
+    results: {
+      source: "default/run-computation/results_table.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Scenario List",
+      description: "Scenarios represent a set of analysis inputs / parameters / settings and the results of that analysis.",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Scenario Name",
+          width: 200
+        },
+        {
+          field: "analysisType",
+          headerName: "Analysis Type",
+          width: 200
+        },
+        {
+          field: "createdDate",
+          headerName: "Date Created",
+          width: 200
+        },
+        {
+          field: "status",
+          headerName: "Status",
+          width: 200
+        }
+      ]
+    },
+    dataInputs: {
+      title: "Data Inputs",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Unit Name",
+          width: 200
+        },
+        {
+          field: "unitType",
+          headerName: "Unit Type",
+          width: 200
+        },
+        {
+          field: "constraints",
+          headerName: "Constraints",
+          width: 200
+        },
+        {
+          field: "lowerBound",
+          headerName: "Lower Bound",
+          width: 200,
+          type: "number"
+        },
+        {
+          field: "upperBound",
+          headerName: "Upper Bound",
+          width: 200,
+          type: "number"
+        }
+      ]
+    },
+    settings: {
+      title: "Optimization Settings"
+    },
+    results: {
+      title: "Results",
+      tableColumns: [
+        {
+          field: "name",
+          headerName: "Unit Name",
+          width: 200
+        },
+        {
+          field: "unitType",
+          headerName: "Unit Type",
+          width: 200
+        },
+        {
+          field: "constraints",
+          headerName: "Constraints",
+          width: 200
+        },
+        {
+          field: "lowerBound",
+          headerName: "Lower Bound",
+          width: 200,
+          type: "number"
+        },
+        {
+          field: "upperBound",
+          headerName: "Upper Bound",
+          width: 200,
+          type: "number"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/run-computation/_config/taskflow.types.ts b/strudel-taskflows/src/pages/run-computation/_config/taskflow.types.ts
new file mode 100644
index 00000000..dbdc4812
--- /dev/null
+++ b/strudel-taskflows/src/pages/run-computation/_config/taskflow.types.ts
@@ -0,0 +1,58 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface RunComputationConfig {
+  /** Attributes that are used across the Task Flow */
+  properties: {
+    itemName: string,
+    itemNamePlural: string
+  },
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    inputs: {
+      source: string,
+      idField: string
+    },
+    results: {
+      source: string,
+      idField: string
+    },
+    barChart: {
+      source: string,
+      idField?: string
+    },
+    lineChart: {
+      source: string,
+      idField?: string
+    },
+    [key: string]: {
+      source: string,
+      idField?: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      tableColumns: GridColDef[]
+    },
+    dataInputs: {
+      title: string,
+      tableColumns: GridColDef[]
+    },
+    settings: {
+      title: string,
+    },
+    results: {
+      title: string,
+      tableColumns: GridColDef[]
+    },
+
+  }
+}
\ No newline at end of file
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/context/ContextProvider.tsx b/strudel-taskflows/src/pages/run-computation/_context/ContextProvider.tsx
similarity index 98%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/context/ContextProvider.tsx
rename to strudel-taskflows/src/pages/run-computation/_context/ContextProvider.tsx
index 9f0e5d4a..4b275864 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/context/ContextProvider.tsx
+++ b/strudel-taskflows/src/pages/run-computation/_context/ContextProvider.tsx
@@ -1,4 +1,4 @@
-import React, { useContext, useEffect, useReducer } from 'react';
+import React, { useContext, useReducer } from 'react';
 import { RunComputationAction, RunComputationActionType } from './actions';
 
 export interface RunComputationState {
diff --git a/strudel-demo-app/src/pages/run-computation/_context/actions.ts b/strudel-taskflows/src/pages/run-computation/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/pages/run-computation/_context/actions.ts
rename to strudel-taskflows/src/pages/run-computation/_context/actions.ts
diff --git a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/RunComputationWrapper.tsx b/strudel-taskflows/src/pages/run-computation/_layout.tsx
similarity index 70%
rename from strudel-cookiecutter/run-computation/{@cookiecutter.name@}/RunComputationWrapper.tsx
rename to strudel-taskflows/src/pages/run-computation/_layout.tsx
index 695929d2..9e918432 100644
--- a/strudel-cookiecutter/run-computation/{@cookiecutter.name@}/RunComputationWrapper.tsx
+++ b/strudel-taskflows/src/pages/run-computation/_layout.tsx
@@ -7,16 +7,16 @@ import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { RunComputationProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { basename } from '../../main';
+import { RunComputationProvider } from './_context/ContextProvider';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the run-computation Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const RunComputationWrapper: React.FC = () => {
-  const listItems = useDataFromSource('{@ cookiecutter.data.list.table.dataSource @}', basename)
+const RunComputationLayout: React.FC = () => {
+  const listItems = useDataFromSource(taskflow.data.items.source, basename)
 
   /**
    * Content to render on the page for this component
@@ -31,9 +31,9 @@ export const RunComputationWrapper: React.FC = () => {
           list={{
             table: {
               data: listItems,
-              dataIdField: '{@ cookiecutter.data.list.table.dataIdField @}',
+              dataIdField: taskflow.data.items.idField,
               columns: [
-                ...definitions.list.table.columns,
+                ...taskflow.pages.index.tableColumns,
                 { 
                   field: 'actions', 
                   headerName: 'Actions', 
@@ -51,15 +51,15 @@ export const RunComputationWrapper: React.FC = () => {
           inputs={{
             table: {
               data: [],
-              dataIdField: '{@ cookiecutter.data.inputs.table.dataIdField @}',
-              columns: definitions.inputs.table.columns,
+              dataIdField: taskflow.data.inputs.idField,
+              columns: taskflow.pages.dataInputs.tableColumns,
             }
           }}
           results={{
             table: {
               data: [],
-              dataIdField: '{@ cookiecutter.data.results.table.dataIdField @}',
-              columns: definitions.results.table.columns,
+              dataIdField: taskflow.data.results.idField,
+              columns: taskflow.pages.results.tableColumns,
             }
           }}
         >
@@ -68,4 +68,6 @@ export const RunComputationWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default RunComputationLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/run-computation/index.tsx b/strudel-taskflows/src/pages/run-computation/index.tsx
similarity index 85%
rename from strudel-demo-app/src/pages/run-computation/index.tsx
rename to strudel-taskflows/src/pages/run-computation/index.tsx
index 11d8a3d2..519296e4 100644
--- a/strudel-demo-app/src/pages/run-computation/index.tsx
+++ b/strudel-taskflows/src/pages/run-computation/index.tsx
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
 import { DataGrid } from '@mui/x-data-grid';
 import { NewScenarioModal } from './_components/NewScenarioModal';
 import { useRunComputation } from './_context/ContextProvider';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * List page for all compuation runs in the run-computation Task Flow.
@@ -42,23 +43,20 @@ export const ListPage: React.FC = () => {
                 variant="h6" 
                 component="h1" 
               >
-                {/* strudel-kit-variable-next-line */}
-                Scenario List
+                {taskflow.pages.index.title}
               </Typography>
               <Typography 
                 variant="subtitle1" 
                 component="p" 
               >
-                {/* strudel-kit-variable-next-line */}
-                Scenarios represent a set of analysis inputs / parameters / settings and the results of that analysis. 
+                {taskflow.pages.index.description}
               </Typography>
             </Box>
             <Button
               variant="contained"
               onClick={handleNewScenario}
             >
-              {/* strudel-kit-variable-next-line */}
-              New Scenario
+              New {taskflow.properties.itemName}
             </Button>
             <NewScenarioModal modalOpen={modalOpen} setModalOpen={setModalOpen} />
           </Stack>
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DatasetDetail.tsx b/strudel-taskflows/src/pages/search-data-repositories/[id].tsx
similarity index 95%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DatasetDetail.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/[id].tsx
index 65d180f2..55e685e6 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DatasetDetail.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/[id].tsx
@@ -2,23 +2,23 @@ import DownloadIcon from '@mui/icons-material/Download';
 import VisibilityIcon from '@mui/icons-material/Visibility';
 import { TabContext, TabList, TabPanel } from '@mui/lab';
 import { Box, Button, Container, Grid, Link, Paper, Stack, Tab, Typography } from '@mui/material';
-import { DataGrid, GridActionsCellItem, GridRowParams } from '@mui/x-data-grid';
+import { DataGrid, GridActionsCellItem, GridColDef, GridRowParams } from '@mui/x-data-grid';
 import React from 'react';
 import { useParams } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
 import { PageHeader } from '../../components/PageHeader';
-import { useSearchDataRepositories } from './context/ContextProvider';
+import { useSearchDataRepositories } from './_context/ContextProvider';
 
 /**
  * Detail page for a particular dataset. This is accessed by clicking on a 
  * card title or the link in the `<PreviewPanel>`.
  */
-export const DatasetDetail: React.FC = () => {
+const DatasetDetail: React.FC = () => {
   const {state, dispatch} = useSearchDataRepositories();
   const params = useParams();
   const dataset = state.data?.find((d) => {
-    if (params.datasetId) {
-      return d.id === parseInt(params.datasetId);
+    if (params.id) {
+      return d.id === parseInt(params.id);
     }
   });
   const datasetTitle = dataset ? dataset['title'] : 'Not Found';
@@ -184,7 +184,7 @@ export const DatasetDetail: React.FC = () => {
 /**
  * Attached files data configured in-file for prototyping
  */
-const attachedFilesColumns = [
+const attachedFilesColumns: GridColDef[] = [
   { 
     field: 'file_name', 
     headerName: 'File Name',
@@ -210,4 +210,6 @@ const attachedFilesColumns = [
     ],
     flex: 1
   },
-];
\ No newline at end of file
+];
+
+export default DatasetDetail;
\ No newline at end of file
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DataListCard.tsx b/strudel-taskflows/src/pages/search-data-repositories/_components/DataListCard.tsx
similarity index 79%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DataListCard.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/_components/DataListCard.tsx
index c0fef5e4..17a8c20a 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DataListCard.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/_components/DataListCard.tsx
@@ -2,8 +2,8 @@ import { Box, Link, Stack, Typography } from '@mui/material';
 import { blue } from '@mui/material/colors';
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setPreviewItem } from './context/actions';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
+import { setPreviewItem } from '../_context/actions';
 
 interface DataListCardProps {
   item: any;
@@ -61,20 +61,20 @@ export const DataListCard: React.FC<DataListCardProps> = ({ item }) => {
         {state.cardFields.content && (
           <Typography
             sx={{
-              '-webkit-box-orient': 'vertical',
-              '-webkit-line-clamp': '2',
+              WebkitBoxOrient: 'vertical',
+              WebkitLineClamp: '2',
               display: '-webkit-box',
               overflow: 'hidden'
             }}
           >
-            {item[state.cardFields.content]}
+            {item[state.cardFields.content]} test
           </Typography>
         )}
         {state.cardFields.tags && (
           <Typography
             sx={{
-              '-webkit-box-orient': 'vertical',
-              '-webkit-line-clamp': '1',
+              WebkitBoxOrient: 'vertical',
+              WebkitLineClamp: '1',
               display: '-webkit-box',
               fontStyle: 'italic',
               overflow: 'hidden'
@@ -82,9 +82,9 @@ export const DataListCard: React.FC<DataListCardProps> = ({ item }) => {
           >
             {item[state.cardFields.tags].map((tag: string, i: number) => {
               if (i < item['tags'].length -1) {
-                return <Typography component="span" sx={{ fontSize: 'small', marginRight: 0.5 }}>{tag},</Typography>
+                return <Typography key={`${tag}-${i}`} component="span" sx={{ fontSize: 'small', marginRight: 0.5 }}>{tag},</Typography>
               } else {
-                return <Typography component="span" sx={{ fontSize: 'small' }}>{tag}</Typography>
+                return <Typography key={`${tag}-${i}`} component="span" sx={{ fontSize: 'small' }}>{tag}</Typography>
               }
             })}
           </Typography>  
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DataListPanel.tsx b/strudel-taskflows/src/pages/search-data-repositories/_components/DataListPanel.tsx
similarity index 91%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DataListPanel.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/_components/DataListPanel.tsx
index 7e11db38..0de42ffa 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DataListPanel.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/_components/DataListPanel.tsx
@@ -3,8 +3,8 @@ import SortIcon from '@mui/icons-material/Sort';
 import { Box, Button, Pagination, Paper, Stack, TextField, Typography } from '@mui/material';
 import React from 'react';
 import { DataListCard } from './DataListCard';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setSearch } from './context/actions';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
+import { setSearch } from '../_context/actions';
 
 interface DataListPanelProps {
   onToggleFiltersPanel: () => any
@@ -64,7 +64,7 @@ export const DataListPanel: React.FC<DataListPanelProps> = (props) => {
       >
         <Stack flex={1}>
           {state.filteredData?.map((item, i) => (
-            <DataListCard item={item} />
+            <DataListCard key={item[state.dataIdField]} item={item} />
           ))}
         </Stack>
         {!state.previewItem && (
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/FiltersPanel.tsx b/strudel-taskflows/src/pages/search-data-repositories/_components/FiltersPanel.tsx
similarity index 87%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/FiltersPanel.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/_components/FiltersPanel.tsx
index 9ea787ba..41fcb531 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/FiltersPanel.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/_components/FiltersPanel.tsx
@@ -1,13 +1,13 @@
 import { Stack } from '@mui/material';
 import { DatePicker } from '@mui/x-date-pickers';
 import React from 'react';
-import { CheckboxList } from '../../components/CheckboxList';
-import { FilterField } from '../../components/FilterField';
-import { Filters } from '../../components/Filters';
-import { StrudelSlider } from '../../components/StrudelSlider';
-import { FilterConfig, FilterOperator } from '../../types/filters.types';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setFilter } from './context/actions';
+import { CheckboxList } from '../../../components/CheckboxList';
+import { FilterField } from '../../../components/FilterField';
+import { Filters } from '../../../components/Filters';
+import { StrudelSlider } from '../../../components/StrudelSlider';
+import { FilterConfig, FilterOperator } from '../../../types/filters.types';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
+import { setFilter } from '../_context/actions';
 
 interface FiltersPanelProps {
   onClose: () => any
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/PreviewPanel.tsx b/strudel-taskflows/src/pages/search-data-repositories/_components/PreviewPanel.tsx
similarity index 93%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/PreviewPanel.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/_components/PreviewPanel.tsx
index 56923cd9..06961f23 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/PreviewPanel.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/_components/PreviewPanel.tsx
@@ -2,9 +2,9 @@ import React from 'react';
 import { Box, Button, IconButton, Link, Paper, Stack, Typography } from '@mui/material';
 import CloseIcon from '@mui/icons-material/Close';
 import { Link as RouterLink } from 'react-router-dom';
-import { LabelValueTable } from '../../components/LabelValueTable';
+import { LabelValueTable } from '../../../components/LabelValueTable';
 import { DataGrid } from '@mui/x-data-grid';
-import { useSearchDataRepositories } from './context/ContextProvider';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
 import { GridColDef } from '@mui/x-data-grid';
 
 interface PreviewPanelProps {
@@ -67,9 +67,9 @@ export const PreviewPanel: React.FC<PreviewPanelProps> = (props) => {
             <Typography>
               {state.previewItem[state.cardFields.tags].map((tag: string, i: number) => {
                 if (state.cardFields.tags && i < state.previewItem[state.cardFields.tags].length - 1) {
-                  return <span>{`${tag}, `}</span>
+                  return <span key={`${tag}-${i}`}>{`${tag}, `}</span>
                 } else {
-                  return <span>{tag}</span>
+                  return <span key={`${tag}-${i}`}>{tag}</span>
                 }
               })}
             </Typography>
diff --git a/strudel-taskflows/src/pages/search-data-repositories/_config/taskflow.config.ts b/strudel-taskflows/src/pages/search-data-repositories/_config/taskflow.config.ts
new file mode 100644
index 00000000..e2da70eb
--- /dev/null
+++ b/strudel-taskflows/src/pages/search-data-repositories/_config/taskflow.config.ts
@@ -0,0 +1,74 @@
+import { SearchDataRepositoriesConfig } from "./taskflow.types";
+
+export const taskflow: SearchDataRepositoriesConfig = {
+  data: {
+    items: {
+      source: "default/search-data-repositories/datasets.json",
+      idField: "id"
+    }
+  },
+  pages: {
+    index: {
+      title: "Search Data Repositories App",
+      description: "Description of this app section",
+      cardFields: {
+        title: "title",
+        content: "summary",
+        tags: "tags"
+      },
+      cardFilters: [
+        {
+          field: "category",
+          displayName: "Category",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "Groundwater",
+                value: "Groundwater"
+              },
+              {
+                label: "Fires",
+                value: "Fires"
+              },
+              {
+                label: "Floods",
+                value: "Floods"
+              },
+              {
+                label: "Earthquakes",
+                value: "Earthquakes"
+              }
+            ]
+          }
+        },
+        {
+          field: "tags",
+          displayName: "Tags",
+          filterType: "CheckboxList",
+          props: {
+            options: [
+              {
+                label: "Boreal forest",
+                value: "Boreal forest"
+              },
+              {
+                label: "Carbon and greenhouse gas emissions",
+                value: "Carbon and greenhouse gas emissions"
+              },
+              {
+                label: "Ecology",
+                value: "Ecology"
+              }
+            ]
+          }
+        },
+        {
+          field: "publication_date",
+          displayName: "Publication Date",
+          filterType: "date range"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-taskflows/src/pages/search-data-repositories/_config/taskflow.types.ts b/strudel-taskflows/src/pages/search-data-repositories/_config/taskflow.types.ts
new file mode 100644
index 00000000..f3117167
--- /dev/null
+++ b/strudel-taskflows/src/pages/search-data-repositories/_config/taskflow.types.ts
@@ -0,0 +1,37 @@
+import { GridColDef } from "@mui/x-data-grid"
+
+/**
+ * Type definitions for the Compare Data Task Flow config object
+ */
+export interface SearchDataRepositoriesConfig {
+  /** Attributes that are used across the Task Flow */
+  properties?: Record<string, any>,
+  data: {
+    items: {
+      source: string,
+      idField: string
+    },
+    [key: string]: {
+      source: string,
+      idField: string
+    }
+  },
+  /** Cool pages */
+  pages: {
+    index: {
+      title: string,
+      description: string,
+      cardFields: {
+        title: string,
+        content: string,
+        tags: string
+      },
+      cardFilters: {
+        field: string,
+        displayName: string,
+        filterType: 'CheckboxList' | 'Slider' | 'date range',
+        props?: object
+      }[]
+    }
+  }
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_context/ContextProvider.tsx b/strudel-taskflows/src/pages/search-data-repositories/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/_context/ContextProvider.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/pages/search-data-repositories/_context/actions.ts b/strudel-taskflows/src/pages/search-data-repositories/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/pages/search-data-repositories/_context/actions.ts
rename to strudel-taskflows/src/pages/search-data-repositories/_context/actions.ts
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/SearchDataRepositoriesWrapper.tsx b/strudel-taskflows/src/pages/search-data-repositories/_layout.tsx
similarity index 58%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/SearchDataRepositoriesWrapper.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/_layout.tsx
index 130b3318..ce088df1 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/SearchDataRepositoriesWrapper.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/_layout.tsx
@@ -3,16 +3,16 @@ import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { SearchDataRepositoriesProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { SearchDataRepositoriesProvider } from './_context/ContextProvider';
+import { basename } from '../../main';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * Top-level wrapper for the search-data-repositories Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const SearchDataRepositoriesWrapper: React.FC = () => {
-  const datasets = useDataFromSource('{@ cookiecutter.data.main.cards.dataSource @}', basename)
+const SearchDataRepositoriesLayout: React.FC = () => {
+  const datasets = useDataFromSource(taskflow.data.items.source, basename)
 
   /**
    * Content to render on the page for this component
@@ -25,13 +25,15 @@ export const SearchDataRepositoriesWrapper: React.FC = () => {
       <Box>
         <SearchDataRepositoriesProvider 
           data={datasets} 
-          dataIdField='{@ cookiecutter.data.main.cards.dataIdField @}' 
-          filters={definitions.filters.main} 
-          cardFields={definitions.columns.main.card}
+          dataIdField={taskflow.data.items.idField} 
+          filters={taskflow.pages.index.cardFilters} 
+          cardFields={taskflow.pages.index.cardFields}
         >
           <Outlet />
         </SearchDataRepositoriesProvider>
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default SearchDataRepositoriesLayout;
\ No newline at end of file
diff --git a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DatasetExplorer.tsx b/strudel-taskflows/src/pages/search-data-repositories/index.tsx
similarity index 76%
rename from strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DatasetExplorer.tsx
rename to strudel-taskflows/src/pages/search-data-repositories/index.tsx
index 6f167f09..a891e633 100644
--- a/strudel-cookiecutter/search-data-repositories/{@cookiecutter.name@}/DatasetExplorer.tsx
+++ b/strudel-taskflows/src/pages/search-data-repositories/index.tsx
@@ -1,17 +1,18 @@
 import { Box, Grid } from '@mui/material';
 import React, { useState } from 'react';
 import { PageHeader } from '../../components/PageHeader';
-import { DataListPanel } from './DataListPanel';
-import { FiltersPanel } from './FiltersPanel';
-import { PreviewPanel } from './PreviewPanel';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setPreviewItem } from './context/actions';
+import { DataListPanel } from './_components/DataListPanel';
+import { FiltersPanel } from './_components/FiltersPanel';
+import { PreviewPanel } from './_components/PreviewPanel';
+import { useSearchDataRepositories } from './_context/ContextProvider';
+import { setPreviewItem } from './_context/actions';
+import { taskflow } from './_config/taskflow.config';
 
 /**
  * The main explore page for the search-data-repositories Task Flow.
  * Displays a page header, `<FiltersPanel>`, `<DataListPanel>`, and `<PreviewPanel>`.
  */
-export const DatasetExplorer: React.FC = () => {
+const DatasetExplorer: React.FC = () => {
   const {state, dispatch} = useSearchDataRepositories();
   const [showFiltersPanel, setShowFiltersPanel] = useState(true);
 
@@ -33,8 +34,8 @@ export const DatasetExplorer: React.FC = () => {
   return (
     <Box>
       <PageHeader
-        pageTitle="{@ cookiecutter.pages.main.pageTitle @}"
-        description="{@ cookiecutter.pages.main.pageDescription @}"
+        pageTitle={taskflow.pages.index.title}
+        description={taskflow.pages.index.description}
         sx={{
           marginBottom: 1,
           padding: 2,
@@ -69,4 +70,6 @@ const getMainColumnSize = (showFiltersPanel: boolean, showPreviewPanel: boolean)
   } else if (showFiltersPanel && showPreviewPanel) {
     return 6;
   }
-};
\ No newline at end of file
+};
+
+export default DatasetExplorer;
\ No newline at end of file
diff --git a/strudel-demo-app/src/theme.tsx b/strudel-taskflows/src/theme.tsx
similarity index 100%
rename from strudel-demo-app/src/theme.tsx
rename to strudel-taskflows/src/theme.tsx
diff --git a/strudel-demo-app/src/types/filters.types.tsx b/strudel-taskflows/src/types/filters.types.tsx
similarity index 100%
rename from strudel-demo-app/src/types/filters.types.tsx
rename to strudel-taskflows/src/types/filters.types.tsx
diff --git a/strudel-demo-app/src/utils/filters.utils.ts b/strudel-taskflows/src/utils/filters.utils.ts
similarity index 100%
rename from strudel-demo-app/src/utils/filters.utils.ts
rename to strudel-taskflows/src/utils/filters.utils.ts
diff --git a/strudel-taskflows/src/utils/string.utils.ts b/strudel-taskflows/src/utils/string.utils.ts
new file mode 100644
index 00000000..e2e9b8b3
--- /dev/null
+++ b/strudel-taskflows/src/utils/string.utils.ts
@@ -0,0 +1,3 @@
+export const removeExtraSlashes = (str: string) => {
+  return str.replace(/([^:]\/)\/+/g, "$1")
+}
\ No newline at end of file
diff --git a/strudel-demo-app/src/utils/useDataFromSource.tsx b/strudel-taskflows/src/utils/useDataFromSource.tsx
similarity index 100%
rename from strudel-demo-app/src/utils/useDataFromSource.tsx
rename to strudel-taskflows/src/utils/useDataFromSource.tsx
diff --git a/strudel-demo-app/src/utils/useInterval.tsx b/strudel-taskflows/src/utils/useInterval.tsx
similarity index 100%
rename from strudel-demo-app/src/utils/useInterval.tsx
rename to strudel-taskflows/src/utils/useInterval.tsx
diff --git a/strudel-demo-app/src/vite-env.d.ts b/strudel-taskflows/src/vite-env.d.ts
similarity index 100%
rename from strudel-demo-app/src/vite-env.d.ts
rename to strudel-taskflows/src/vite-env.d.ts
diff --git a/strudel-demo-app/tsconfig.json b/strudel-taskflows/tsconfig.json
similarity index 100%
rename from strudel-demo-app/tsconfig.json
rename to strudel-taskflows/tsconfig.json
diff --git a/strudel-demo-app/tsconfig.node.json b/strudel-taskflows/tsconfig.node.json
similarity index 100%
rename from strudel-demo-app/tsconfig.node.json
rename to strudel-taskflows/tsconfig.node.json
diff --git a/strudel-demo-app/vite.config.ts b/strudel-taskflows/vite.config.ts
similarity index 100%
rename from strudel-demo-app/vite.config.ts
rename to strudel-taskflows/vite.config.ts


Test patch:
diff --git a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.test.tsx b/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.test.tsx
deleted file mode 100644
index 2a68616d..00000000
--- a/strudel-cookiecutter/base/{@cookiecutter.name@}/src/app/App.test.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import App from './App';
-
-test('renders learn react link', () => {
-  render(<App />);
-  const linkElement = screen.getByText(/learn react/i);
-  expect(linkElement).toBeInTheDocument();
-});