- Run record
- loading
Loading this Run…
Loading the Revisions of this Run…
A published source file of Revision 0 of Boardly — Website Relaunch 1.1.0, shown as text in the browser.
import boardJson from "../public/data/board.json";
import membersJson from "../public/data/members.json";
import type { BoardState, Card, LabelDef, Member } from "./types";
interface BoardJson {
workspace: string;
board: string;
today: string;
fictional_notice: string;
labels: LabelDef[];
columns: { id: string; title: string; wip_limit: number | null; card_ids: string[] }[];
cards: Card[];
}
const data = boardJson as unknown as BoardJson;
const membersData = membersJson as unknown as { members: Member[] };
export const WORKSPACE = data.workspace;
export const BOARD_NAME = data.board;
export const TODAY = data.today;
export const NOTICE = data.fictional_notice;
export const LABELS: LabelDef[] = data.labels;
export const MEMBERS: Member[] = membersData.members;
export const DONE_COLUMN_ID = "done";
const memberById = new Map(MEMBERS.map((m) => [m.id, m]));
const labelById = new Map(LABELS.map((l) => [l.id, l]));
export function getMember(id: string | null): Member | undefined {
return id ? memberById.get(id) : undefined;
}
export function getLabel(id: string): LabelDef | undefined {
return labelById.get(id);
}
export const LABEL_STYLE: Record<string, { bg: string; fg: string }> = {
design: { bg: "#f1eafd", fg: "#6d3fc0" },
frontend: { bg: "#e3effd", fg: "#1d5fc2" },
backend: { bg: "#fdf1df", fg: "#9a5b06" },
content: { bg: "#e3f6ec", fg: "#147a4b" },
research: { bg: "#fdeaf1", fg: "#b02a62" },
bug: { bg: "#fdebe9", fg: "#bb2d20" },
};
export const AVATAR_COLORS: Record<string, string> = {
maya: "#0e8093",
tomas: "#c06a12",
aiko: "#6d3fc0",
jonah: "#0e7a4b",
priya: "#b8336a",
};
function deepCopy<T>(value: T): T {
return JSON.parse(JSON.stringify(value)) as T;
}
export function buildInitialState(): BoardState {
const cards: Record<string, Card> = {};
for (const card of deepCopy(data.cards)) cards[card.id] = card;
return {
columns: deepCopy(data.columns),
cards,
};
}
export function parseISODate(iso: string): Date {
const parts = iso.split("-").map((n) => Number(n));
return new Date(parts[0] ?? 2026, (parts[1] ?? 1) - 1, parts[2] ?? 1);
}
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
export function formatDueShort(iso: string): string {
const d = parseISODate(iso);
return `${MONTHS[d.getMonth()]} ${d.getDate()}`;
}
export function formatDueLong(iso: string): string {
const d = parseISODate(iso);
return `${MONTHS[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
}
export function isOverdue(card: Card, columnId: string): boolean {
if (!card.due || columnId === DONE_COLUMN_ID) return false;
return parseISODate(card.due).getTime() < parseISODate(TODAY).getTime();
}
export interface DueInfo {
text: string;
tone: "normal" | "today" | "overdue";
}
export function dueInfo(card: Card, columnId: string): DueInfo | null {
if (!card.due) return null;
if (isOverdue(card, columnId)) {
return { text: `Overdue · ${formatDueShort(card.due)}`, tone: "overdue" };
}
if (card.due === TODAY) return { text: "Due today", tone: "today" };
return { text: formatDueShort(card.due), tone: "normal" };
}
export function nextCardId(cards: Record<string, Card>): string {
let max = 100;
for (const id of Object.keys(cards)) {
const m = /^c-(\d+)$/.exec(id);
if (m) max = Math.max(max, Number(m[1]));
}
return `c-${max + 1}`;
}
export function moveCardInBoard(
board: BoardState,
cardId: string,
toColId: string,
toFullIndex: number,
): BoardState {
const columns = board.columns.map((c) => ({ ...c, card_ids: [...c.card_ids] }));
const from = columns.find((c) => c.card_ids.includes(cardId));
if (!from) return board;
const to = columns.find((c) => c.id === toColId);
if (!to) return board;
const fromIdx = from.card_ids.indexOf(cardId);
from.card_ids.splice(fromIdx, 1);
let idx = Math.max(0, Math.min(toFullIndex, to.card_ids.length));
if (to === from && idx > fromIdx) idx -= 1;
if (to === from) {
const before = from.card_ids.join("\u0000");
from.card_ids.splice(idx, 0, cardId);
if (from.card_ids.join("\u0000") === before) return board;
return { columns, cards: board.cards };
}
to.card_ids.splice(idx, 0, cardId);
return { columns, cards: board.cards };
}Source is browser-viewable only. There is no download or archive endpoint, and no additional license is granted beyond inspection and research.