hotsheet 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +150 -31
- package/dist/client/app.global.js +4 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -72,9 +72,9 @@ var init_gitignore = __esm({
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
// src/cli.ts
|
|
75
|
-
import { mkdirSync as
|
|
75
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
76
76
|
import { tmpdir } from "os";
|
|
77
|
-
import { join as
|
|
77
|
+
import { join as join7, resolve } from "path";
|
|
78
78
|
|
|
79
79
|
// src/cleanup.ts
|
|
80
80
|
import { rmSync as rmSync2 } from "fs";
|
|
@@ -1150,12 +1150,14 @@ var CATEGORY_DESCRIPTIONS = {
|
|
|
1150
1150
|
|
|
1151
1151
|
// src/sync/markdown.ts
|
|
1152
1152
|
var dataDir;
|
|
1153
|
+
var port;
|
|
1153
1154
|
var worklistTimeout = null;
|
|
1154
1155
|
var openTicketsTimeout = null;
|
|
1155
1156
|
var WORKLIST_DEBOUNCE = 500;
|
|
1156
1157
|
var OPEN_TICKETS_DEBOUNCE = 5e3;
|
|
1157
|
-
function initMarkdownSync(dir) {
|
|
1158
|
+
function initMarkdownSync(dir, serverPort) {
|
|
1158
1159
|
dataDir = dir;
|
|
1160
|
+
port = serverPort;
|
|
1159
1161
|
}
|
|
1160
1162
|
function scheduleWorklistSync() {
|
|
1161
1163
|
if (worklistTimeout) clearTimeout(worklistTimeout);
|
|
@@ -1233,13 +1235,13 @@ async function syncWorklist() {
|
|
|
1233
1235
|
sections.push("");
|
|
1234
1236
|
sections.push("## Workflow");
|
|
1235
1237
|
sections.push("");
|
|
1236
|
-
sections.push(
|
|
1238
|
+
sections.push(`The Hot Sheet API is available at http://localhost:${port}/api. Use it to update ticket status as you work:`);
|
|
1237
1239
|
sections.push("");
|
|
1238
1240
|
sections.push('- **When you start working on a ticket**, set its status to "started":');
|
|
1239
|
-
sections.push(
|
|
1241
|
+
sections.push(` \`curl -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "started"}'\``);
|
|
1240
1242
|
sections.push("");
|
|
1241
1243
|
sections.push('- **When you finish working on a ticket**, set its status to "completed" and add notes describing what was done:');
|
|
1242
|
-
sections.push(
|
|
1244
|
+
sections.push(` \`curl -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "completed", "notes": "Description of work completed"}'\``);
|
|
1243
1245
|
sections.push("");
|
|
1244
1246
|
sections.push('Do NOT set tickets to "verified" \u2014 that status is reserved for human review.');
|
|
1245
1247
|
sections.push("");
|
|
@@ -1475,8 +1477,8 @@ apiRoutes.post("/tickets/:id/attachments", async (c) => {
|
|
|
1475
1477
|
mkdirSync2(attachDir, { recursive: true });
|
|
1476
1478
|
const storedPath = join4(attachDir, storedName);
|
|
1477
1479
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
1478
|
-
const { writeFileSync:
|
|
1479
|
-
|
|
1480
|
+
const { writeFileSync: writeFileSync4 } = await import("fs");
|
|
1481
|
+
writeFileSync4(storedPath, buffer);
|
|
1480
1482
|
const attachment = await addAttachment(id, originalName, storedPath);
|
|
1481
1483
|
scheduleAllSync();
|
|
1482
1484
|
notifyChange();
|
|
@@ -1501,8 +1503,8 @@ apiRoutes.get("/attachments/file/*", async (c) => {
|
|
|
1501
1503
|
if (!existsSync2(fullPath)) {
|
|
1502
1504
|
return c.json({ error: "File not found" }, 404);
|
|
1503
1505
|
}
|
|
1504
|
-
const { readFileSync:
|
|
1505
|
-
const content =
|
|
1506
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
1507
|
+
const content = readFileSync4(fullPath);
|
|
1506
1508
|
const ext = extname(fullPath).toLowerCase();
|
|
1507
1509
|
const mimeTypes = {
|
|
1508
1510
|
".png": "image/png",
|
|
@@ -1903,18 +1905,18 @@ pageRoutes.get("/", (c) => {
|
|
|
1903
1905
|
});
|
|
1904
1906
|
|
|
1905
1907
|
// src/server.ts
|
|
1906
|
-
function tryServe(fetch,
|
|
1908
|
+
function tryServe(fetch, port2) {
|
|
1907
1909
|
return new Promise((resolve2, reject) => {
|
|
1908
|
-
const server = serve({ fetch, port });
|
|
1910
|
+
const server = serve({ fetch, port: port2 });
|
|
1909
1911
|
server.on("listening", () => {
|
|
1910
|
-
resolve2(
|
|
1912
|
+
resolve2(port2);
|
|
1911
1913
|
});
|
|
1912
1914
|
server.on("error", (err) => {
|
|
1913
1915
|
reject(err);
|
|
1914
1916
|
});
|
|
1915
1917
|
});
|
|
1916
1918
|
}
|
|
1917
|
-
async function startServer(
|
|
1919
|
+
async function startServer(port2, dataDir2) {
|
|
1918
1920
|
const app = new Hono3();
|
|
1919
1921
|
app.use("*", async (c, next) => {
|
|
1920
1922
|
c.set("dataDir", dataDir2);
|
|
@@ -1932,10 +1934,10 @@ async function startServer(port, dataDir2) {
|
|
|
1932
1934
|
});
|
|
1933
1935
|
app.route("/api", apiRoutes);
|
|
1934
1936
|
app.route("/", pageRoutes);
|
|
1935
|
-
let actualPort =
|
|
1937
|
+
let actualPort = port2;
|
|
1936
1938
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
1937
1939
|
try {
|
|
1938
|
-
actualPort = await tryServe(app.fetch,
|
|
1940
|
+
actualPort = await tryServe(app.fetch, port2 + attempt);
|
|
1939
1941
|
break;
|
|
1940
1942
|
} catch (err) {
|
|
1941
1943
|
if (err instanceof Error && err.code === "EADDRINUSE" && attempt < 19) {
|
|
@@ -1944,8 +1946,8 @@ async function startServer(port, dataDir2) {
|
|
|
1944
1946
|
throw err;
|
|
1945
1947
|
}
|
|
1946
1948
|
}
|
|
1947
|
-
if (actualPort !==
|
|
1948
|
-
console.log(` Port ${
|
|
1949
|
+
if (actualPort !== port2) {
|
|
1950
|
+
console.log(` Port ${port2} in use, using ${actualPort} instead.`);
|
|
1949
1951
|
}
|
|
1950
1952
|
const url = `http://localhost:${actualPort}`;
|
|
1951
1953
|
console.log(`
|
|
@@ -1955,6 +1957,117 @@ async function startServer(port, dataDir2) {
|
|
|
1955
1957
|
exec(`${openCmd} ${url}`);
|
|
1956
1958
|
}
|
|
1957
1959
|
|
|
1960
|
+
// src/update-check.ts
|
|
1961
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1962
|
+
import { get } from "https";
|
|
1963
|
+
import { homedir } from "os";
|
|
1964
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
1965
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1966
|
+
var DATA_DIR = join6(homedir(), ".hotsheet");
|
|
1967
|
+
var CHECK_FILE = join6(DATA_DIR, "last-update-check");
|
|
1968
|
+
var PACKAGE_NAME = "hotsheet";
|
|
1969
|
+
function getCurrentVersion() {
|
|
1970
|
+
try {
|
|
1971
|
+
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
1972
|
+
const pkg = JSON.parse(readFileSync3(join6(dir, "..", "package.json"), "utf-8"));
|
|
1973
|
+
return pkg.version;
|
|
1974
|
+
} catch {
|
|
1975
|
+
return "0.0.0";
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
function getLastCheckDate() {
|
|
1979
|
+
try {
|
|
1980
|
+
if (existsSync4(CHECK_FILE)) {
|
|
1981
|
+
return readFileSync3(CHECK_FILE, "utf-8").trim();
|
|
1982
|
+
}
|
|
1983
|
+
} catch {
|
|
1984
|
+
}
|
|
1985
|
+
return null;
|
|
1986
|
+
}
|
|
1987
|
+
function saveCheckDate() {
|
|
1988
|
+
mkdirSync3(DATA_DIR, { recursive: true });
|
|
1989
|
+
writeFileSync3(CHECK_FILE, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "utf-8");
|
|
1990
|
+
}
|
|
1991
|
+
function isFirstUseToday() {
|
|
1992
|
+
const last = getLastCheckDate();
|
|
1993
|
+
if (last === null) return true;
|
|
1994
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1995
|
+
return last !== today;
|
|
1996
|
+
}
|
|
1997
|
+
function fetchLatestVersion() {
|
|
1998
|
+
return new Promise((resolve2) => {
|
|
1999
|
+
const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
|
|
2000
|
+
if (res.statusCode !== 200) {
|
|
2001
|
+
resolve2(null);
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
let data = "";
|
|
2005
|
+
res.on("data", (chunk) => {
|
|
2006
|
+
data += chunk.toString();
|
|
2007
|
+
});
|
|
2008
|
+
res.on("end", () => {
|
|
2009
|
+
try {
|
|
2010
|
+
resolve2(JSON.parse(data).version);
|
|
2011
|
+
} catch {
|
|
2012
|
+
resolve2(null);
|
|
2013
|
+
}
|
|
2014
|
+
});
|
|
2015
|
+
});
|
|
2016
|
+
req.on("error", () => {
|
|
2017
|
+
resolve2(null);
|
|
2018
|
+
});
|
|
2019
|
+
req.on("timeout", () => {
|
|
2020
|
+
req.destroy();
|
|
2021
|
+
resolve2(null);
|
|
2022
|
+
});
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
function detectUpgradeCommand() {
|
|
2026
|
+
const binPath = process.argv[1] || "";
|
|
2027
|
+
if (binPath.includes("/.bun/") || binPath.includes("/bun/")) {
|
|
2028
|
+
return `bun update -g ${PACKAGE_NAME}`;
|
|
2029
|
+
}
|
|
2030
|
+
if (binPath.includes("/.pnpm/") || binPath.includes("/pnpm/")) {
|
|
2031
|
+
return `pnpm update -g ${PACKAGE_NAME}`;
|
|
2032
|
+
}
|
|
2033
|
+
if (binPath.includes("/.yarn/") || binPath.includes("/yarn/")) {
|
|
2034
|
+
return `yarn global upgrade ${PACKAGE_NAME}`;
|
|
2035
|
+
}
|
|
2036
|
+
return `npm update -g ${PACKAGE_NAME}`;
|
|
2037
|
+
}
|
|
2038
|
+
function compareVersions(current, latest) {
|
|
2039
|
+
const a = current.split(".").map(Number);
|
|
2040
|
+
const b = latest.split(".").map(Number);
|
|
2041
|
+
for (let i = 0; i < 3; i++) {
|
|
2042
|
+
if ((a[i] || 0) < (b[i] || 0)) return -1;
|
|
2043
|
+
if ((a[i] || 0) > (b[i] || 0)) return 1;
|
|
2044
|
+
}
|
|
2045
|
+
return 0;
|
|
2046
|
+
}
|
|
2047
|
+
async function checkForUpdates(force) {
|
|
2048
|
+
if (!force && !isFirstUseToday()) return;
|
|
2049
|
+
const current = getCurrentVersion();
|
|
2050
|
+
const latest = await fetchLatestVersion();
|
|
2051
|
+
saveCheckDate();
|
|
2052
|
+
if (latest === null || compareVersions(current, latest) >= 0) return;
|
|
2053
|
+
const cmd = detectUpgradeCommand();
|
|
2054
|
+
const updateLine = `Update available: ${current} \u2192 ${latest}`;
|
|
2055
|
+
const cmdLine = `Run: ${cmd}`;
|
|
2056
|
+
const width = Math.max(updateLine.length, cmdLine.length) + 4;
|
|
2057
|
+
const pad = (text, visLen) => text + " ".repeat(Math.max(0, width - visLen));
|
|
2058
|
+
const border = "\u2500".repeat(width);
|
|
2059
|
+
const empty = " ".repeat(width);
|
|
2060
|
+
console.log("");
|
|
2061
|
+
console.log(` \u250C${border}\u2510`);
|
|
2062
|
+
console.log(` \u2502${empty}\u2502`);
|
|
2063
|
+
console.log(` \u2502 ${pad(`Update available: ${current} \u2192 \x1B[32m${latest}\x1B[0m`, updateLine.length + 2)}\u2502`);
|
|
2064
|
+
console.log(` \u2502${empty}\u2502`);
|
|
2065
|
+
console.log(` \u2502 ${pad(`Run: \x1B[36m${cmd}\x1B[0m`, cmdLine.length + 2)}\u2502`);
|
|
2066
|
+
console.log(` \u2502${empty}\u2502`);
|
|
2067
|
+
console.log(` \u2514${border}\u2518`);
|
|
2068
|
+
console.log("");
|
|
2069
|
+
}
|
|
2070
|
+
|
|
1958
2071
|
// src/cli.ts
|
|
1959
2072
|
function printUsage() {
|
|
1960
2073
|
console.log(`
|
|
@@ -1964,9 +2077,10 @@ Usage:
|
|
|
1964
2077
|
hotsheet [options]
|
|
1965
2078
|
|
|
1966
2079
|
Options:
|
|
1967
|
-
--port <number>
|
|
1968
|
-
--data-dir <path>
|
|
1969
|
-
--
|
|
2080
|
+
--port <number> Port to run on (default: 4174)
|
|
2081
|
+
--data-dir <path> Store data in an alternative location (default: .hotsheet/)
|
|
2082
|
+
--check-for-updates Check for new versions now
|
|
2083
|
+
--help Show this help message
|
|
1970
2084
|
|
|
1971
2085
|
Examples:
|
|
1972
2086
|
hotsheet
|
|
@@ -1976,9 +2090,10 @@ Examples:
|
|
|
1976
2090
|
}
|
|
1977
2091
|
function parseArgs(argv) {
|
|
1978
2092
|
const args = argv.slice(2);
|
|
1979
|
-
let
|
|
1980
|
-
let dataDir2 =
|
|
2093
|
+
let port2 = 4174;
|
|
2094
|
+
let dataDir2 = join7(process.cwd(), ".hotsheet");
|
|
1981
2095
|
let demo = null;
|
|
2096
|
+
let forceUpdateCheck = false;
|
|
1982
2097
|
for (let i = 0; i < args.length; i++) {
|
|
1983
2098
|
const arg = args[i];
|
|
1984
2099
|
if (arg.startsWith("--demo:")) {
|
|
@@ -1996,8 +2111,8 @@ function parseArgs(argv) {
|
|
|
1996
2111
|
process.exit(0);
|
|
1997
2112
|
break;
|
|
1998
2113
|
case "--port":
|
|
1999
|
-
|
|
2000
|
-
if (isNaN(
|
|
2114
|
+
port2 = parseInt(args[++i], 10);
|
|
2115
|
+
if (isNaN(port2)) {
|
|
2001
2116
|
console.error("Invalid port number");
|
|
2002
2117
|
process.exit(1);
|
|
2003
2118
|
}
|
|
@@ -2005,13 +2120,16 @@ function parseArgs(argv) {
|
|
|
2005
2120
|
case "--data-dir":
|
|
2006
2121
|
dataDir2 = resolve(args[++i]);
|
|
2007
2122
|
break;
|
|
2123
|
+
case "--check-for-updates":
|
|
2124
|
+
forceUpdateCheck = true;
|
|
2125
|
+
break;
|
|
2008
2126
|
default:
|
|
2009
2127
|
console.error(`Unknown option: ${arg}`);
|
|
2010
2128
|
printUsage();
|
|
2011
2129
|
process.exit(1);
|
|
2012
2130
|
}
|
|
2013
2131
|
}
|
|
2014
|
-
return { port, dataDir: dataDir2, demo };
|
|
2132
|
+
return { port: port2, dataDir: dataDir2, demo, forceUpdateCheck };
|
|
2015
2133
|
}
|
|
2016
2134
|
async function main() {
|
|
2017
2135
|
const parsed = parseArgs(process.argv);
|
|
@@ -2019,8 +2137,9 @@ async function main() {
|
|
|
2019
2137
|
printUsage();
|
|
2020
2138
|
process.exit(1);
|
|
2021
2139
|
}
|
|
2022
|
-
const { port, demo } = parsed;
|
|
2140
|
+
const { port: port2, demo, forceUpdateCheck } = parsed;
|
|
2023
2141
|
let { dataDir: dataDir2 } = parsed;
|
|
2142
|
+
await checkForUpdates(forceUpdateCheck);
|
|
2024
2143
|
if (demo !== null) {
|
|
2025
2144
|
const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
|
|
2026
2145
|
if (!scenario) {
|
|
@@ -2031,12 +2150,12 @@ async function main() {
|
|
|
2031
2150
|
}
|
|
2032
2151
|
process.exit(1);
|
|
2033
2152
|
}
|
|
2034
|
-
dataDir2 =
|
|
2153
|
+
dataDir2 = join7(tmpdir(), `hotsheet-demo-${demo}-${Date.now()}`);
|
|
2035
2154
|
console.log(`
|
|
2036
2155
|
DEMO MODE: ${scenario.label}
|
|
2037
2156
|
`);
|
|
2038
2157
|
}
|
|
2039
|
-
|
|
2158
|
+
mkdirSync4(dataDir2, { recursive: true });
|
|
2040
2159
|
if (demo === null) {
|
|
2041
2160
|
ensureGitignore(process.cwd());
|
|
2042
2161
|
}
|
|
@@ -2045,13 +2164,13 @@ async function main() {
|
|
|
2045
2164
|
if (demo !== null) {
|
|
2046
2165
|
await seedDemoData(demo);
|
|
2047
2166
|
}
|
|
2048
|
-
initMarkdownSync(dataDir2);
|
|
2167
|
+
initMarkdownSync(dataDir2, port2);
|
|
2049
2168
|
scheduleAllSync();
|
|
2050
2169
|
if (demo === null) {
|
|
2051
2170
|
await cleanupAttachments();
|
|
2052
2171
|
}
|
|
2053
2172
|
console.log(` Data directory: ${dataDir2}`);
|
|
2054
|
-
await startServer(
|
|
2173
|
+
await startServer(port2, dataDir2);
|
|
2055
2174
|
}
|
|
2056
2175
|
main().catch((err) => {
|
|
2057
2176
|
console.error(err);
|
|
@@ -1 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var re=Object.defineProperty;var le=(e,t,i)=>t in e?re(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i;var z=(e,t,i)=>le(e,typeof t!="symbol"?t+"":t,i);function F(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function j(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}var h=class{constructor(t){z(this,"__html");this.__html=t}toString(){return this.__html}};function b(e){return new h(e)}var de=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]);function D(e){return e==null||typeof e=="boolean"?"":e instanceof h?e.__html:typeof e=="string"?F(e):typeof e=="number"?String(e):Array.isArray(e)?e.map(D).join(""):""}function ce(e,t){if(t==null||t===!1)return"";if(t===!0)return` ${e}`;let i=e==="className"?"class":e==="htmlFor"?"for":e,s;return t instanceof h?s=t.__html:typeof t=="number"?s=String(t):typeof t=="string"?s=j(t):s="",` ${i}="${s}"`}function l(e,t){if(typeof e=="function")return e(t);let{children:i,...s}=t,a=Object.entries(s).map(([d,r])=>ce(d,r)).join("");if(de.has(e))return new h(`<${e}${a}>`);let o=i!=null?D(i):"";return new h(`<${e}${a}>${o}</${e}>`)}function L({children:e}){return new h(e!=null?D(e):"")}function f(e){let t=document.createElement("template");return t.innerHTML=e.toString(),t.content.firstElementChild}function V(e){document.getElementById("network-error-popup")?.remove();let t=f(l("div",{id:"network-error-popup",className:"error-popup",children:l("div",{className:"error-popup-content",children:[l("strong",{children:"Connection Error"}),l("p",{children:e}),l("button",{children:b("Dismiss")})]})}));t.querySelector("button").addEventListener("click",()=>t.remove()),document.body.appendChild(t)}async function c(e,t={}){try{return(await fetch("/api"+e,{headers:t.body!==void 0?{"Content-Type":"application/json"}:{},method:t.method,body:t.body!==void 0?JSON.stringify(t.body):void 0})).json()}catch(i){throw V("Unable to reach the server. It may have been stopped."),i}}async function W(e,t){try{let i=new FormData;return i.append("file",t),(await fetch("/api"+e,{method:"POST",body:i})).json()}catch(i){throw V("Unable to reach the server. It may have been stopped."),i}}var ue={detail_position:"side",detail_width:360,detail_height:300,trash_cleanup_days:3,verified_cleanup_days:30},n={tickets:[],selectedIds:new Set,lastClickedId:null,activeTicketId:null,view:"all",sortBy:"created",sortDir:"desc",search:"",settings:{...ue}},me={issue:"#6b7280",bug:"#ef4444",feature:"#22c55e",requirement_change:"#f97316",task:"#3b82f6",investigation:"#8b5cf6"},pe={issue:"ISS",bug:"BUG",feature:"FEA",requirement_change:"REQ",task:"TSK",investigation:"INV"},ye={highest:"\u2B06\u2B06",high:"\u2B06",default:"\u2014",low:"\u2B07",lowest:"\u2B07\u2B07"},fe={highest:"#ef4444",high:"#f97316",default:"#6b7280",low:"#3b82f6",lowest:"#94a3b8"},ge={not_started:"\u25CB",started:"\u25D4",completed:"\u2713",verified:"svg"},G='<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 7 17l-5-5"/><path d="m22 10-9.5 9.5-2-2"/></svg>';function k(e){return me[e]||"#6b7280"}function S(e){return pe[e]||"ISS"}function Y(e){return ye[e]||"\u2014"}function A(e){return fe[e]||"#6b7280"}function J(e){return ge[e]||"\u25CB"}function x(e){n.activeTicketId=e,Z(e)}function X(){n.selectedIds.clear(),n.activeTicketId=null;let e=new CustomEvent("hotsheet:render");document.dispatchEvent(e)}function Q(){let e=n.view==="trash",t=document.getElementById("detail-panel"),i=document.getElementById("detail-resize-handle");if(n.selectedIds.size===1&&!e){let s=Array.from(n.selectedIds)[0];t.style.display="flex",i&&(i.style.display=""),n.activeTicketId!==s&&(n.activeTicketId=s,Z(s))}else n.activeTicketId!=null&&(n.activeTicketId=null),t.style.display="none",i&&(i.style.display="none")}async function Z(e){let t=await c(`/tickets/${e}`);if(n.activeTicketId!==e)return;document.getElementById("detail-ticket-number").textContent=t.ticket_number,document.getElementById("detail-title").value=t.title,document.getElementById("detail-category").value=t.category,document.getElementById("detail-priority").value=t.priority,document.getElementById("detail-status").value=t.status,document.getElementById("detail-upnext").checked=t.up_next,document.getElementById("detail-details").value=t.details;let i=document.getElementById("detail-attachments");t.attachments.length>0?i.innerHTML=l(L,{children:t.attachments.map(r=>l("div",{className:"attachment-item",children:[l("span",{className:"attachment-name",children:r.original_filename}),l("button",{className:"attachment-delete","data-att-id":String(r.id),title:"Remove",children:b("×")})]}))}).toString():i.innerHTML="";let s=document.getElementById("detail-notes-section"),a=document.getElementById("detail-notes"),o=he(t.notes);o.length>0?(s.style.display="",a.innerHTML=l(L,{children:o.map(r=>l("div",{className:"note-entry",children:[r.created_at?l("div",{className:"note-timestamp",children:new Date(r.created_at).toLocaleString()}):null,l("div",{className:"note-text",children:r.text})]}))}).toString()):(s.style.display="none",a.innerHTML="");let d=document.getElementById("detail-meta");d.innerHTML=l(L,{children:[l("div",{children:["Created: ",new Date(t.created_at).toLocaleString()]}),l("div",{children:["Updated: ",new Date(t.updated_at).toLocaleString()]}),t.completed_at?l("div",{children:["Completed: ",new Date(t.completed_at).toLocaleString()]}):null,t.verified_at?l("div",{children:["Verified: ",new Date(t.verified_at).toLocaleString()]}):null]}).toString()}function he(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}async function ee(){try{let e=await c("/stats"),t=document.getElementById("status-bar");t&&(t.textContent=`${e.total} tickets \xB7 ${e.open} open \xB7 ${e.up_next} up next`)}catch{}}function P(e){let t=document.getElementById("content-area");t.classList.remove("detail-side","detail-bottom"),t.classList.add(e==="bottom"?"detail-bottom":"detail-side")}function N(){let e=document.getElementById("detail-panel");n.settings.detail_position==="bottom"?(e.style.width="",e.style.height=`${n.settings.detail_height}px`):(e.style.height="",e.style.width=`${n.settings.detail_width}px`)}function te(){let e=document.getElementById("detail-resize-handle"),t=document.getElementById("detail-panel"),i=document.getElementById("content-area"),s=!1;e.addEventListener("mousedown",a=>{a.preventDefault(),s=!0,document.body.style.cursor=n.settings.detail_position==="bottom"?"row-resize":"col-resize",document.body.style.userSelect="none"}),document.addEventListener("mousemove",a=>{if(!s)return;let o=i.getBoundingClientRect();if(n.settings.detail_position==="bottom"){let d=Math.max(150,Math.min(500,o.bottom-a.clientY));n.settings.detail_height=d,t.style.height=`${d}px`}else{let d=Math.max(250,Math.min(600,o.right-a.clientX));n.settings.detail_width=d,t.style.width=`${d}px`}}),document.addEventListener("mouseup",()=>{s&&(s=!1,document.body.style.cursor="",document.body.style.userSelect="",n.settings.detail_position==="bottom"?c("/settings",{method:"PATCH",body:{detail_height:String(n.settings.detail_height)}}):c("/settings",{method:"PATCH",body:{detail_width:String(n.settings.detail_width)}}))})}function _(e,t){let i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),a=window.innerWidth,o=window.innerHeight,d=i.left;d+s.width>a-8&&(d=i.right-s.width),d<8&&(d=8);let r=i.bottom+4;r+s.height>o-8&&(r=i.top-s.height-4),r<8&&(r=8),e.style.left=`${d}px`,e.style.top=`${r}px`}function H(e,t){let i=f(l("div",{className:"dropdown-menu",style:"visibility:hidden;top:0;left:0",children:t.map(r=>l("button",{className:`dropdown-item${r.active?" active":""}`,"data-key":r.key,children:[r.color?l("span",{className:"dropdown-dot",style:`background-color:${r.color}`}):null,l("span",{className:"dropdown-label",children:r.label}),r.shortcut?l("kbd",{className:"dropdown-kbd",children:r.shortcut}):null]}))}));i.querySelectorAll(".dropdown-item").forEach((r,u)=>{r.addEventListener("click",()=>{t[u].action(),i.remove()})});function a(r){let u=t.find(m=>r.key.toLowerCase()===m.key.toLowerCase());u?(r.preventDefault(),r.stopPropagation(),u.action(),o()):r.key==="Escape"&&(r.preventDefault(),o())}function o(){i.remove(),document.removeEventListener("keydown",a,!0),document.removeEventListener("click",d)}function d(){o()}return document.addEventListener("keydown",a,!0),setTimeout(()=>{document.addEventListener("click",d)},0),i}function C(){document.querySelectorAll(".dropdown-menu").forEach(e=>{e.remove()})}var $=null,I=!1,T=null,M=[{key:"i",value:"issue",label:"Issue"},{key:"b",value:"bug",label:"Bug"},{key:"f",value:"feature",label:"Feature"},{key:"r",value:"requirement_change",label:"Req Change"},{key:"k",value:"task",label:"Task"},{key:"g",value:"investigation",label:"Investigation"}],R=[{key:"1",value:"highest",label:"Highest"},{key:"2",value:"high",label:"High"},{key:"3",value:"default",label:"Default"},{key:"4",value:"low",label:"Low"},{key:"5",value:"lowest",label:"Lowest"}];function ve(){let e=document.activeElement;if(!e||!(e instanceof HTMLElement))return null;let t=e.closest(".ticket-row");if(!t)return null;if(t.classList.contains("draft-row"))return"draft";let i=t.dataset.id;return i!==void 0&&i!==""?parseInt(i,10):null}function be(e){e!=null&&(I=!0,e==="draft"?g():document.querySelector(`.ticket-row[data-id="${e}"] .ticket-title-input`)?.focus(),I=!1)}function y(){let e=n.view==="trash",t=ve(),i=document.getElementById("ticket-list");i.innerHTML="",e||i.appendChild(ke()),e&&n.tickets.length===0&&i.appendChild(f(l("div",{className:"ticket-list-empty",children:"Trash is empty"})));for(let s of n.tickets)i.appendChild(e?we(s):Ie(s));be(t),B(),ee()}function ke(){let e=se(),t=n.view.startsWith("category:"),i=f(l("div",{className:"ticket-row draft-row",children:[l("span",{className:"ticket-checkbox-spacer"}),l("span",{className:"ticket-status-btn draft-placeholder",children:"\u25CB"}),l("span",{className:"ticket-category-badge draft-badge",style:`background-color:${k(e)}${t?"":";cursor:pointer;opacity:1"}`,children:S(e)}),l("span",{className:"ticket-number draft-number"}),l("input",{type:"text",className:"ticket-title-input draft-input",placeholder:"New ticket..."}),l("span",{className:"ticket-priority-indicator draft-placeholder"}),l("span",{className:"ticket-star draft-placeholder"})]}));if(!t){let a=i.querySelector(".ticket-category-badge");a.addEventListener("click",o=>{o.stopPropagation(),Ee(a)})}let s=i.querySelector(".draft-input");return s.addEventListener("keydown",async a=>{if(a.key==="Enter"&&s.value.trim()){a.preventDefault();let o=s.value.trim();s.value="";let d=Te();T&&!n.view.startsWith("category:")&&(d.category=T),await c("/tickets",{method:"POST",body:{title:o,defaults:d}}),T=null,await p(),g()}else a.key==="ArrowDown"&&(a.preventDefault(),n.tickets.length>0&&document.querySelector(`.ticket-row[data-id="${n.tickets[0].id}"] .ticket-title-input`)?.focus())}),i}function g(){document.querySelector(".draft-row .draft-input")?.focus()}function Te(){let e=n.view;return e==="up-next"?{up_next:!0}:e==="open"?{}:e==="completed"?{status:"completed"}:e.startsWith("category:")?{category:e.split(":")[1]}:e.startsWith("priority:")?{priority:e.split(":")[1]}:{}}function se(){if(T)return T;let e=n.view;return e.startsWith("category:")?e.split(":")[1]:"issue"}function Ee(e){C();let i=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",s=se(),a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${i}${o.key.toUpperCase()}`,color:k(o.value),active:s===o.value,action:()=>{T=o.value,y(),g()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function Ie(e){let t=n.selectedIds.has(e.id),i=e.status==="completed"||e.status==="verified",s=e.status==="verified",a=f(l("div",{className:`ticket-row${t?" selected":""}${i?" completed":""}${e.up_next?" up-next":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("button",{className:`ticket-status-btn${s?" verified":""}`,title:e.status.replace("_"," "),children:s?b(G):J(e.status)}),l("span",{className:"ticket-category-badge",style:`background-color:${k(e.category)}`,title:e.category,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("input",{type:"text",className:"ticket-title-input",value:e.title}),l("span",{className:"ticket-priority-indicator",style:`color:${A(e.priority)}`,title:e.priority,children:Y(e.priority)}),l("button",{className:`ticket-star${e.up_next?" active":""}`,title:e.up_next?"Remove from Up Next":"Add to Up Next",children:e.up_next?"\u2605":"\u2606"})]}));a.addEventListener("mousedown",m=>{(m.metaKey||m.ctrlKey||m.shiftKey)&&(m.preventDefault(),ae(m,e)&&m.stopPropagation())});let o=a.querySelector(".ticket-checkbox");o.addEventListener("click",m=>m.stopPropagation()),o.addEventListener("change",()=>{o.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,y()}),a.querySelector(".ticket-status-btn").addEventListener("click",m=>{m.stopPropagation(),_e(e)});let d=a.querySelector(".ticket-category-badge");d.addEventListener("click",m=>{m.stopPropagation(),Be(d,e)});let r=a.querySelector(".ticket-title-input");r.addEventListener("focus",()=>{I||n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,O(),B())}),r.addEventListener("input",()=>{Me(e.id,{title:r.value})}),r.addEventListener("keydown",m=>{Le(m,e,r)});let u=a.querySelector(".ticket-priority-indicator");return u.addEventListener("click",m=>{m.stopPropagation(),De(u,e)}),a.querySelector(".ticket-star").addEventListener("click",m=>{m.stopPropagation(),He(e)}),a}function we(e){let t=n.selectedIds.has(e.id),i=e.deleted_at?new Date(e.deleted_at):null,s=f(l("div",{className:`ticket-row trash-row${t?" selected":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("span",{className:"ticket-category-badge",style:`background-color:${k(e.category)}`,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("span",{className:"ticket-title-input trash-title",style:"cursor:default",children:e.title}),l("span",{className:"ticket-number",title:i?`Deleted: ${i.toLocaleString()}`:"",children:i?i.toLocaleDateString():""}),l("button",{className:"btn btn-sm",title:"Restore from trash",children:"Restore"})]}));s.addEventListener("mousedown",o=>{(o.metaKey||o.ctrlKey||o.shiftKey)&&(o.preventDefault(),ae(o,e)&&o.stopPropagation())});let a=s.querySelector(".ticket-checkbox");return a.addEventListener("click",o=>o.stopPropagation()),a.addEventListener("change",()=>{a.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,y()}),s.querySelector(".trash-title").addEventListener("click",()=>{n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,O(),B())}),s.querySelector(".btn").addEventListener("click",async o=>{o.stopPropagation(),await c(`/tickets/${e.id}/restore`,{method:"POST"}),p()}),s}function ae(e,t){let i=e.metaKey||e.ctrlKey,s=e.shiftKey;if(i)n.selectedIds.has(t.id)?n.selectedIds.delete(t.id):n.selectedIds.add(t.id),n.lastClickedId=t.id,y();else if(s&&n.lastClickedId!=null){let a=n.tickets.map(r=>r.id),o=a.indexOf(n.lastClickedId),d=a.indexOf(t.id);if(o!==-1&&d!==-1){let r=Math.min(o,d),u=Math.max(o,d);n.selectedIds.clear();for(let m=r;m<=u;m++)n.selectedIds.add(a[m])}y()}else return!1;return!0}function Le(e,t,i){if(e.key==="Enter")e.preventDefault(),g();else if(e.key==="Backspace"&&i.value==="")e.preventDefault(),Ce(t.id);else if(e.key==="ArrowDown"&&e.shiftKey)e.preventDefault(),ne(t.id,1);else if(e.key==="ArrowUp"&&e.shiftKey)e.preventDefault(),ne(t.id,-1);else if(e.key==="ArrowDown")e.preventDefault(),Se(t.id);else if(e.key==="ArrowUp")e.preventDefault(),xe(t.id);else if((e.metaKey||e.ctrlKey)&&!e.altKey&&M.some(s=>s.key===e.key)){e.preventDefault();let s=M.find(a=>a.key===e.key);ie(t,"category",s.value)}else if(e.altKey&&!e.metaKey&&!e.ctrlKey&&R.some(s=>s.key===e.key)){e.preventDefault();let s=R.find(a=>a.key===e.key);ie(t,"priority",s.value)}}function Se(e){let t=n.tickets.findIndex(i=>i.id===e);t<n.tickets.length-1&&document.querySelector(`.ticket-row[data-id="${n.tickets[t+1].id}"] .ticket-title-input`)?.focus()}function xe(e){let t=n.tickets.findIndex(i=>i.id===e);t>0?document.querySelector(`.ticket-row[data-id="${n.tickets[t-1].id}"] .ticket-title-input`)?.focus():g()}function ne(e,t){let s=n.tickets.findIndex(d=>d.id===e)+t;if(s<0||s>=n.tickets.length)return;let a=n.tickets[s].id;n.selectedIds.add(e),n.selectedIds.has(a)?n.selectedIds.delete(e):n.selectedIds.add(a),I=!0,document.querySelector(`.ticket-row[data-id="${a}"] .ticket-title-input`)?.focus(),I=!1,O(),B()}async function _e(e){let i={not_started:"started",started:"completed",completed:"verified",verified:"not_started"}[e.status]||"not_started",s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:i}});Object.assign(e,s),y()}async function He(e){if(!e.up_next&&(e.status==="completed"||e.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?"))return;let i=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:"not_started",up_next:!0}});Object.assign(e,i),y();return}let t=await c(`/tickets/${e.id}/up-next`,{method:"POST"});Object.assign(e,t),y()}async function ie(e,t,i){let s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{[t]:i}});Object.assign(e,s),y()}async function Ce(e){let t=n.tickets.findIndex(i=>i.id===e);if(await c(`/tickets/${e}`,{method:"DELETE"}),n.tickets=n.tickets.filter(i=>i.id!==e),n.selectedIds.delete(e),y(),t>0&&n.tickets.length>0){let i=Math.min(t-1,n.tickets.length-1);document.querySelector(`.ticket-row[data-id="${n.tickets[i].id}"] .ticket-title-input`)?.focus()}else g()}function Me(e,t){$&&clearTimeout($),$=setTimeout(()=>{c(`/tickets/${e}`,{method:"PATCH",body:t})},300)}function Be(e,t){C();let s=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${s}${o.key.toUpperCase()}`,color:k(o.value),active:t.category===o.value,action:async()=>{let d=await c(`/tickets/${t.id}`,{method:"PATCH",body:{category:o.value}});Object.assign(t,d),y()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function De(e,t){C();let i=H(e,R.map(s=>({label:s.label,key:s.key,shortcut:`Alt+${s.key}`,color:A(s.value),active:t.priority===s.value,action:async()=>{let a=await c(`/tickets/${t.id}`,{method:"PATCH",body:{priority:s.value}});Object.assign(t,a),y()}})));document.body.appendChild(i),_(i,e),i.style.visibility=""}function O(){document.querySelectorAll(".ticket-row[data-id]").forEach(e=>{let t=parseInt(e.dataset.id,10),i=e.querySelector(".ticket-checkbox");n.selectedIds.has(t)?(e.classList.add("selected"),i&&(i.checked=!0)):(e.classList.remove("selected"),i&&(i.checked=!1))})}function B(){let e=n.selectedIds.size,t=n.tickets.length,i=e>0,s=n.view==="trash",a=document.getElementById("batch-select-all");a.checked=t>0&&e===t,a.indeterminate=e>0&&e<t,document.getElementById("batch-count").textContent=i?`${e} selected`:"";let o=["batch-category","batch-priority","batch-status","batch-upnext","batch-delete"];for(let v of o){let w=document.getElementById(v);w.style.display=s?"none":"",s||(w.disabled=!i)}let d=document.getElementById("batch-restore"),r=document.getElementById("batch-empty-trash");if(s){let v=document.getElementById("batch-toolbar");d||(d=f(l("button",{id:"batch-restore",className:"btn btn-sm",children:"Restore"})),d.addEventListener("click",async()=>{await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"restore"}}),n.selectedIds.clear(),p()}),v.insertBefore(d,document.getElementById("batch-count"))),d.disabled=!i,d.style.display="",r||(r=f(l("button",{id:"batch-empty-trash",className:"btn btn-sm btn-danger",children:"Empty Trash"})),r.addEventListener("click",async()=>{confirm("Permanently delete all items in trash? This cannot be undone.")&&(await c("/trash/empty",{method:"POST"}),n.selectedIds.clear(),p())}),v.insertBefore(r,document.getElementById("batch-count"))),r.disabled=t===0,r.style.display=""}else d&&(d.style.display="none"),r&&(r.style.display="none");let u=document.querySelector(".batch-star-icon"),m=document.getElementById("batch-upnext");if(!s&&u&&i){let v=n.tickets.filter(E=>n.selectedIds.has(E.id)),w=v.every(E=>E.up_next),oe=v.every(E=>!E.up_next);w?(u.textContent="\u2605",m.classList.add("active"),m.classList.remove("mixed")):oe?(u.textContent="\u2606",m.classList.remove("active","mixed")):(u.innerHTML=l("span",{className:"star-mixed-wrap",children:[l("span",{className:"star-mixed-fill",children:"\u2605"}),"\u2606"]}).toString(),m.classList.remove("active"),m.classList.add("mixed"))}else u&&(u.textContent="\u2606",m.classList.remove("active","mixed"));Q()}async function p(){let e=new URLSearchParams;n.view==="trash"?e.set("status","deleted"):n.view==="up-next"?e.set("up_next","true"):n.view==="open"?e.set("status","open"):n.view==="completed"?e.set("status","completed"):n.view==="verified"?e.set("status","verified"):n.view.startsWith("category:")?e.set("category",n.view.split(":")[1]):n.view.startsWith("priority:")&&e.set("priority",n.view.split(":")[1]),n.search&&e.set("search",n.search),e.set("sort_by",n.sortBy),e.set("sort_dir",n.sortDir);let t=e.toString();n.tickets=await c(`/tickets${t?"?"+t:""}`),y()}async function Ae(){await Pe(),await p(),Re(),Oe(),qe(),Ke(),Ue(),ze(),Ne(),$e(),te(),Fe(),document.addEventListener("hotsheet:render",()=>y()),g()}async function Pe(){try{let e=await c("/settings");(e.detail_position==="side"||e.detail_position==="bottom")&&(n.settings.detail_position=e.detail_position),e.detail_width&&(n.settings.detail_width=parseInt(e.detail_width,10)||360),e.detail_height&&(n.settings.detail_height=parseInt(e.detail_height,10)||300),e.trash_cleanup_days&&(n.settings.trash_cleanup_days=parseInt(e.trash_cleanup_days,10)||3),e.verified_cleanup_days&&(n.settings.verified_cleanup_days=parseInt(e.verified_cleanup_days,10)||30)}catch{}P(n.settings.detail_position),N()}function Ne(){let e=document.getElementById("settings-overlay"),t=document.getElementById("settings-close");document.getElementById("settings-btn").addEventListener("click",()=>{document.getElementById("settings-detail-position").value=n.settings.detail_position,document.getElementById("settings-trash-days").value=String(n.settings.trash_cleanup_days),document.getElementById("settings-verified-days").value=String(n.settings.verified_cleanup_days),e.style.display="flex"}),t.addEventListener("click",()=>{e.style.display="none"}),e.addEventListener("click",u=>{u.target===e&&(e.style.display="none")});let s=document.getElementById("settings-detail-position");s.addEventListener("change",()=>{n.settings.detail_position=s.value,P(n.settings.detail_position),N(),c("/settings",{method:"PATCH",body:{detail_position:s.value}})});let a=document.getElementById("settings-trash-days"),o=null;a.addEventListener("input",()=>{o&&clearTimeout(o),o=setTimeout(()=>{let u=Math.max(1,parseInt(a.value,10)||3);a.value=String(u),n.settings.trash_cleanup_days=u,c("/settings",{method:"PATCH",body:{trash_cleanup_days:String(u)}})},500)});let d=document.getElementById("settings-verified-days"),r=null;d.addEventListener("input",()=>{r&&clearTimeout(r),r=setTimeout(()=>{let u=Math.max(1,parseInt(d.value,10)||30);d.value=String(u),n.settings.verified_cleanup_days=u,c("/settings",{method:"PATCH",body:{verified_cleanup_days:String(u)}})},500)})}function $e(){let e=document.getElementById("copy-prompt-section"),t=document.getElementById("copy-prompt-btn"),i=document.getElementById("copy-prompt-label"),s=document.getElementById("copy-prompt-icon"),a="";c("/worklist-info").then(o=>{a=o.prompt,e.style.display="",o.skillCreated&&console.log("Hot Sheet: Created /hotsheet skill in .claude/skills/hotsheet/")}),t.addEventListener("click",()=>{a!==""&&navigator.clipboard.writeText(a).then(()=>{i.textContent="Copied!",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>',setTimeout(()=>{i.textContent="Copy AI prompt",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'},1500)})})}function Re(){let e=document.querySelectorAll(".sidebar-item[data-view]");e.forEach(t=>{t.addEventListener("click",()=>{e.forEach(i=>{i.classList.remove("active")}),t.classList.add("active"),n.view=t.dataset.view,n.selectedIds.clear(),p()})})}function Oe(){let e=document.getElementById("sort-select");e.addEventListener("change",()=>{let[t,i]=e.value.split(":");n.sortBy=t,n.sortDir=i,p()})}var q=null;function qe(){let e=document.getElementById("search-input");e.addEventListener("input",()=>{q&&clearTimeout(q),q=setTimeout(()=>{n.search=e.value,p()},200)}),e.addEventListener("keydown",t=>{t.key==="Escape"&&(e.value="",n.search="",p())})}function Ke(){let e=document.getElementById("batch-category");e.addEventListener("change",async()=>{e.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"category",value:e.value}}),e.value="",p())});let t=document.getElementById("batch-priority");t.addEventListener("change",async()=>{t.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"priority",value:t.value}}),t.value="",p())});let i=document.getElementById("batch-status");i.addEventListener("change",async()=>{i.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"status",value:i.value}}),i.value="",p())}),document.getElementById("batch-upnext").addEventListener("click",async()=>{let s=n.tickets.filter(d=>n.selectedIds.has(d.id)),o=!s.every(d=>d.up_next);if(o){let d=s.filter(r=>r.status==="completed"||r.status==="verified");if(d.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;await c("/tickets/batch",{method:"POST",body:{ids:d.map(r=>r.id),action:"status",value:"not_started"}})}}await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:o}}),p()}),document.getElementById("batch-delete").addEventListener("click",async()=>{let s=n.selectedIds.size;confirm(`Delete ${s} ticket(s)?`)&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"delete"}}),n.selectedIds.clear(),p())}),document.getElementById("batch-select-all").addEventListener("change",s=>{if(s.target.checked)for(let o of n.tickets)n.selectedIds.add(o.id);else n.selectedIds.clear();y()})}var K=null;function Ue(){document.getElementById("detail-close").addEventListener("click",X);let e=["detail-title","detail-details"];for(let i of e){let s=document.getElementById(i);s.addEventListener("input",()=>{K&&clearTimeout(K),K=setTimeout(()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}).then(()=>{p()})},300)})}let t=["detail-category","detail-priority","detail-status"];for(let i of t){let s=document.getElementById(i);s.addEventListener("change",async()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}),p()})}document.getElementById("detail-upnext").addEventListener("change",async()=>{if(n.activeTicketId==null)return;let i=n.tickets.find(a=>a.id===n.activeTicketId),s=document.getElementById("detail-upnext");if(s.checked&&i&&(i.status==="completed"||i.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?")){s.checked=!1;return}await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{status:"not_started",up_next:!0}})}else await c(`/tickets/${n.activeTicketId}/up-next`,{method:"POST"});p(),x(n.activeTicketId)}),document.getElementById("detail-file-input").addEventListener("change",async i=>{let s=i.target,a=s.files?.[0];!a||n.activeTicketId==null||(await W(`/tickets/${n.activeTicketId}/attachments`,a),s.value="",x(n.activeTicketId),p())}),document.getElementById("detail-attachments").addEventListener("click",async i=>{let a=i.target.closest(".attachment-delete");if(a===null)return;let o=a.dataset.attId;o===void 0||o===""||(await c(`/attachments/${o}`,{method:"DELETE"}),n.activeTicketId!=null&&x(n.activeTicketId))})}function ze(){document.addEventListener("keydown",e=>{let t=e.target.tagName,i=t==="INPUT"||t==="TEXTAREA"||t==="SELECT",s=document.getElementById("settings-overlay");if(e.key==="Escape"&&s.style.display!=="none"){s.style.display="none";return}if(e.key==="Escape"){n.selectedIds.size>0&&(n.selectedIds.clear(),y());return}if((e.metaKey||e.ctrlKey)&&e.key==="a"&&!i){e.preventDefault(),n.selectedIds.clear();for(let a of n.tickets)n.selectedIds.add(a.id);y();return}if((e.metaKey||e.ctrlKey)&&e.key==="d"){if(n.selectedIds.size>0){e.preventDefault();let a=n.tickets.filter(r=>n.selectedIds.has(r.id)),d=!a.every(r=>r.up_next);if(d){let r=a.filter(u=>u.status==="completed"||u.status==="verified");if(r.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;c("/tickets/batch",{method:"POST",body:{ids:r.map(u=>u.id),action:"status",value:"not_started"}}).then(()=>c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:!0}})).then(()=>{p()});return}}c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:d}}).then(()=>{p()})}return}if((e.metaKey||e.ctrlKey)&&e.key==="n"){e.preventDefault(),g();return}if((e.metaKey||e.ctrlKey)&&e.key==="f"){e.preventDefault(),document.getElementById("search-input").focus();return}if(e.key==="n"&&!i){e.preventDefault(),g();return}})}var U=0;function Fe(){async function e(){try{let t=await c(`/poll?version=${U}`);t.version>U&&(U=t.version,p())}catch{await new Promise(t=>setTimeout(t,5e3))}setTimeout(e,100)}e()}Ae();})();
|
|
1
|
+
"use strict";(()=>{var le=Object.defineProperty;var de=(e,t,i)=>t in e?le(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i;var j=(e,t,i)=>de(e,typeof t!="symbol"?t+"":t,i);function F(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function V(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}var h=class{constructor(t){j(this,"__html");this.__html=t}toString(){return this.__html}};function k(e){return new h(e)}var ce=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]);function D(e){return e==null||typeof e=="boolean"?"":e instanceof h?e.__html:typeof e=="string"?F(e):typeof e=="number"?String(e):Array.isArray(e)?e.map(D).join(""):""}function ue(e,t){if(t==null||t===!1)return"";if(t===!0)return` ${e}`;let i=e==="className"?"class":e==="htmlFor"?"for":e,s;return t instanceof h?s=t.__html:typeof t=="number"?s=String(t):typeof t=="string"?s=V(t):s="",` ${i}="${s}"`}function l(e,t){if(typeof e=="function")return e(t);let{children:i,...s}=t,a=Object.entries(s).map(([d,r])=>ue(d,r)).join("");if(ce.has(e))return new h(`<${e}${a}>`);let o=i!=null?D(i):"";return new h(`<${e}${a}>${o}</${e}>`)}function L({children:e}){return new h(e!=null?D(e):"")}function y(e){let t=document.createElement("template");return t.innerHTML=e.toString(),t.content.firstElementChild}function W(e){document.getElementById("network-error-popup")?.remove();let t=y(l("div",{id:"network-error-popup",className:"error-popup",children:l("div",{className:"error-popup-content",children:[l("strong",{children:"Connection Error"}),l("p",{children:e}),l("button",{children:k("Dismiss")})]})}));t.querySelector("button").addEventListener("click",()=>t.remove()),document.body.appendChild(t)}async function c(e,t={}){try{return(await fetch("/api"+e,{headers:t.body!==void 0?{"Content-Type":"application/json"}:{},method:t.method,body:t.body!==void 0?JSON.stringify(t.body):void 0})).json()}catch(i){throw W("Unable to reach the server. It may have been stopped."),i}}async function G(e,t){try{let i=new FormData;return i.append("file",t),(await fetch("/api"+e,{method:"POST",body:i})).json()}catch(i){throw W("Unable to reach the server. It may have been stopped."),i}}var pe={detail_position:"side",detail_width:360,detail_height:300,trash_cleanup_days:3,verified_cleanup_days:30},n={tickets:[],selectedIds:new Set,lastClickedId:null,activeTicketId:null,view:"all",sortBy:"created",sortDir:"desc",search:"",settings:{...pe}},me={issue:"#6b7280",bug:"#ef4444",feature:"#22c55e",requirement_change:"#f97316",task:"#3b82f6",investigation:"#8b5cf6"},fe={issue:"ISS",bug:"BUG",feature:"FEA",requirement_change:"REQ",task:"TSK",investigation:"INV"},ye={highest:"\u2B06\u2B06",high:"\u2B06",default:"\u2014",low:"\u2B07",lowest:"\u2B07\u2B07"},ge={highest:"#ef4444",high:"#f97316",default:"#6b7280",low:"#3b82f6",lowest:"#94a3b8"},he={not_started:"\u25CB",started:"\u25D4",completed:"\u2713",verified:"svg"},Y='<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 7 17l-5-5"/><path d="m22 10-9.5 9.5-2-2"/></svg>';function b(e){return me[e]||"#6b7280"}function S(e){return fe[e]||"ISS"}function J(e){return ye[e]||"\u2014"}function A(e){return ge[e]||"#6b7280"}function X(e){return he[e]||"\u25CB"}function x(e){n.activeTicketId=e,ee(e)}function Q(){n.selectedIds.clear(),n.activeTicketId=null;let e=new CustomEvent("hotsheet:render");document.dispatchEvent(e)}function Z(){let e=n.view==="trash",t=document.getElementById("detail-panel"),i=document.getElementById("detail-resize-handle");if(n.selectedIds.size===1&&!e){let s=Array.from(n.selectedIds)[0];t.style.display="flex",i&&(i.style.display=""),n.activeTicketId!==s&&(n.activeTicketId=s,ee(s))}else n.activeTicketId!=null&&(n.activeTicketId=null),t.style.display="none",i&&(i.style.display="none")}async function ee(e){let t=await c(`/tickets/${e}`);if(n.activeTicketId!==e)return;document.getElementById("detail-ticket-number").textContent=t.ticket_number,document.getElementById("detail-title").value=t.title,document.getElementById("detail-category").value=t.category,document.getElementById("detail-priority").value=t.priority,document.getElementById("detail-status").value=t.status,document.getElementById("detail-upnext").checked=t.up_next,document.getElementById("detail-details").value=t.details;let i=document.getElementById("detail-attachments");t.attachments.length>0?i.innerHTML=l(L,{children:t.attachments.map(r=>l("div",{className:"attachment-item",children:[l("span",{className:"attachment-name",children:r.original_filename}),l("button",{className:"attachment-delete","data-att-id":String(r.id),title:"Remove",children:k("×")})]}))}).toString():i.innerHTML="";let s=document.getElementById("detail-notes-section"),a=document.getElementById("detail-notes"),o=ve(t.notes);o.length>0?(s.style.display="",a.innerHTML=l(L,{children:o.map(r=>l("div",{className:"note-entry",children:[r.created_at?l("div",{className:"note-timestamp",children:new Date(r.created_at).toLocaleString()}):null,l("div",{className:"note-text",children:r.text})]}))}).toString()):(s.style.display="none",a.innerHTML="");let d=document.getElementById("detail-meta");d.innerHTML=l(L,{children:[l("div",{children:["Created: ",new Date(t.created_at).toLocaleString()]}),l("div",{children:["Updated: ",new Date(t.updated_at).toLocaleString()]}),t.completed_at?l("div",{children:["Completed: ",new Date(t.completed_at).toLocaleString()]}):null,t.verified_at?l("div",{children:["Verified: ",new Date(t.verified_at).toLocaleString()]}):null]}).toString()}function ve(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}async function te(){try{let e=await c("/stats"),t=document.getElementById("status-bar");t&&(t.textContent=`${e.total} tickets \xB7 ${e.open} open \xB7 ${e.up_next} up next`)}catch{}}function N(e){let t=document.getElementById("content-area");t.classList.remove("detail-side","detail-bottom"),t.classList.add(e==="bottom"?"detail-bottom":"detail-side")}function P(){let e=document.getElementById("detail-panel");n.settings.detail_position==="bottom"?(e.style.width="",e.style.height=`${n.settings.detail_height}px`):(e.style.height="",e.style.width=`${n.settings.detail_width}px`)}function ne(){let e=document.getElementById("detail-resize-handle"),t=document.getElementById("detail-panel"),i=document.getElementById("content-area"),s=!1;e.addEventListener("mousedown",a=>{a.preventDefault(),s=!0,document.body.style.cursor=n.settings.detail_position==="bottom"?"row-resize":"col-resize",document.body.style.userSelect="none"}),document.addEventListener("mousemove",a=>{if(!s)return;let o=i.getBoundingClientRect();if(n.settings.detail_position==="bottom"){let d=Math.max(150,Math.min(500,o.bottom-a.clientY));n.settings.detail_height=d,t.style.height=`${d}px`}else{let d=Math.max(250,Math.min(600,o.right-a.clientX));n.settings.detail_width=d,t.style.width=`${d}px`}}),document.addEventListener("mouseup",()=>{s&&(s=!1,document.body.style.cursor="",document.body.style.userSelect="",n.settings.detail_position==="bottom"?c("/settings",{method:"PATCH",body:{detail_height:String(n.settings.detail_height)}}):c("/settings",{method:"PATCH",body:{detail_width:String(n.settings.detail_width)}}))})}function _(e,t){let i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),a=window.innerWidth,o=window.innerHeight,d=i.left;d+s.width>a-8&&(d=i.right-s.width),d<8&&(d=8);let r=i.bottom+4;r+s.height>o-8&&(r=i.top-s.height-4),r<8&&(r=8),e.style.left=`${d}px`,e.style.top=`${r}px`}function H(e,t){let i=y(l("div",{className:"dropdown-menu",style:"visibility:hidden;top:0;left:0",children:t.map(r=>l("button",{className:`dropdown-item${r.active?" active":""}`,"data-key":r.key,children:[r.color?l("span",{className:"dropdown-dot",style:`background-color:${r.color}`}):null,l("span",{className:"dropdown-label",children:r.label}),r.shortcut?l("kbd",{className:"dropdown-kbd",children:r.shortcut}):null]}))}));i.querySelectorAll(".dropdown-item").forEach((r,u)=>{r.addEventListener("click",()=>{t[u].action(),i.remove()})});function a(r){let u=t.find(p=>r.key.toLowerCase()===p.key.toLowerCase());u?(r.preventDefault(),r.stopPropagation(),u.action(),o()):r.key==="Escape"&&(r.preventDefault(),o())}function o(){i.remove(),document.removeEventListener("keydown",a,!0),document.removeEventListener("click",d)}function d(){o()}return document.addEventListener("keydown",a,!0),setTimeout(()=>{document.addEventListener("click",d)},0),i}function C(){document.querySelectorAll(".dropdown-menu").forEach(e=>{e.remove()})}var $=null,E=!1,I=null,R="",M=[{key:"i",value:"issue",label:"Issue"},{key:"b",value:"bug",label:"Bug"},{key:"f",value:"feature",label:"Feature"},{key:"r",value:"requirement_change",label:"Req Change"},{key:"k",value:"task",label:"Task"},{key:"g",value:"investigation",label:"Investigation"}],O=[{key:"1",value:"highest",label:"Highest"},{key:"2",value:"high",label:"High"},{key:"3",value:"default",label:"Default"},{key:"4",value:"low",label:"Low"},{key:"5",value:"lowest",label:"Lowest"}];function ke(){let e=document.activeElement;if(!e||!(e instanceof HTMLElement))return null;let t=e.closest(".ticket-row");if(!t)return null;if(t.classList.contains("draft-row"))return"draft";let i=t.dataset.id;return i!==void 0&&i!==""?parseInt(i,10):null}function be(e){e!=null&&(E=!0,e==="draft"?g():document.querySelector(`.ticket-row[data-id="${e}"] .ticket-title-input`)?.focus(),E=!1)}function f(){let e=n.view==="trash",t=ke(),i=null;if(t!=null&&t!=="draft"){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&(i=a.value)}let s=document.getElementById("ticket-list");s.innerHTML="",e||s.appendChild(Te()),e&&n.tickets.length===0&&s.appendChild(y(l("div",{className:"ticket-list-empty",children:"Trash is empty"})));for(let a of n.tickets)s.appendChild(e?Le(a):we(a));if(t!=null&&t!=="draft"&&i!=null){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&a.value!==i&&(a.value=i)}be(t),B(),te()}function Te(){let e=ae(),t=n.view.startsWith("category:"),i=y(l("div",{className:"ticket-row draft-row",children:[l("span",{className:"ticket-checkbox-spacer"}),l("span",{className:"ticket-status-btn draft-placeholder",children:"\u25CB"}),l("span",{className:"ticket-category-badge draft-badge",style:`background-color:${b(e)}${t?"":";cursor:pointer;opacity:1"}`,children:S(e)}),l("span",{className:"ticket-number draft-number"}),l("input",{type:"text",className:"ticket-title-input draft-input",placeholder:"New ticket...",value:R}),l("span",{className:"ticket-priority-indicator draft-placeholder"}),l("span",{className:"ticket-star draft-placeholder"})]}));if(!t){let a=i.querySelector(".ticket-category-badge");a.addEventListener("click",o=>{o.stopPropagation(),Ie(a)})}let s=i.querySelector(".draft-input");return s.addEventListener("input",()=>{R=s.value}),s.addEventListener("keydown",async a=>{if(a.key==="Enter"&&s.value.trim()){a.preventDefault();let o=s.value.trim();R="",s.value="";let d=Ee();I&&!n.view.startsWith("category:")&&(d.category=I);let r=await c("/tickets",{method:"POST",body:{title:o,defaults:d}});r&&(n.selectedIds.clear(),n.selectedIds.add(r.id)),await m(),g()}else a.key==="ArrowDown"&&(a.preventDefault(),n.tickets.length>0&&document.querySelector(`.ticket-row[data-id="${n.tickets[0].id}"] .ticket-title-input`)?.focus())}),i}function g(){document.querySelector(".draft-row .draft-input")?.focus()}function Ee(){let e=n.view;return e==="up-next"?{up_next:!0}:e==="open"?{}:e==="completed"?{status:"completed"}:e.startsWith("category:")?{category:e.split(":")[1]}:e.startsWith("priority:")?{priority:e.split(":")[1]}:{}}function ae(){if(I)return I;let e=n.view;return e.startsWith("category:")?e.split(":")[1]:"issue"}function Ie(e){C();let i=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",s=ae(),a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${i}${o.key.toUpperCase()}`,color:b(o.value),active:s===o.value,action:()=>{I=o.value,f(),g()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function we(e){let t=n.selectedIds.has(e.id),i=e.status==="completed"||e.status==="verified",s=e.status==="verified",a=y(l("div",{className:`ticket-row${t?" selected":""}${i?" completed":""}${e.up_next?" up-next":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("button",{className:`ticket-status-btn${s?" verified":""}`,title:e.status.replace("_"," "),children:s?k(Y):X(e.status)}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,title:e.category,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("input",{type:"text",className:"ticket-title-input",value:e.title}),l("span",{className:"ticket-priority-indicator",style:`color:${A(e.priority)}`,title:e.priority,children:J(e.priority)}),l("button",{className:`ticket-star${e.up_next?" active":""}`,title:e.up_next?"Remove from Up Next":"Add to Up Next",children:e.up_next?"\u2605":"\u2606"})]}));a.addEventListener("mousedown",p=>{(p.metaKey||p.ctrlKey||p.shiftKey)&&(p.preventDefault(),oe(p,e)&&p.stopPropagation())});let o=a.querySelector(".ticket-checkbox");o.addEventListener("click",p=>p.stopPropagation()),o.addEventListener("change",()=>{o.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),a.querySelector(".ticket-status-btn").addEventListener("click",p=>{p.stopPropagation(),He(e)});let d=a.querySelector(".ticket-category-badge");d.addEventListener("click",p=>{p.stopPropagation(),De(d,e)});let r=a.querySelector(".ticket-title-input");r.addEventListener("focus",()=>{E||n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,q(),B())}),r.addEventListener("input",()=>{Be(e.id,{title:r.value})}),r.addEventListener("keydown",p=>{Se(p,e,r)});let u=a.querySelector(".ticket-priority-indicator");return u.addEventListener("click",p=>{p.stopPropagation(),Ae(u,e)}),a.querySelector(".ticket-star").addEventListener("click",p=>{p.stopPropagation(),Ce(e)}),a}function Le(e){let t=n.selectedIds.has(e.id),i=e.deleted_at?new Date(e.deleted_at):null,s=y(l("div",{className:`ticket-row trash-row${t?" selected":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("span",{className:"ticket-title-input trash-title",style:"cursor:default",children:e.title}),l("span",{className:"ticket-number",title:i?`Deleted: ${i.toLocaleString()}`:"",children:i?i.toLocaleDateString():""}),l("button",{className:"btn btn-sm",title:"Restore from trash",children:"Restore"})]}));s.addEventListener("mousedown",o=>{(o.metaKey||o.ctrlKey||o.shiftKey)&&(o.preventDefault(),oe(o,e)&&o.stopPropagation())});let a=s.querySelector(".ticket-checkbox");return a.addEventListener("click",o=>o.stopPropagation()),a.addEventListener("change",()=>{a.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),s.querySelector(".trash-title").addEventListener("click",()=>{n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,q(),B())}),s.querySelector(".btn").addEventListener("click",async o=>{o.stopPropagation(),await c(`/tickets/${e.id}/restore`,{method:"POST"}),m()}),s}function oe(e,t){let i=e.metaKey||e.ctrlKey,s=e.shiftKey;if(i)n.selectedIds.has(t.id)?n.selectedIds.delete(t.id):n.selectedIds.add(t.id),n.lastClickedId=t.id,f();else if(s&&n.lastClickedId!=null){let a=n.tickets.map(r=>r.id),o=a.indexOf(n.lastClickedId),d=a.indexOf(t.id);if(o!==-1&&d!==-1){let r=Math.min(o,d),u=Math.max(o,d);n.selectedIds.clear();for(let p=r;p<=u;p++)n.selectedIds.add(a[p])}f()}else return!1;return!0}function Se(e,t,i){if(e.key==="Enter")e.preventDefault(),g();else if(e.key==="Backspace"&&i.value==="")e.preventDefault(),Me(t.id);else if(e.key==="ArrowDown"&&e.shiftKey)e.preventDefault(),ie(t.id,1);else if(e.key==="ArrowUp"&&e.shiftKey)e.preventDefault(),ie(t.id,-1);else if(e.key==="ArrowDown")e.preventDefault(),xe(t.id);else if(e.key==="ArrowUp")e.preventDefault(),_e(t.id);else if((e.metaKey||e.ctrlKey)&&!e.altKey&&M.some(s=>s.key===e.key)){e.preventDefault();let s=M.find(a=>a.key===e.key);se(t,"category",s.value)}else if(e.altKey&&!e.metaKey&&!e.ctrlKey&&O.some(s=>s.key===e.key)){e.preventDefault();let s=O.find(a=>a.key===e.key);se(t,"priority",s.value)}}function xe(e){let t=n.tickets.findIndex(i=>i.id===e);t<n.tickets.length-1&&document.querySelector(`.ticket-row[data-id="${n.tickets[t+1].id}"] .ticket-title-input`)?.focus()}function _e(e){let t=n.tickets.findIndex(i=>i.id===e);t>0?document.querySelector(`.ticket-row[data-id="${n.tickets[t-1].id}"] .ticket-title-input`)?.focus():g()}function ie(e,t){let s=n.tickets.findIndex(d=>d.id===e)+t;if(s<0||s>=n.tickets.length)return;let a=n.tickets[s].id;n.selectedIds.add(e),n.selectedIds.has(a)?n.selectedIds.delete(e):n.selectedIds.add(a),E=!0,document.querySelector(`.ticket-row[data-id="${a}"] .ticket-title-input`)?.focus(),E=!1,q(),B()}async function He(e){let i={not_started:"started",started:"completed",completed:"verified",verified:"not_started"}[e.status]||"not_started",s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:i}});Object.assign(e,s),f()}async function Ce(e){if(!e.up_next&&(e.status==="completed"||e.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?"))return;let i=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:"not_started",up_next:!0}});Object.assign(e,i),f();return}let t=await c(`/tickets/${e.id}/up-next`,{method:"POST"});Object.assign(e,t),f()}async function se(e,t,i){let s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{[t]:i}});Object.assign(e,s),f()}async function Me(e){let t=n.tickets.findIndex(i=>i.id===e);if(await c(`/tickets/${e}`,{method:"DELETE"}),n.tickets=n.tickets.filter(i=>i.id!==e),n.selectedIds.delete(e),f(),t>0&&n.tickets.length>0){let i=Math.min(t-1,n.tickets.length-1);document.querySelector(`.ticket-row[data-id="${n.tickets[i].id}"] .ticket-title-input`)?.focus()}else g()}function Be(e,t){$&&clearTimeout($),$=setTimeout(()=>{c(`/tickets/${e}`,{method:"PATCH",body:t})},300)}function De(e,t){C();let s=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${s}${o.key.toUpperCase()}`,color:b(o.value),active:t.category===o.value,action:async()=>{let d=await c(`/tickets/${t.id}`,{method:"PATCH",body:{category:o.value}});Object.assign(t,d),f()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function Ae(e,t){C();let i=H(e,O.map(s=>({label:s.label,key:s.key,shortcut:`Alt+${s.key}`,color:A(s.value),active:t.priority===s.value,action:async()=>{let a=await c(`/tickets/${t.id}`,{method:"PATCH",body:{priority:s.value}});Object.assign(t,a),f()}})));document.body.appendChild(i),_(i,e),i.style.visibility=""}function q(){document.querySelectorAll(".ticket-row[data-id]").forEach(e=>{let t=parseInt(e.dataset.id,10),i=e.querySelector(".ticket-checkbox");n.selectedIds.has(t)?(e.classList.add("selected"),i&&(i.checked=!0)):(e.classList.remove("selected"),i&&(i.checked=!1))})}function B(){let e=n.selectedIds.size,t=n.tickets.length,i=e>0,s=n.view==="trash",a=document.getElementById("batch-select-all");a.checked=t>0&&e===t,a.indeterminate=e>0&&e<t,document.getElementById("batch-count").textContent=i?`${e} selected`:"";let o=["batch-category","batch-priority","batch-status","batch-upnext","batch-delete"];for(let v of o){let w=document.getElementById(v);w.style.display=s?"none":"",s||(w.disabled=!i)}let d=document.getElementById("batch-restore"),r=document.getElementById("batch-empty-trash");if(s){let v=document.getElementById("batch-toolbar");d||(d=y(l("button",{id:"batch-restore",className:"btn btn-sm",children:"Restore"})),d.addEventListener("click",async()=>{await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"restore"}}),n.selectedIds.clear(),m()}),v.insertBefore(d,document.getElementById("batch-count"))),d.disabled=!i,d.style.display="",r||(r=y(l("button",{id:"batch-empty-trash",className:"btn btn-sm btn-danger",children:"Empty Trash"})),r.addEventListener("click",async()=>{confirm("Permanently delete all items in trash? This cannot be undone.")&&(await c("/trash/empty",{method:"POST"}),n.selectedIds.clear(),m())}),v.insertBefore(r,document.getElementById("batch-count"))),r.disabled=t===0,r.style.display=""}else d&&(d.style.display="none"),r&&(r.style.display="none");let u=document.querySelector(".batch-star-icon"),p=document.getElementById("batch-upnext");if(!s&&u&&i){let v=n.tickets.filter(T=>n.selectedIds.has(T.id)),w=v.every(T=>T.up_next),re=v.every(T=>!T.up_next);w?(u.textContent="\u2605",p.classList.add("active"),p.classList.remove("mixed")):re?(u.textContent="\u2606",p.classList.remove("active","mixed")):(u.innerHTML=l("span",{className:"star-mixed-wrap",children:[l("span",{className:"star-mixed-fill",children:"\u2605"}),"\u2606"]}).toString(),p.classList.remove("active"),p.classList.add("mixed"))}else u&&(u.textContent="\u2606",p.classList.remove("active","mixed"));Z()}async function m(){let e=new URLSearchParams;n.view==="trash"?e.set("status","deleted"):n.view==="up-next"?e.set("up_next","true"):n.view==="open"?e.set("status","open"):n.view==="completed"?e.set("status","completed"):n.view==="verified"?e.set("status","verified"):n.view.startsWith("category:")?e.set("category",n.view.split(":")[1]):n.view.startsWith("priority:")&&e.set("priority",n.view.split(":")[1]),n.search&&e.set("search",n.search),e.set("sort_by",n.sortBy),e.set("sort_dir",n.sortDir);let t=e.toString();n.tickets=await c(`/tickets${t?"?"+t:""}`),f()}async function Ne(){await Pe(),await m(),Oe(),qe(),Ke(),Ue(),ze(),Ve(),$e(),Re(),ne(),We(),document.addEventListener("hotsheet:render",()=>f()),g()}async function Pe(){try{let e=await c("/settings");(e.detail_position==="side"||e.detail_position==="bottom")&&(n.settings.detail_position=e.detail_position),e.detail_width&&(n.settings.detail_width=parseInt(e.detail_width,10)||360),e.detail_height&&(n.settings.detail_height=parseInt(e.detail_height,10)||300),e.trash_cleanup_days&&(n.settings.trash_cleanup_days=parseInt(e.trash_cleanup_days,10)||3),e.verified_cleanup_days&&(n.settings.verified_cleanup_days=parseInt(e.verified_cleanup_days,10)||30)}catch{}N(n.settings.detail_position),P()}function $e(){let e=document.getElementById("settings-overlay"),t=document.getElementById("settings-close");document.getElementById("settings-btn").addEventListener("click",()=>{document.getElementById("settings-detail-position").value=n.settings.detail_position,document.getElementById("settings-trash-days").value=String(n.settings.trash_cleanup_days),document.getElementById("settings-verified-days").value=String(n.settings.verified_cleanup_days),e.style.display="flex"}),t.addEventListener("click",()=>{e.style.display="none"}),e.addEventListener("click",u=>{u.target===e&&(e.style.display="none")});let s=document.getElementById("settings-detail-position");s.addEventListener("change",()=>{n.settings.detail_position=s.value,N(n.settings.detail_position),P(),c("/settings",{method:"PATCH",body:{detail_position:s.value}})});let a=document.getElementById("settings-trash-days"),o=null;a.addEventListener("input",()=>{o&&clearTimeout(o),o=setTimeout(()=>{let u=Math.max(1,parseInt(a.value,10)||3);a.value=String(u),n.settings.trash_cleanup_days=u,c("/settings",{method:"PATCH",body:{trash_cleanup_days:String(u)}})},500)});let d=document.getElementById("settings-verified-days"),r=null;d.addEventListener("input",()=>{r&&clearTimeout(r),r=setTimeout(()=>{let u=Math.max(1,parseInt(d.value,10)||30);d.value=String(u),n.settings.verified_cleanup_days=u,c("/settings",{method:"PATCH",body:{verified_cleanup_days:String(u)}})},500)})}function Re(){let e=document.getElementById("copy-prompt-section"),t=document.getElementById("copy-prompt-btn"),i=document.getElementById("copy-prompt-label"),s=document.getElementById("copy-prompt-icon"),a="";c("/worklist-info").then(o=>{a=o.prompt,e.style.display="",o.skillCreated&&console.log("Hot Sheet: Created /hotsheet skill in .claude/skills/hotsheet/")}),t.addEventListener("click",()=>{a!==""&&navigator.clipboard.writeText(a).then(()=>{i.textContent="Copied!",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>',setTimeout(()=>{i.textContent="Copy AI prompt",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'},1500)})})}function Oe(){let e=document.querySelectorAll(".sidebar-item[data-view]");e.forEach(t=>{t.addEventListener("click",()=>{e.forEach(i=>{i.classList.remove("active")}),t.classList.add("active"),n.view=t.dataset.view,n.selectedIds.clear(),m()})})}function qe(){let e=document.getElementById("sort-select");e.addEventListener("change",()=>{let[t,i]=e.value.split(":");n.sortBy=t,n.sortDir=i,m()})}var K=null;function Ke(){let e=document.getElementById("search-input");e.addEventListener("input",()=>{K&&clearTimeout(K),K=setTimeout(()=>{n.search=e.value,m()},200)}),e.addEventListener("keydown",t=>{t.key==="Escape"&&(e.value="",n.search="",m())})}function Ue(){let e=document.getElementById("batch-category");e.addEventListener("change",async()=>{e.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"category",value:e.value}}),e.value="",m())});let t=document.getElementById("batch-priority");t.addEventListener("change",async()=>{t.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"priority",value:t.value}}),t.value="",m())});let i=document.getElementById("batch-status");i.addEventListener("change",async()=>{i.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"status",value:i.value}}),i.value="",m())}),document.getElementById("batch-upnext").addEventListener("click",async()=>{let s=n.tickets.filter(d=>n.selectedIds.has(d.id)),o=!s.every(d=>d.up_next);if(o){let d=s.filter(r=>r.status==="completed"||r.status==="verified");if(d.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;await c("/tickets/batch",{method:"POST",body:{ids:d.map(r=>r.id),action:"status",value:"not_started"}})}}await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:o}}),m()}),document.getElementById("batch-delete").addEventListener("click",async()=>{let s=n.selectedIds.size;confirm(`Delete ${s} ticket(s)?`)&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"delete"}}),n.selectedIds.clear(),m())}),document.getElementById("batch-select-all").addEventListener("change",s=>{if(s.target.checked)for(let o of n.tickets)n.selectedIds.add(o.id);else n.selectedIds.clear();f()})}var U=null;function ze(){document.getElementById("detail-close").addEventListener("click",Q);let e=["detail-title","detail-details"];for(let i of e){let s=document.getElementById(i);s.addEventListener("input",()=>{U&&clearTimeout(U),U=setTimeout(()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}).then(()=>{m()})},300)})}let t=["detail-category","detail-priority","detail-status"];for(let i of t){let s=document.getElementById(i);s.addEventListener("change",async()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}),m()})}document.getElementById("detail-upnext").addEventListener("change",async()=>{if(n.activeTicketId==null)return;let i=n.tickets.find(a=>a.id===n.activeTicketId),s=document.getElementById("detail-upnext");if(s.checked&&i&&(i.status==="completed"||i.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?")){s.checked=!1;return}await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{status:"not_started",up_next:!0}})}else await c(`/tickets/${n.activeTicketId}/up-next`,{method:"POST"});m(),x(n.activeTicketId)}),document.getElementById("detail-file-input").addEventListener("change",async i=>{let s=i.target,a=s.files?.[0];!a||n.activeTicketId==null||(await G(`/tickets/${n.activeTicketId}/attachments`,a),s.value="",x(n.activeTicketId),m())}),document.getElementById("detail-attachments").addEventListener("click",async i=>{let a=i.target.closest(".attachment-delete");if(a===null)return;let o=a.dataset.attId;o===void 0||o===""||(await c(`/attachments/${o}`,{method:"DELETE"}),n.activeTicketId!=null&&x(n.activeTicketId))})}function je(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}function Fe(e){let t=[];t.push(`${e.ticket_number}: ${e.title}`),e.details.trim()&&(t.push(""),t.push(e.details.trim()));let i=je(e.notes);if(i.length>0){t.push("");for(let s of i)t.push(`- ${s.text}`)}return t.join(`
|
|
2
|
+
`)}function Ve(){document.addEventListener("keydown",e=>{let t=e.target.tagName,i=t==="INPUT"||t==="TEXTAREA"||t==="SELECT",s=document.getElementById("settings-overlay");if(e.key==="Escape"&&s.style.display!=="none"){s.style.display="none";return}if(e.key==="Escape"){n.selectedIds.size>0&&(n.selectedIds.clear(),f());return}if((e.metaKey||e.ctrlKey)&&e.key==="a"&&!i){e.preventDefault(),n.selectedIds.clear();for(let a of n.tickets)n.selectedIds.add(a.id);f();return}if((e.metaKey||e.ctrlKey)&&e.key==="d"){if(n.selectedIds.size>0){e.preventDefault();let a=n.tickets.filter(r=>n.selectedIds.has(r.id)),d=!a.every(r=>r.up_next);if(d){let r=a.filter(u=>u.status==="completed"||u.status==="verified");if(r.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;c("/tickets/batch",{method:"POST",body:{ids:r.map(u=>u.id),action:"status",value:"not_started"}}).then(()=>c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:!0}})).then(()=>{m()});return}}c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:d}}).then(()=>{m()})}return}if((e.metaKey||e.ctrlKey)&&e.key==="c"&&n.selectedIds.size>0){let a=window.getSelection();if(!a||a.isCollapsed||a.toString().trim()===""){e.preventDefault();let d=n.tickets.filter(r=>n.selectedIds.has(r.id)).map(Fe).join(`
|
|
3
|
+
|
|
4
|
+
`);navigator.clipboard.writeText(d);return}}if((e.metaKey||e.ctrlKey)&&e.key==="n"){e.preventDefault(),g();return}if((e.metaKey||e.ctrlKey)&&e.key==="f"){e.preventDefault(),document.getElementById("search-input").focus();return}if(e.key==="n"&&!i){e.preventDefault(),g();return}})}var z=0;function We(){async function e(){try{let t=await c(`/poll?version=${z}`);t.version>z&&(z=t.version,m())}catch{await new Promise(t=>setTimeout(t,5e3))}setTimeout(e,100)}e()}Ne();})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hotsheet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A lightweight local project management tool. Create, categorize, and prioritize tickets with a fast bullet-list interface, then export an Up Next worklist for AI tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|