Spaces:
Running
Running
Update templates/index.html
Browse files- templates/index.html +86 -1
templates/index.html
CHANGED
|
@@ -139,6 +139,24 @@
|
|
| 139 |
gap: 8px;
|
| 140 |
}
|
| 141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
.pill {
|
| 143 |
border: 1px solid var(--line);
|
| 144 |
background: #18233d;
|
|
@@ -457,6 +475,8 @@
|
|
| 457 |
.btn, .pill { width: 100%; text-align: center; }
|
| 458 |
.pager-actions { width: 100%; }
|
| 459 |
.pager-actions .btn { flex: 1; }
|
|
|
|
|
|
|
| 460 |
}
|
| 461 |
</style>
|
| 462 |
</head>
|
|
@@ -485,6 +505,15 @@
|
|
| 485 |
<div class="meta-line" id="listSummary"></div>
|
| 486 |
</div>
|
| 487 |
<div class="filters" id="statusFilters"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 488 |
<form id="searchForm" class="search-form">
|
| 489 |
<input id="searchInput" type="search" placeholder="Search questions, answers, or versions" />
|
| 490 |
<button class="btn primary" type="submit">Search</button>
|
|
@@ -545,7 +574,7 @@
|
|
| 545 |
(() => {
|
| 546 |
const S = {
|
| 547 |
clientId: null,
|
| 548 |
-
filters: { status: "unanswered", q: "", page: 1, page_size: 20 },
|
| 549 |
list: null,
|
| 550 |
detail: null,
|
| 551 |
};
|
|
@@ -597,12 +626,18 @@
|
|
| 597 |
status: S.filters.status,
|
| 598 |
q: S.filters.q,
|
| 599 |
page: S.filters.page,
|
|
|
|
|
|
|
|
|
|
| 600 |
...extra,
|
| 601 |
};
|
| 602 |
if (merged.status) params.set("status", merged.status);
|
| 603 |
if (merged.q) params.set("q", merged.q);
|
| 604 |
if (merged.page && Number(merged.page) > 1) params.set("page", String(merged.page));
|
| 605 |
if (merged.conversation_id) params.set("conversation_id", merged.conversation_id);
|
|
|
|
|
|
|
|
|
|
| 606 |
return params.toString();
|
| 607 |
}
|
| 608 |
|
|
@@ -669,10 +704,33 @@
|
|
| 669 |
});
|
| 670 |
}
|
| 671 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
function renderList() {
|
| 673 |
const list = S.list || { items: [], total: 0, page: 1, page_size: 20, has_next: false, has_prev: false };
|
| 674 |
$("listSummary").textContent = `${list.total} question${list.total === 1 ? "" : "s"} in queue`;
|
| 675 |
$("searchInput").value = S.filters.q || "";
|
|
|
|
|
|
|
| 676 |
|
| 677 |
const items = Array.isArray(list.items) ? list.items : [];
|
| 678 |
$("queueList").innerHTML = items.map((item) => {
|
|
@@ -809,6 +867,9 @@
|
|
| 809 |
status: S.filters.status,
|
| 810 |
q: S.filters.q,
|
| 811 |
page: S.filters.page,
|
|
|
|
|
|
|
|
|
|
| 812 |
});
|
| 813 |
if (!res.ok) {
|
| 814 |
showToast(res.error || "Could not load question list");
|
|
@@ -816,6 +877,7 @@
|
|
| 816 |
}
|
| 817 |
S.list = res;
|
| 818 |
renderStatusFilters();
|
|
|
|
| 819 |
renderList();
|
| 820 |
showView("list");
|
| 821 |
pushListUrl();
|
|
@@ -853,6 +915,22 @@
|
|
| 853 |
await loadList();
|
| 854 |
};
|
| 855 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 856 |
$("prevPageBtn").onclick = async () => {
|
| 857 |
if (!S.list?.has_prev) return;
|
| 858 |
S.filters.page = Math.max(1, Number(S.filters.page || 1) - 1);
|
|
@@ -940,11 +1018,15 @@
|
|
| 940 |
S.filters = {
|
| 941 |
...S.filters,
|
| 942 |
...(init.filters || {}),
|
|
|
|
|
|
|
|
|
|
| 943 |
};
|
| 944 |
S.list = init.list || null;
|
| 945 |
S.detail = init.detail || null;
|
| 946 |
|
| 947 |
renderStatusFilters();
|
|
|
|
| 948 |
renderList();
|
| 949 |
bindListHandlers();
|
| 950 |
|
|
@@ -961,6 +1043,9 @@
|
|
| 961 |
S.filters.status = params.get("status") || "unanswered";
|
| 962 |
S.filters.q = params.get("q") || "";
|
| 963 |
S.filters.page = Number(params.get("page") || 1);
|
|
|
|
|
|
|
|
|
|
| 964 |
const conversationId = params.get("conversation_id");
|
| 965 |
await loadList();
|
| 966 |
if (conversationId) {
|
|
|
|
| 139 |
gap: 8px;
|
| 140 |
}
|
| 141 |
|
| 142 |
+
.word-filter-row {
|
| 143 |
+
display: flex;
|
| 144 |
+
flex-wrap: wrap;
|
| 145 |
+
gap: 8px;
|
| 146 |
+
align-items: center;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.word-input {
|
| 150 |
+
border: 1px solid var(--line);
|
| 151 |
+
border-radius: 14px;
|
| 152 |
+
padding: 9px 12px;
|
| 153 |
+
background: #0e1528;
|
| 154 |
+
color: var(--text);
|
| 155 |
+
width: 80px;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
.word-input::placeholder { color: #7381a6; }
|
| 159 |
+
|
| 160 |
.pill {
|
| 161 |
border: 1px solid var(--line);
|
| 162 |
background: #18233d;
|
|
|
|
| 475 |
.btn, .pill { width: 100%; text-align: center; }
|
| 476 |
.pager-actions { width: 100%; }
|
| 477 |
.pager-actions .btn { flex: 1; }
|
| 478 |
+
.word-filter-row { flex-direction: column; align-items: stretch; }
|
| 479 |
+
.word-input { width: 100%; }
|
| 480 |
}
|
| 481 |
</style>
|
| 482 |
</head>
|
|
|
|
| 505 |
<div class="meta-line" id="listSummary"></div>
|
| 506 |
</div>
|
| 507 |
<div class="filters" id="statusFilters"></div>
|
| 508 |
+
<div class="filters" id="sortFilters"></div>
|
| 509 |
+
<div class="word-filter-row">
|
| 510 |
+
<span class="muted" style="align-self:center;white-space:nowrap">Question words:</span>
|
| 511 |
+
<input id="minWordsInput" type="number" class="word-input" placeholder="Min" min="0" />
|
| 512 |
+
<span class="muted" style="align-self:center">–</span>
|
| 513 |
+
<input id="maxWordsInput" type="number" class="word-input" placeholder="Max" min="0" />
|
| 514 |
+
<button class="btn" type="button" id="applyWordsBtn">Apply</button>
|
| 515 |
+
<button class="btn" type="button" id="clearWordsBtn">Clear</button>
|
| 516 |
+
</div>
|
| 517 |
<form id="searchForm" class="search-form">
|
| 518 |
<input id="searchInput" type="search" placeholder="Search questions, answers, or versions" />
|
| 519 |
<button class="btn primary" type="submit">Search</button>
|
|
|
|
| 574 |
(() => {
|
| 575 |
const S = {
|
| 576 |
clientId: null,
|
| 577 |
+
filters: { status: "unanswered", q: "", page: 1, page_size: 20, sort: "newest", min_words: 0, max_words: 0 },
|
| 578 |
list: null,
|
| 579 |
detail: null,
|
| 580 |
};
|
|
|
|
| 626 |
status: S.filters.status,
|
| 627 |
q: S.filters.q,
|
| 628 |
page: S.filters.page,
|
| 629 |
+
sort: S.filters.sort,
|
| 630 |
+
min_words: S.filters.min_words,
|
| 631 |
+
max_words: S.filters.max_words,
|
| 632 |
...extra,
|
| 633 |
};
|
| 634 |
if (merged.status) params.set("status", merged.status);
|
| 635 |
if (merged.q) params.set("q", merged.q);
|
| 636 |
if (merged.page && Number(merged.page) > 1) params.set("page", String(merged.page));
|
| 637 |
if (merged.conversation_id) params.set("conversation_id", merged.conversation_id);
|
| 638 |
+
if (merged.sort && merged.sort !== "newest") params.set("sort", merged.sort);
|
| 639 |
+
if (Number(merged.min_words) > 0) params.set("min_words", String(merged.min_words));
|
| 640 |
+
if (Number(merged.max_words) > 0) params.set("max_words", String(merged.max_words));
|
| 641 |
return params.toString();
|
| 642 |
}
|
| 643 |
|
|
|
|
| 704 |
});
|
| 705 |
}
|
| 706 |
|
| 707 |
+
function renderSortFilters() {
|
| 708 |
+
const sorts = [
|
| 709 |
+
["newest", "Newest"],
|
| 710 |
+
["oldest", "Oldest"],
|
| 711 |
+
["most_votes", "Most Votes"],
|
| 712 |
+
["most_answers", "Most Answers"],
|
| 713 |
+
["longest_answer", "Longest Answer"],
|
| 714 |
+
["shortest_answer", "Shortest Answer"],
|
| 715 |
+
];
|
| 716 |
+
$("sortFilters").innerHTML = sorts.map(([value, label]) => `
|
| 717 |
+
<button class="pill ${S.filters.sort === value ? "active" : ""}" type="button" data-sort="${value}">${label}</button>
|
| 718 |
+
`).join("");
|
| 719 |
+
document.querySelectorAll("[data-sort]").forEach((button) => {
|
| 720 |
+
button.onclick = () => {
|
| 721 |
+
S.filters.sort = button.getAttribute("data-sort");
|
| 722 |
+
S.filters.page = 1;
|
| 723 |
+
loadList();
|
| 724 |
+
};
|
| 725 |
+
});
|
| 726 |
+
}
|
| 727 |
+
|
| 728 |
function renderList() {
|
| 729 |
const list = S.list || { items: [], total: 0, page: 1, page_size: 20, has_next: false, has_prev: false };
|
| 730 |
$("listSummary").textContent = `${list.total} question${list.total === 1 ? "" : "s"} in queue`;
|
| 731 |
$("searchInput").value = S.filters.q || "";
|
| 732 |
+
$("minWordsInput").value = S.filters.min_words > 0 ? S.filters.min_words : "";
|
| 733 |
+
$("maxWordsInput").value = S.filters.max_words > 0 ? S.filters.max_words : "";
|
| 734 |
|
| 735 |
const items = Array.isArray(list.items) ? list.items : [];
|
| 736 |
$("queueList").innerHTML = items.map((item) => {
|
|
|
|
| 867 |
status: S.filters.status,
|
| 868 |
q: S.filters.q,
|
| 869 |
page: S.filters.page,
|
| 870 |
+
sort: S.filters.sort,
|
| 871 |
+
min_words: S.filters.min_words || 0,
|
| 872 |
+
max_words: S.filters.max_words || 0,
|
| 873 |
});
|
| 874 |
if (!res.ok) {
|
| 875 |
showToast(res.error || "Could not load question list");
|
|
|
|
| 877 |
}
|
| 878 |
S.list = res;
|
| 879 |
renderStatusFilters();
|
| 880 |
+
renderSortFilters();
|
| 881 |
renderList();
|
| 882 |
showView("list");
|
| 883 |
pushListUrl();
|
|
|
|
| 915 |
await loadList();
|
| 916 |
};
|
| 917 |
|
| 918 |
+
$("applyWordsBtn").onclick = async () => {
|
| 919 |
+
S.filters.min_words = Number($("minWordsInput").value) || 0;
|
| 920 |
+
S.filters.max_words = Number($("maxWordsInput").value) || 0;
|
| 921 |
+
S.filters.page = 1;
|
| 922 |
+
await loadList();
|
| 923 |
+
};
|
| 924 |
+
|
| 925 |
+
$("clearWordsBtn").onclick = async () => {
|
| 926 |
+
$("minWordsInput").value = "";
|
| 927 |
+
$("maxWordsInput").value = "";
|
| 928 |
+
S.filters.min_words = 0;
|
| 929 |
+
S.filters.max_words = 0;
|
| 930 |
+
S.filters.page = 1;
|
| 931 |
+
await loadList();
|
| 932 |
+
};
|
| 933 |
+
|
| 934 |
$("prevPageBtn").onclick = async () => {
|
| 935 |
if (!S.list?.has_prev) return;
|
| 936 |
S.filters.page = Math.max(1, Number(S.filters.page || 1) - 1);
|
|
|
|
| 1018 |
S.filters = {
|
| 1019 |
...S.filters,
|
| 1020 |
...(init.filters || {}),
|
| 1021 |
+
sort: (init.filters || {}).sort || "newest",
|
| 1022 |
+
min_words: Number((init.filters || {}).min_words || 0),
|
| 1023 |
+
max_words: Number((init.filters || {}).max_words || 0),
|
| 1024 |
};
|
| 1025 |
S.list = init.list || null;
|
| 1026 |
S.detail = init.detail || null;
|
| 1027 |
|
| 1028 |
renderStatusFilters();
|
| 1029 |
+
renderSortFilters();
|
| 1030 |
renderList();
|
| 1031 |
bindListHandlers();
|
| 1032 |
|
|
|
|
| 1043 |
S.filters.status = params.get("status") || "unanswered";
|
| 1044 |
S.filters.q = params.get("q") || "";
|
| 1045 |
S.filters.page = Number(params.get("page") || 1);
|
| 1046 |
+
S.filters.sort = params.get("sort") || "newest";
|
| 1047 |
+
S.filters.min_words = Number(params.get("min_words") || 0);
|
| 1048 |
+
S.filters.max_words = Number(params.get("max_words") || 0);
|
| 1049 |
const conversationId = params.get("conversation_id");
|
| 1050 |
await loadList();
|
| 1051 |
if (conversationId) {
|