TEZv commited on
Commit
de90565
·
verified ·
1 Parent(s): 78d7982

Upload app.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.js +200 -0
app.js ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const dataUrl = "./data/registry.json";
2
+
3
+ const searchInput = document.getElementById("search-input");
4
+ const sphereFilter = document.getElementById("sphere-filter");
5
+ const comboFilter = document.getElementById("combo-filter");
6
+ const statusFilter = document.getElementById("status-filter");
7
+ const artifactFilter = document.getElementById("artifact-filter");
8
+ const registryGrid = document.getElementById("registry-grid");
9
+ const stats = document.getElementById("stats");
10
+ const resultCount = document.getElementById("result-count");
11
+ const cardTemplate = document.getElementById("card-template");
12
+
13
+ let registryEntries = [];
14
+
15
+ function titleCase(value) {
16
+ return value
17
+ .split(/[\s_-]+/)
18
+ .filter(Boolean)
19
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
20
+ .join(" ");
21
+ }
22
+
23
+ function prettyLabel(key, value) {
24
+ if (!value) {
25
+ return "";
26
+ }
27
+
28
+ if (key === "sphere") {
29
+ return titleCase(value);
30
+ }
31
+
32
+ if (key === "artifactType" || key === "validationStage") {
33
+ return titleCase(value);
34
+ }
35
+
36
+ return value;
37
+ }
38
+
39
+ function uniqueValues(entries, key) {
40
+ return [...new Set(entries.map((entry) => entry[key]).filter(Boolean))].sort();
41
+ }
42
+
43
+ function fillSelect(select, values, key) {
44
+ for (const value of values) {
45
+ const option = document.createElement("option");
46
+ option.value = value;
47
+ option.textContent = prettyLabel(key, value);
48
+ select.appendChild(option);
49
+ }
50
+ }
51
+
52
+ function buildStats(entries) {
53
+ const bySphere = ["science", "entrepreneurship", "technology"].map((sphere) => ({
54
+ label: prettyLabel("sphere", sphere),
55
+ value: entries.filter((entry) => entry.primarySphere === sphere).length,
56
+ className: sphere,
57
+ }));
58
+
59
+ const cards = [
60
+ { label: "Total entries", value: entries.length, className: "total" },
61
+ ...bySphere,
62
+ {
63
+ label: "Hybrid lanes",
64
+ value: entries.filter((entry) => entry.combo.includes("+")).length,
65
+ className: "hybrid",
66
+ },
67
+ ];
68
+
69
+ stats.innerHTML = "";
70
+ for (const card of cards) {
71
+ const wrapper = document.createElement("article");
72
+ wrapper.className = `stat ${card.className}`;
73
+ wrapper.innerHTML = `
74
+ <div class="stat-value">${card.value}</div>
75
+ <div class="stat-label">${card.label}</div>
76
+ `;
77
+ stats.appendChild(wrapper);
78
+ }
79
+ }
80
+
81
+ function setText(target, value) {
82
+ target.textContent = value && value.length ? value : "—";
83
+ }
84
+
85
+ function renderCards(entries) {
86
+ registryGrid.innerHTML = "";
87
+ resultCount.textContent = `${entries.length} result${entries.length === 1 ? "" : "s"}`;
88
+
89
+ if (!entries.length) {
90
+ const empty = document.createElement("div");
91
+ empty.className = "empty";
92
+ empty.textContent = "No entries match the current filters yet.";
93
+ registryGrid.appendChild(empty);
94
+ return;
95
+ }
96
+
97
+ for (const entry of entries) {
98
+ const fragment = cardTemplate.content.cloneNode(true);
99
+ const sphereBadge = fragment.querySelector(".badge.sphere");
100
+ const comboBadge = fragment.querySelector(".badge.combo");
101
+ const statusBadge = fragment.querySelector(".badge.status");
102
+ const title = fragment.querySelector(".title");
103
+ const summary = fragment.querySelector(".summary");
104
+ const track = fragment.querySelector(".track");
105
+ const artifactType = fragment.querySelector(".artifact-type");
106
+ const secondarySpheres = fragment.querySelector(".secondary-spheres");
107
+ const deliveryLayers = fragment.querySelector(".delivery-layers");
108
+ const validationStage = fragment.querySelector(".validation-stage");
109
+ const tags = fragment.querySelector(".tags");
110
+ const links = fragment.querySelector(".links");
111
+
112
+ sphereBadge.textContent = prettyLabel("sphere", entry.primarySphere);
113
+ sphereBadge.classList.add(entry.primarySphere);
114
+ comboBadge.textContent = entry.combo;
115
+ comboBadge.classList.add(`combo-${entry.combo.toLowerCase().replace(/\+/g, "-")}`);
116
+ statusBadge.textContent = prettyLabel("status", entry.status);
117
+
118
+ title.textContent = entry.title;
119
+ summary.textContent = entry.summary;
120
+ setText(track, entry.track);
121
+ setText(artifactType, prettyLabel("artifactType", entry.artifactType));
122
+ setText(
123
+ secondarySpheres,
124
+ entry.secondarySpheres.length
125
+ ? entry.secondarySpheres.map((item) => prettyLabel("sphere", item)).join(", ")
126
+ : "",
127
+ );
128
+ setText(deliveryLayers, entry.deliveryLayers.join(", "));
129
+ setText(validationStage, prettyLabel("validationStage", entry.validationStage));
130
+ setText(tags, entry.tags.join(", "));
131
+
132
+ for (const link of entry.links) {
133
+ const anchor = document.createElement("a");
134
+ anchor.href = link.href;
135
+ anchor.textContent = link.label;
136
+ anchor.target = "_blank";
137
+ anchor.rel = "noreferrer";
138
+ links.appendChild(anchor);
139
+ }
140
+
141
+ registryGrid.appendChild(fragment);
142
+ }
143
+ }
144
+
145
+ function applyFilters() {
146
+ const query = searchInput.value.trim().toLowerCase();
147
+ const sphere = sphereFilter.value;
148
+ const combo = comboFilter.value;
149
+ const status = statusFilter.value;
150
+ const artifact = artifactFilter.value;
151
+
152
+ const filtered = registryEntries.filter((entry) => {
153
+ const matchesSphere = sphere === "all" || entry.primarySphere === sphere;
154
+ const matchesCombo = combo === "all" || entry.combo === combo;
155
+ const matchesStatus = status === "all" || entry.status === status;
156
+ const matchesArtifact = artifact === "all" || entry.artifactType === artifact;
157
+ const haystack = [
158
+ entry.title,
159
+ entry.summary,
160
+ entry.track,
161
+ entry.entryType,
162
+ entry.primarySphere,
163
+ entry.combo,
164
+ entry.artifactType,
165
+ entry.validationStage,
166
+ ...entry.secondarySpheres,
167
+ ...entry.deliveryLayers,
168
+ ...entry.tags,
169
+ ]
170
+ .join(" ")
171
+ .toLowerCase();
172
+ const matchesQuery = !query || haystack.includes(query);
173
+ return matchesSphere && matchesCombo && matchesStatus && matchesArtifact && matchesQuery;
174
+ });
175
+
176
+ renderCards(filtered);
177
+ }
178
+
179
+ async function init() {
180
+ const response = await fetch(dataUrl);
181
+ registryEntries = await response.json();
182
+
183
+ fillSelect(sphereFilter, uniqueValues(registryEntries, "primarySphere"), "sphere");
184
+ fillSelect(comboFilter, uniqueValues(registryEntries, "combo"), "combo");
185
+ fillSelect(statusFilter, uniqueValues(registryEntries, "status"), "status");
186
+ fillSelect(artifactFilter, uniqueValues(registryEntries, "artifactType"), "artifactType");
187
+ buildStats(registryEntries);
188
+ renderCards(registryEntries);
189
+
190
+ searchInput.addEventListener("input", applyFilters);
191
+ sphereFilter.addEventListener("change", applyFilters);
192
+ comboFilter.addEventListener("change", applyFilters);
193
+ statusFilter.addEventListener("change", applyFilters);
194
+ artifactFilter.addEventListener("change", applyFilters);
195
+ }
196
+
197
+ init().catch((error) => {
198
+ registryGrid.innerHTML = `<div class="empty">Failed to load registry data: ${error.message}</div>`;
199
+ resultCount.textContent = "Error";
200
+ });