1// upload/pages/products.js â generated by scripts/migrate-pages.mjs. 2// Do not hand-edit; re-run migrate-pages.mjs and edit the original 3// HTML / inline script there. 4 5import "../_globals.js"; 6import { applySeoMeta } from "nodality"; 7 8// SEO metadata â description, canonical, Open Graph + Twitter cards. Lands in the 9// static HTML at prerender time so crawlers and social scrapers see it. 10applySeoMeta({ 11 path: "/products.html", 12 title: "BLUE70 â produkty blueseventy", 13 description: "Kompletnà nabÃdka produktů blueseventy â neoprenové obleky, plavecké brýle a doplÅky pro triatlonisty a plavce.", 14 image: "/img/blue70.jpg", 15 ogType: "website", 16 siteName: "BLUE70", 17}); 18 19import {renderNav} from "../components/nav.js"; renderNav(); 20import {renderDarkMode} from "../components/dark-mode.js"; renderDarkMode(); 21import "../components/footer.js"; 22 23// --- inline block --- 24 25 const urlParams = new URLSearchParams(window.location.search); 26const cat = urlParams.get("cat"); 27document.title = `BLUE70 ${cat}`; 28// http://localhost:2000/categoryDetail?cat=neopreny 29 30// http://localhost:2000/products 31 32 33// http://localhost:2000/products?cat=neopreny 34// http://localhost:2000/products?cat=wetsuits 35 36import {Blue70Button} from "../components/blue-70-button.js"; 37 38 class ProductCard extends Base { 39 constructor(obj){ 40 super(); 41 this.obj = obj; 42 43 if (obj.images === undefined){ 44 alert(obj.name); 45 46 } else { 47 if (obj.images[0] === undefined){ 48 alert(obj.name); 49 } 50 } 51 } 52 53 render(){ 54 return new Wrapper(/*isLink: true, child: `detail.html?id=a`*/) 55 .add([ 56 new Image().set({ 57 type: "img", 58 width: "300px", 59 isFull: true, 60 url: this.obj.images[0], 61 alt: this.obj.name, 62 vtn: this.obj.name 63 }), 64 new Wrapper().set({ 65 pad: [{a: 14}] 66 }).add([ 67 68 new Wrapper().set({ 69 70 71 resprop: [ 72 { 73 breakpoint: "md", 74 height: "120px", 75 // background: "green" 76 77 }, 78 { 79 breakpoint: "1800px", 80 height: "170px", 81 // background: "orange" 82 83 } 84 ] 85 }).add([ 86 new Text(this.obj.name).set({ 87 color: "black", 88 weight: "bold", 89 exact: "1.7rem", 90 font: "Helvetica" 91 }), 92 93 94 new Text(this.obj.price).set({ 95 weight: "bold", 96 color: "black", 97 exact: "1.1rem", 98 pad: [{b: "0.7rem"}], 99 hide: this.obj.price === "n/a" 100 }), 101 ]), 102 103 new Blue70Button({ 104 // Link straight to the per-product static page generated 105 // by `nodality prerender`. Keeps the old 106 // detail.html?id=⦠runtime path working as a fallback 107 // for bookmarks / inbound external links. 108 url: `https://www.blue70.cz/detail-${this.obj.id}.html` 109 }).render() 110 111 ]) 112 ]); 113 } // 205954 WORKS 114 // locally have to type manually http://localhost:2000/detail?id=helix 115 } 116 117 // â Title 118 119 120 121 import { FooterEl } from "../components/footer.js"; 122import { loadJson } from "nodality"; 123 124// Top-level await so prerender serialises the full grid + footer into 125// the static HTML. Previously this was a `fetch(...).then(...)` chain 126// that resolved after prerender had already snapshot the DOM, leaving 127// crawlers with an empty mount. `loadJson` reads from disk at SSG time 128// (./upload/products.json) and falls through to fetch in the browser. 129try { 130 const data = await loadJson("products.json"); 131 const cata = data.categories.filter(cato => cato.id.toLowerCase() === cat)[0] 132 ?? { products: [] }; 133 134 new FlexGrid() 135 .set({ 136 colat: "700px", 137 wrap: true, 138 align: "center", 139 gap: "1rem", 140 pad: [{ a: "1rem" }] 141 }) 142 .items( 143 cata.products.map(el => 144 new Card() 145 .set({ 146 width: "300px", 147 radius: "1rem", 148 border: "3px solid #ECF0F1", 149 background: "#ECF0F1", 150 inpad: "1rem", 151 mar: { sides: ["all"], value: "0.8rem" } 152 }) 153 .items([ new ProductCard(el).render() ]) 154 ) 155 ) 156 .render("#mount"); 157 158 // Render footer under grid â now lives inside the awaited block so 159 // it lands in the static HTML alongside the products it sits under. 160 new FooterEl().render().render("#mount"); 161} catch (err) { 162 console.error("Failed to load products.json", err); 163} 164 165 166// We can host on github and redirect for free 167 168// UPDATE: npm i nodality@latest 169// BYPASS package.json 170 171// run with npx serve -l 2000 172// http://localhost:2000/publish 173
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.