From 5c472258e2f559b571bb868fde840daef926c56f Mon Sep 17 00:00:00 2001 From: Dan Livings Date: Tue, 17 Sep 2024 16:34:18 +0100 Subject: [PATCH] Update dependency view to include end-of-life information Dependencies are considered to be near the end of their life when there is less than one year of support remaining. In this case, the second capsule used to display the latest version of the dependency has been expanded to provide a timescale for this deadline in a relative format, with an icon as a secondary visual indicator: - `bxs-hourglass` is used when the end-of-life date is upcoming - `bxs-hourglass-bottom` is used when the end-of-life date has passed. --- index.njk | 38 +++++++++++++------- utils/index.js | 94 ++++++++++++++++++++++++++++---------------------- 2 files changed, 78 insertions(+), 54 deletions(-) diff --git a/index.njk b/index.njk index d17c1c6..4f6c7a7 100644 --- a/index.njk +++ b/index.njk @@ -75,21 +75,35 @@
  • - {{ dependency.name }} - {% if dependency.version %} - v{{ dependency.version }} - {% endif %} - {% if dependency.tag %} - {{ dependency.tag }} - {% endif %} + {{ dependency.name }} + {% if dependency.version %} + v{{ dependency.version }} + {% endif %} + {% if dependency.tag %} + {{ dependency.tag }} + {% endif %} - {% if dependency.isOutdated %} - - - v{{ dependency.latestVersion }} + + {% if dependency.isOutdated %} + + + {{ dependency.changelog }} + v{{ dependency.latestVersion }} + + {% endif %} + {% if dependency.isEndOfLifeSoon %} + + + Support ends {{ dependency.endOfLifeRelative }} + + {% elif dependency.isOutOfSupport %} + + + Support ended {{ dependency.endOfLifeRelative }} + + {% endif %} - {% endif%}
  • {% endfor %} diff --git a/utils/index.js b/utils/index.js index 9be6d4f..870aa8e 100644 --- a/utils/index.js +++ b/utils/index.js @@ -1,5 +1,6 @@ import { readFile } from "fs/promises"; -import { getDependencyState } from "./endOfLifeDateApi/index.js"; +import { differenceInYears, formatDistance, startOfToday } from "date-fns"; +import { getDependencyEndOfLifeDate, getDependencyState } from "./endOfLifeDateApi/index.js"; /** * @typedef {Object} PersistedData @@ -15,14 +16,47 @@ import { getDependencyState } from "./endOfLifeDateApi/index.js"; * @property {string} org - The name of the Github organisation the repos belong to. */ +/** + * @typedef {"sky"|"amber"|"orange"|"red"} TailwindColor + */ + +/** + * @typedef {"bx-check-circle"|"bx-up-arrow-circle"|"bxs-up-arrow-circle"|"bxs-x-circle"} Boxicon + */ + /** * @typedef {Object} UiDependency * @property {string} name * @property {string?} version * @property {string?} tag - * @property {string} color + * @property {TailwindColor} color + * @property {Boxicon} icon + * @property {string?} latestVersion + * @property {Date?} endOfLifeDate + * @property {string?} endOfLifeRelative + * @property {boolean} isOutdated + * @property {boolean} isOutOfSupport + * @property {boolean} isEndOfLifeSoon */ +const stateColors = { + "upToDate": "sky", + "minorUpdateAvailable": "amber", + "majorUpdateAvailable": "orange", + "endOfLife": "red", +}; + +const defaultColor = "stone"; + +const stateIcons = { + "upToDate": "bx-check-circle", + "minorUpdateAvailable": "bx-up-arrow-circle", + "majorUpdateAvailable": "bxs-up-arrow-circle", + "endOfLife": "bxs-x-circle", +}; + +const defaultIcon = "bx-question-mark"; + /** * Maps the persisted dependency data from storage to a format suitable for the UI * @param {import("../model/Dependency").Dependency} dependency @@ -35,43 +69,15 @@ export const mapDependencyFromStorageToUi = (dependency, persistedLifetimes) => const state = lifetimes === undefined ? "unknown" : getDependencyState(dependency, lifetimes.lifetimes); const latestVersion = lifetimes?.lifetimes[0]?.latest; - let color; - switch (state) { - case "unknown": - color = "stone"; - break; - case "upToDate": - color = "sky"; - break; - case "minorUpdateAvailable": - color = "amber"; - break; - case "majorUpdateAvailable": - color = "orange"; - break; - case "endOfLife": - color = "red"; - break; - } - - let icon; - switch (state) { - case "unknown": - icon = "bx-question-mark"; - break; - case "upToDate": - icon = "bx-check-circle"; - break; - case "minorUpdateAvailable": - icon = "bx-up-arrow-circle"; - break; - case "majorUpdateAvailable": - icon = "bxs-up-arrow-circle"; - break; - case "endOfLife": - icon = "bxs-x-circle"; - break; - } + const color = stateColors[state] ?? defaultColor; + const icon = stateIcons[state] ?? defaultIcon; + + const endOfLifeDate = lifetimes && getDependencyEndOfLifeDate(dependency, lifetimes.lifetimes); + const endOfLifeRelative = endOfLifeDate && formatDistance(endOfLifeDate, startOfToday(), { addSuffix: true }); + + const isOutdated = state !== "upToDate" && state !== "unknown"; + const isOutOfSupport = state === "endOfLife"; + const isEndOfLifeSoon = !!endOfLifeDate && !isOutOfSupport && differenceInYears(endOfLifeDate, startOfToday()) < 1; return { name: dependency.name, @@ -79,10 +85,14 @@ export const mapDependencyFromStorageToUi = (dependency, persistedLifetimes) => tag: dependency.tag, color, icon, - isOutdated: state !== "upToDate" && state !== "unknown", latestVersion, - } -} + endOfLifeDate, + endOfLifeRelative, + isOutdated, + isOutOfSupport, + isEndOfLifeSoon, + }; +}; /** * Maps the persisted repo data from storage to a format suitable for the UI