workstations and light theme

This commit is contained in:
2026-04-16 16:33:48 +03:00
parent 555e920c18
commit bbfaf5ccc5
37 changed files with 1015 additions and 106 deletions
+7 -2
View File
@@ -1,5 +1,6 @@
let stackList = [];
let projectsList = [];
let workstationsList = [];
let linksList = [];
const fetchJson = async (path) => {
@@ -13,7 +14,7 @@ const fetchJson = async (path) => {
};
const loadIcon = async (key) => {
const response = await fetch(`./icons/${key}.svg`);
const response = await fetch(`./img/icons/${key}.svg`);
if (!response.ok) {
return key;
@@ -25,5 +26,9 @@ const loadIcon = async (key) => {
const formatIndex = (index) => String(index + 1).padStart(2, "0");
document.addEventListener("DOMContentLoaded", () => {
loadStack().then(loadProjects).then(loadLinks).catch(console.error);
loadStack()
.then(loadProjects)
.then(loadWorkstations)
.then(loadLinks)
.catch(console.error);
});
+1 -1
View File
@@ -21,7 +21,7 @@ const loadProjects = async () => {
const buildProjectCard = (project, index) => {
const card = document.createElement("article");
card.classList.add("project-card", `project-card--${project.tone ?? "mint"}`);
card.classList.add("project-card", `tone-${project.tone ?? "mint"}`);
const header = document.createElement("div");
header.classList.add("project-card__header");
+20 -5
View File
@@ -1,3 +1,5 @@
let isStackExpanded = false;
const getExperienceLevel = (years) => {
if (years >= 7) return "Core";
if (years >= 4) return "Strong";
@@ -17,21 +19,34 @@ const loadStackForProject = (keys) => {
const loadStack = async () => {
const stackContainer = document.getElementById("stack");
const stackToggle = document.getElementById("stack-toggle");
if (!stackContainer) {
return;
}
stackList = await fetchJson("./data/stack.json");
const stackCards = stackList.map(buildStackCard).filter(Boolean);
renderStack(stackContainer);
if (stackToggle && stackList.some((stack) => stack.hide === true)) {
stackToggle.hidden = false;
stackToggle.addEventListener("click", () => {
isStackExpanded = !isStackExpanded;
stackToggle.textContent = isStackExpanded ? "Show less" : "View all";
stackToggle.setAttribute("aria-expanded", String(isStackExpanded));
renderStack(stackContainer);
});
}
};
const renderStack = (stackContainer) => {
const stackCards = stackList
.filter((stack) => isStackExpanded || stack.hide !== true)
.map(buildStackCard);
stackContainer.replaceChildren(...stackCards);
};
const buildStackCard = (stack) => {
if (stack?.hide === true) {
return null;
}
const card = document.createElement("div");
card.classList.add("stack-card", `tone-${stack.tone ?? "mint"}`);
+144
View File
@@ -0,0 +1,144 @@
let areWorkstationsExpanded = false;
const loadWorkstations = async () => {
const workstationContainer = document.getElementById("workstations");
const workstationToggle = document.getElementById("workstation-toggle");
if (!workstationContainer) {
return;
}
workstationsList = await fetchJson("./data/workstations.json");
renderWorkstations(workstationContainer);
if (
workstationToggle &&
workstationsList.some((workstation) => workstation.actual === false)
) {
workstationToggle.hidden = false;
workstationToggle.addEventListener("click", () => {
areWorkstationsExpanded = !areWorkstationsExpanded;
workstationToggle.textContent = areWorkstationsExpanded
? "Show less"
: "View all";
workstationToggle.setAttribute(
"aria-expanded",
String(areWorkstationsExpanded),
);
renderWorkstations(workstationContainer);
});
}
};
const renderWorkstations = (workstationContainer) => {
const workstationCards = workstationsList
.filter(
(workstation) => areWorkstationsExpanded || workstation.actual !== false,
)
.map(buildWorkstationCard);
workstationContainer.replaceChildren(...workstationCards);
};
const buildWorkstationCard = (workstation, index) => {
const card = document.createElement("article");
card.classList.add("workstation-card", `tone-${workstation.tone ?? "mint"}`);
const header = document.createElement("div");
header.classList.add("workstation-card__header");
const indexBadge = document.createElement("span");
indexBadge.classList.add("workstation-card__index");
indexBadge.textContent = formatIndex(index);
const key = document.createElement("span");
key.classList.add("workstation-card__key");
key.textContent = workstation.key;
const status = document.createElement("span");
status.classList.add(
"workstation-card__status",
workstation.actual
? "workstation-card__status--actual"
: "workstation-card__status--archive",
);
status.textContent = workstation.actual ? "active" : "archive";
const title = document.createElement("h3");
title.classList.add("workstation-card__title");
title.textContent = workstation.name;
const specs = document.createElement("dl");
specs.classList.add("workstation-card__specs");
appendSpec(specs, "CPU", workstation.specs?.cpu);
appendSpec(specs, "GPU", workstation.specs?.gpu);
appendSpec(specs, "RAM", workstation.specs?.ram);
appendSpec(specs, "OS", workstation.specs?.os);
const storage = buildChipGroup(
"Storage",
workstation.specs?.storage?.map(formatStorage) ?? [],
);
const devices = buildChipGroup(
"Devices",
workstation.devices?.map((device) => device.name) ?? [],
);
header.append(indexBadge, key, status);
card.append(header, title, specs);
if (storage) {
card.append(storage);
}
if (devices) {
card.append(devices);
}
return card;
};
const appendSpec = (list, label, value) => {
if (!value) {
return;
}
const term = document.createElement("dt");
term.textContent = label;
const description = document.createElement("dd");
description.textContent = value;
list.append(term, description);
};
const buildChipGroup = (label, items) => {
if (items.length === 0) {
return null;
}
const group = document.createElement("div");
group.classList.add("workstation-card__group");
const title = document.createElement("p");
title.classList.add("workstation-card__group-title");
title.textContent = label;
const chips = document.createElement("div");
chips.classList.add("workstation-card__chips");
chips.append(
...items.map((item) => {
const chip = document.createElement("span");
chip.textContent = item;
return chip;
}),
);
group.append(title, chips);
return group;
};
const formatStorage = (storage) => {
const parts = [storage.capacity, storage.name, storage.type].filter(Boolean);
return parts.join(" · ");
};