+47
-25
@@ -4,9 +4,11 @@ import {
|
||||
appStore,
|
||||
CHROME_PROFILE_NAME,
|
||||
findKnownUrlForBase,
|
||||
ISSUE_LABEL_OPTIONS,
|
||||
type AgentProfile,
|
||||
type AddUrlMode,
|
||||
type AppView,
|
||||
type IssueLabelOption,
|
||||
type OpenIssue,
|
||||
type OpenIssuesStateFilter,
|
||||
type ScreenshotAttachment
|
||||
@@ -389,6 +391,24 @@ function renderAgentOptions(agents: AgentProfile[], selectedAgentId: string): st
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderLabelOptions(selectedLabels: IssueLabelOption[], target: "draft" | "edit"): string {
|
||||
return `
|
||||
<fieldset class="label-fieldset">
|
||||
<legend>Labels</legend>
|
||||
<div class="label-option-grid">
|
||||
${ISSUE_LABEL_OPTIONS.map(
|
||||
(label) => `
|
||||
<label class="checkbox-row label-option">
|
||||
<input type="checkbox" value="${escapeHtml(label)}" data-label-target="${target}" ${selectedLabels.includes(label) ? "checked" : ""} />
|
||||
<span>${escapeHtml(label)}</span>
|
||||
</label>
|
||||
`
|
||||
).join("")}
|
||||
</div>
|
||||
</fieldset>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderBodyText(body: string, emptyText: string): string {
|
||||
const trimmed = body.trim();
|
||||
if (!trimmed) {
|
||||
@@ -540,7 +560,6 @@ function renderIssueRows(issues: OpenIssue[], selectedIssueNumber: number | null
|
||||
<span class="issue-state">${escapeHtml(issue.state)}</span>
|
||||
<span class="issue-meta">${formatDate(issue.createdAt)}</span>
|
||||
</button>
|
||||
<button class="secondary-button compact-button" type="button" data-ready-issue="${issue.number}" data-issue-repo="${escapeHtml(issue.repoFullName)}" ${state.isUpdatingIssue ? "disabled" : ""}>Ready</button>
|
||||
<button class="danger-button compact-button" type="button" data-delete-issue="${issue.number}" data-issue-repo="${escapeHtml(issue.repoFullName)}" ${state.isUpdatingIssue ? "disabled" : ""}>Delete</button>
|
||||
</div>
|
||||
`
|
||||
@@ -606,7 +625,11 @@ function renderSelectedIssueDetail(): string {
|
||||
}
|
||||
|
||||
const draft = state.selectedIssueEditDraft;
|
||||
const hasChanges = draft.subject !== detail.issue.title || draft.content !== detail.issue.body;
|
||||
const detailLabels = ISSUE_LABEL_OPTIONS.filter((label) => detail.issue.labels.includes(label));
|
||||
const hasChanges =
|
||||
draft.subject !== detail.issue.title ||
|
||||
draft.content !== detail.issue.body ||
|
||||
draft.labels.join("|") !== detailLabels.join("|");
|
||||
|
||||
return `
|
||||
<article class="item-card issue-detail-card">
|
||||
@@ -629,6 +652,7 @@ function renderSelectedIssueDetail(): string {
|
||||
<span>Content</span>
|
||||
<textarea id="selected-issue-content" rows="8">${escapeHtml(draft.content)}</textarea>
|
||||
</label>
|
||||
${renderLabelOptions(draft.labels, "edit")}
|
||||
<button id="save-selected-issue" class="primary-button" type="button" ${hasChanges && draft.subject.trim() && !state.isUpdatingIssue ? "" : "disabled"}>
|
||||
${state.isUpdatingIssue ? "Saving issue..." : "Save issue"}
|
||||
</button>
|
||||
@@ -741,6 +765,8 @@ function renderAddIssue(): string {
|
||||
</select>
|
||||
</label>
|
||||
|
||||
${renderLabelOptions(draft.labels, "draft")}
|
||||
|
||||
<section class="attachment-section">
|
||||
<div class="attachment-heading">
|
||||
<span>Screenshots</span>
|
||||
@@ -757,11 +783,6 @@ function renderAddIssue(): string {
|
||||
</label>
|
||||
${renderFileList(draft.files, "issue")}
|
||||
|
||||
<label class="checkbox-row">
|
||||
<input id="issue-ready" type="checkbox" ${draft.ready ? "checked" : ""} />
|
||||
<span>Ready</span>
|
||||
</label>
|
||||
|
||||
<button id="create-issue" class="primary-button" type="button" ${draft.subject.trim() && !state.isCreatingIssue ? "" : "disabled"}>
|
||||
${state.isCreatingIssue ? "Creating issue..." : "Create issue"}
|
||||
</button>
|
||||
@@ -893,12 +914,6 @@ function bindViewActions(): void {
|
||||
});
|
||||
});
|
||||
|
||||
viewContent?.querySelector<HTMLInputElement>("#issue-ready")?.addEventListener("change", (event) => {
|
||||
appStore.getState().setIssueDraft({
|
||||
ready: (event.target as HTMLInputElement).checked
|
||||
});
|
||||
});
|
||||
|
||||
viewContent?.querySelector<HTMLInputElement>("#issue-files")?.addEventListener("change", (event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
appStore.getState().setIssueFiles(Array.from(input.files ?? []));
|
||||
@@ -947,16 +962,6 @@ function bindViewActions(): void {
|
||||
});
|
||||
});
|
||||
|
||||
viewContent?.querySelectorAll<HTMLButtonElement>("[data-ready-issue]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const issueNumber = Number(button.dataset.readyIssue);
|
||||
const repoFullName = button.dataset.issueRepo;
|
||||
if (Number.isFinite(issueNumber)) {
|
||||
void appStore.getState().markIssueReady(issueNumber, repoFullName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
viewContent?.querySelectorAll<HTMLButtonElement>("[data-delete-issue]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const issueNumber = Number(button.dataset.deleteIssue);
|
||||
@@ -991,6 +996,21 @@ function bindViewActions(): void {
|
||||
void appStore.getState().saveSelectedIssueEdits();
|
||||
});
|
||||
|
||||
viewContent?.querySelectorAll<HTMLInputElement>("[data-label-target]").forEach((input) => {
|
||||
input.addEventListener("change", () => {
|
||||
const target = input.dataset.labelTarget;
|
||||
const labels = Array.from(
|
||||
viewContent.querySelectorAll<HTMLInputElement>(`input[data-label-target='${target}']:checked`)
|
||||
).map((checkedInput) => checkedInput.value as IssueLabelOption);
|
||||
|
||||
if (target === "draft") {
|
||||
appStore.getState().setIssueDraft({ labels });
|
||||
} else if (target === "edit") {
|
||||
appStore.getState().setSelectedIssueEditDraft({ labels });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
viewContent?.querySelectorAll<HTMLButtonElement>("[data-delete-issue-attachment]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const attachmentId = Number(button.dataset.deleteIssueAttachment);
|
||||
@@ -1221,7 +1241,7 @@ function shouldRenderView(): boolean {
|
||||
}
|
||||
|
||||
return !activeElement.matches(
|
||||
"#issue-subject, #issue-content, #issue-agent, #issue-ready, #selected-issue-subject, #selected-issue-content, #issue-comment-content, #agent-name, #agent-file, #agent-description, #agent-content"
|
||||
"#issue-subject, #issue-content, #issue-agent, #selected-issue-subject, #selected-issue-content, #issue-comment-content, #agent-name, #agent-file, #agent-description, #agent-content, [data-label-target]"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1244,11 +1264,13 @@ function syncFocusedFormControls(): void {
|
||||
}
|
||||
|
||||
if (saveSelectedIssue && state.selectedIssueDetail) {
|
||||
const detailLabels = ISSUE_LABEL_OPTIONS.filter((label) => state.selectedIssueDetail?.issue.labels.includes(label));
|
||||
saveSelectedIssue.disabled =
|
||||
!state.selectedIssueEditDraft.subject.trim() ||
|
||||
state.isUpdatingIssue ||
|
||||
(state.selectedIssueEditDraft.subject === state.selectedIssueDetail.issue.title &&
|
||||
state.selectedIssueEditDraft.content === state.selectedIssueDetail.issue.body);
|
||||
state.selectedIssueEditDraft.content === state.selectedIssueDetail.issue.body &&
|
||||
state.selectedIssueEditDraft.labels.join("|") === detailLabels.join("|"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user