Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed638f52ce |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Stage 1: Build
|
# Stage 1: Build
|
||||||
FROM golang:1.22-alpine AS builder
|
FROM golang:1.22-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /gitea-mobile ./cmd/server
|
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /gitea-mobile ./cmd/server
|
||||||
|
|||||||
@@ -325,9 +325,8 @@ type PaginatedPulls struct {
|
|||||||
|
|
||||||
// ListAllIssues fetches issues across all repos in the given orgs,
|
// ListAllIssues fetches issues across all repos in the given orgs,
|
||||||
// using concurrent requests with a semaphore. Results are paginated.
|
// using concurrent requests with a semaphore. Results are paginated.
|
||||||
// The label parameter filters issues by label name (empty string means no filter).
|
|
||||||
// The repoFilter parameter narrows results to a single repo name (empty means all repos).
|
// The repoFilter parameter narrows results to a single repo name (empty means all repos).
|
||||||
func (c *Client) ListAllIssues(ctx context.Context, token string, orgs []string, state string, page int, label string, repoFilter string) (PaginatedIssues, error) {
|
func (c *Client) ListAllIssues(ctx context.Context, token string, orgs []string, state string, page int, repoFilter string) (PaginatedIssues, error) {
|
||||||
if state == "" {
|
if state == "" {
|
||||||
state = "open"
|
state = "open"
|
||||||
}
|
}
|
||||||
@@ -335,7 +334,7 @@ func (c *Client) ListAllIssues(ctx context.Context, token string, orgs []string,
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := fmt.Sprintf("issues-%s-%s-%s-%s", state, strings.Join(orgs, ","), label, repoFilter)
|
cacheKey := fmt.Sprintf("issues-%s-%s-%s", state, strings.Join(orgs, ","), repoFilter)
|
||||||
var allIssues []Issue
|
var allIssues []Issue
|
||||||
if cached, ok := c.getFromCache(cacheKey); ok {
|
if cached, ok := c.getFromCache(cacheKey); ok {
|
||||||
allIssues = cached.([]Issue)
|
allIssues = cached.([]Issue)
|
||||||
@@ -375,9 +374,6 @@ func (c *Client) ListAllIssues(ctx context.Context, token string, orgs []string,
|
|||||||
defer func() { <-sem }()
|
defer func() { <-sem }()
|
||||||
|
|
||||||
path := fmt.Sprintf("/repos/%s/issues?state=%s&type=issues&limit=50", r.FullName, state)
|
path := fmt.Sprintf("/repos/%s/issues?state=%s&type=issues&limit=50", r.FullName, state)
|
||||||
if label != "" {
|
|
||||||
path += "&labels=" + label
|
|
||||||
}
|
|
||||||
resp, err := c.doRequest(ctx, token, http.MethodGet, path, nil)
|
resp, err := c.doRequest(ctx, token, http.MethodGet, path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
@@ -440,9 +436,8 @@ func (c *Client) ListAllIssues(ctx context.Context, token string, orgs []string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListAllPullRequests fetches PRs across all repos in the given orgs.
|
// ListAllPullRequests fetches PRs across all repos in the given orgs.
|
||||||
// Results are paginated. The label parameter filters PRs by label name.
|
// Results are paginated. The repoFilter parameter narrows results to a single repo name.
|
||||||
// The repoFilter parameter narrows results to a single repo name.
|
func (c *Client) ListAllPullRequests(ctx context.Context, token string, orgs []string, state string, page int, repoFilter string) (PaginatedPulls, error) {
|
||||||
func (c *Client) ListAllPullRequests(ctx context.Context, token string, orgs []string, state string, page int, label string, repoFilter string) (PaginatedPulls, error) {
|
|
||||||
if state == "" {
|
if state == "" {
|
||||||
state = "open"
|
state = "open"
|
||||||
}
|
}
|
||||||
@@ -450,7 +445,7 @@ func (c *Client) ListAllPullRequests(ctx context.Context, token string, orgs []s
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := fmt.Sprintf("pulls-%s-%s-%s-%s", state, strings.Join(orgs, ","), label, repoFilter)
|
cacheKey := fmt.Sprintf("pulls-%s-%s-%s", state, strings.Join(orgs, ","), repoFilter)
|
||||||
var allPRs []PullRequest
|
var allPRs []PullRequest
|
||||||
if cached, ok := c.getFromCache(cacheKey); ok {
|
if cached, ok := c.getFromCache(cacheKey); ok {
|
||||||
allPRs = cached.([]PullRequest)
|
allPRs = cached.([]PullRequest)
|
||||||
@@ -488,9 +483,6 @@ func (c *Client) ListAllPullRequests(ctx context.Context, token string, orgs []s
|
|||||||
defer func() { <-sem }()
|
defer func() { <-sem }()
|
||||||
|
|
||||||
path := fmt.Sprintf("/repos/%s/pulls?state=%s&limit=50", r.FullName, state)
|
path := fmt.Sprintf("/repos/%s/pulls?state=%s&limit=50", r.FullName, state)
|
||||||
if label != "" {
|
|
||||||
path += "&labels=" + label
|
|
||||||
}
|
|
||||||
resp, err := c.doRequest(ctx, token, http.MethodGet, path, nil)
|
resp, err := c.doRequest(ctx, token, http.MethodGet, path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
@@ -555,7 +547,7 @@ func (c *Client) GetTriageQueue(ctx context.Context, token string, orgs []string
|
|||||||
// Collect all open issues across all pages.
|
// Collect all open issues across all pages.
|
||||||
var issues []Issue
|
var issues []Issue
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
result, err := c.ListAllIssues(ctx, token, orgs, "open", page, "", "")
|
result, err := c.ListAllIssues(ctx, token, orgs, "open", page, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fetching issues for triage: %w", err)
|
return nil, fmt.Errorf("fetching issues for triage: %w", err)
|
||||||
}
|
}
|
||||||
@@ -568,7 +560,7 @@ func (c *Client) GetTriageQueue(ctx context.Context, token string, orgs []string
|
|||||||
// Collect all open PRs across all pages.
|
// Collect all open PRs across all pages.
|
||||||
var prs []PullRequest
|
var prs []PullRequest
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
result, err := c.ListAllPullRequests(ctx, token, orgs, "open", page, "", "")
|
result, err := c.ListAllPullRequests(ctx, token, orgs, "open", page, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fetching PRs for triage: %w", err)
|
return nil, fmt.Errorf("fetching PRs for triage: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ func (h *Handler) ListIssues(w http.ResponseWriter, r *http.Request) {
|
|||||||
Orgs []string
|
Orgs []string
|
||||||
SelectedOrg string
|
SelectedOrg string
|
||||||
SelectedState string
|
SelectedState string
|
||||||
SelectedLabel string
|
|
||||||
SelectedRepo string
|
SelectedRepo string
|
||||||
Repos []string
|
Repos []string
|
||||||
HasMore bool
|
HasMore bool
|
||||||
@@ -262,7 +261,6 @@ func (h *Handler) ListIssues(w http.ResponseWriter, r *http.Request) {
|
|||||||
if selectedState == "" {
|
if selectedState == "" {
|
||||||
selectedState = "open"
|
selectedState = "open"
|
||||||
}
|
}
|
||||||
selectedLabel := r.URL.Query().Get("label")
|
|
||||||
selectedRepo := r.URL.Query().Get("repo")
|
selectedRepo := r.URL.Query().Get("repo")
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
@@ -273,7 +271,6 @@ func (h *Handler) ListIssues(w http.ResponseWriter, r *http.Request) {
|
|||||||
Orgs: orgNames,
|
Orgs: orgNames,
|
||||||
SelectedOrg: selectedOrg,
|
SelectedOrg: selectedOrg,
|
||||||
SelectedState: selectedState,
|
SelectedState: selectedState,
|
||||||
SelectedLabel: selectedLabel,
|
|
||||||
SelectedRepo: selectedRepo,
|
SelectedRepo: selectedRepo,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +293,7 @@ func (h *Handler) ListIssues(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.Client.ListAllIssues(r.Context(), token, queryOrgs, selectedState, page, selectedLabel, selectedRepo)
|
result, err := h.Client.ListAllIssues(r.Context(), token, queryOrgs, selectedState, page, selectedRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to list issues", "error", err)
|
slog.Error("failed to list issues", "error", err)
|
||||||
data.Error = "Error loading issues."
|
data.Error = "Error loading issues."
|
||||||
@@ -355,7 +352,6 @@ func (h *Handler) ListPulls(w http.ResponseWriter, r *http.Request) {
|
|||||||
Orgs []string
|
Orgs []string
|
||||||
SelectedOrg string
|
SelectedOrg string
|
||||||
SelectedState string
|
SelectedState string
|
||||||
SelectedLabel string
|
|
||||||
SelectedRepo string
|
SelectedRepo string
|
||||||
Repos []string
|
Repos []string
|
||||||
HasMore bool
|
HasMore bool
|
||||||
@@ -368,7 +364,6 @@ func (h *Handler) ListPulls(w http.ResponseWriter, r *http.Request) {
|
|||||||
if selectedState == "" {
|
if selectedState == "" {
|
||||||
selectedState = "open"
|
selectedState = "open"
|
||||||
}
|
}
|
||||||
selectedLabel := r.URL.Query().Get("label")
|
|
||||||
selectedRepo := r.URL.Query().Get("repo")
|
selectedRepo := r.URL.Query().Get("repo")
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
@@ -379,7 +374,6 @@ func (h *Handler) ListPulls(w http.ResponseWriter, r *http.Request) {
|
|||||||
Orgs: orgNames,
|
Orgs: orgNames,
|
||||||
SelectedOrg: selectedOrg,
|
SelectedOrg: selectedOrg,
|
||||||
SelectedState: selectedState,
|
SelectedState: selectedState,
|
||||||
SelectedLabel: selectedLabel,
|
|
||||||
SelectedRepo: selectedRepo,
|
SelectedRepo: selectedRepo,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +395,7 @@ func (h *Handler) ListPulls(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.Client.ListAllPullRequests(r.Context(), token, queryOrgs, selectedState, page, selectedLabel, selectedRepo)
|
result, err := h.Client.ListAllPullRequests(r.Context(), token, queryOrgs, selectedState, page, selectedRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to list pull requests", "error", err)
|
slog.Error("failed to list pull requests", "error", err)
|
||||||
data.Error = "Error loading pull requests."
|
data.Error = "Error loading pull requests."
|
||||||
@@ -566,13 +560,6 @@ func (h *Handler) PullDetail(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch comments for this PR (Gitea uses the issues endpoint for PR comments).
|
|
||||||
comments, err := h.Client.GetIssueComments(r.Context(), token, owner, repo, index)
|
|
||||||
if err != nil {
|
|
||||||
slog.Warn("failed to fetch PR comments", "error", err, "owner", owner, "repo", repo, "index", index)
|
|
||||||
// Non-fatal: continue rendering without comments.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the content HTML using the template.
|
// Build the content HTML using the template.
|
||||||
tmpl, err := template.ParseFiles("internal/templates/pull_detail.html")
|
tmpl, err := template.ParseFiles("internal/templates/pull_detail.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -584,13 +571,11 @@ func (h *Handler) PullDetail(w http.ResponseWriter, r *http.Request) {
|
|||||||
type templateData struct {
|
type templateData struct {
|
||||||
Pull *giteaclient.PullRequest
|
Pull *giteaclient.PullRequest
|
||||||
RenderedBody template.HTML
|
RenderedBody template.HTML
|
||||||
Comments []giteaclient.Comment
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data := templateData{
|
data := templateData{
|
||||||
Pull: pr,
|
Pull: pr,
|
||||||
RenderedBody: renderedBody,
|
RenderedBody: renderedBody,
|
||||||
Comments: comments,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
|
|||||||
@@ -6,15 +6,16 @@
|
|||||||
<form id="create-issue-form" hx-post="/issues" hx-swap="innerHTML" hx-target="#main-content">
|
<form id="create-issue-form" hx-post="/issues" hx-swap="innerHTML" hx-target="#main-content">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="repo-select">Repository</label>
|
<label for="repo-select">Repository</label>
|
||||||
<input list="repo-options" id="repo-select" name="owner_repo"
|
<select id="repo-select" name="owner_repo" required>
|
||||||
placeholder="Type to search repositories..." required autocomplete="off">
|
<option value="">Select a repository...</option>
|
||||||
<datalist id="repo-options">
|
|
||||||
{{range $org, $repos := .Repos}}
|
{{range $org, $repos := .Repos}}
|
||||||
|
<optgroup label="{{$org}}">
|
||||||
{{range $repos}}
|
{{range $repos}}
|
||||||
<option value="{{.Owner.Login}}/{{.Name}}">{{.FullName}}</option>
|
<option value="{{.Owner.Login}}/{{.Name}}">{{.FullName}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
</optgroup>
|
||||||
{{end}}
|
{{end}}
|
||||||
</datalist>
|
</select>
|
||||||
<input type="hidden" name="owner" id="owner-input">
|
<input type="hidden" name="owner" id="owner-input">
|
||||||
<input type="hidden" name="repo" id="repo-input">
|
<input type="hidden" name="repo" id="repo-input">
|
||||||
</div>
|
</div>
|
||||||
@@ -44,16 +45,9 @@
|
|||||||
var repoInput = document.getElementById('repo-input');
|
var repoInput = document.getElementById('repo-input');
|
||||||
var formError = document.getElementById('form-error');
|
var formError = document.getElementById('form-error');
|
||||||
|
|
||||||
// Build a set of valid repo values for validation.
|
|
||||||
var validRepos = {};
|
|
||||||
var options = document.getElementById('repo-options').options;
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
|
||||||
validRepos[options[i].value] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function splitOwnerRepo() {
|
function splitOwnerRepo() {
|
||||||
var val = repoSelect.value;
|
var val = repoSelect.value;
|
||||||
if (val && val.indexOf('/') !== -1) {
|
if (val) {
|
||||||
var parts = val.split('/');
|
var parts = val.split('/');
|
||||||
ownerInput.value = parts[0] || '';
|
ownerInput.value = parts[0] || '';
|
||||||
repoInput.value = parts[1] || '';
|
repoInput.value = parts[1] || '';
|
||||||
@@ -63,31 +57,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var debounceTimer = null;
|
|
||||||
repoSelect.addEventListener('input', function() {
|
|
||||||
clearTimeout(debounceTimer);
|
|
||||||
debounceTimer = setTimeout(function() {
|
|
||||||
splitOwnerRepo();
|
|
||||||
var labelSection = document.getElementById('label-section');
|
|
||||||
var labelList = document.getElementById('label-list');
|
|
||||||
if (ownerInput.value && repoInput.value && validRepos[repoSelect.value]) {
|
|
||||||
labelList.innerHTML = '<span class="empty">Loading labels...</span>';
|
|
||||||
labelSection.style.display = 'block';
|
|
||||||
htmx.ajax('GET', '/issues/new/labels?owner=' + encodeURIComponent(ownerInput.value) + '&repo=' + encodeURIComponent(repoInput.value), {target: '#label-list', swap: 'innerHTML'});
|
|
||||||
} else {
|
|
||||||
labelSection.style.display = 'none';
|
|
||||||
labelList.innerHTML = '';
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also handle the change event for when a datalist option is selected directly.
|
|
||||||
repoSelect.addEventListener('change', function() {
|
repoSelect.addEventListener('change', function() {
|
||||||
clearTimeout(debounceTimer);
|
|
||||||
splitOwnerRepo();
|
splitOwnerRepo();
|
||||||
var labelSection = document.getElementById('label-section');
|
var labelSection = document.getElementById('label-section');
|
||||||
var labelList = document.getElementById('label-list');
|
var labelList = document.getElementById('label-list');
|
||||||
if (ownerInput.value && repoInput.value && validRepos[repoSelect.value]) {
|
if (ownerInput.value && repoInput.value) {
|
||||||
labelList.innerHTML = '<span class="empty">Loading labels...</span>';
|
labelList.innerHTML = '<span class="empty">Loading labels...</span>';
|
||||||
labelSection.style.display = 'block';
|
labelSection.style.display = 'block';
|
||||||
htmx.ajax('GET', '/issues/new/labels?owner=' + encodeURIComponent(ownerInput.value) + '&repo=' + encodeURIComponent(repoInput.value), {target: '#label-list', swap: 'innerHTML'});
|
htmx.ajax('GET', '/issues/new/labels?owner=' + encodeURIComponent(ownerInput.value) + '&repo=' + encodeURIComponent(repoInput.value), {target: '#label-list', swap: 'innerHTML'});
|
||||||
@@ -106,12 +80,6 @@
|
|||||||
formError.style.display = 'block';
|
formError.style.display = 'block';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!validRepos[repoSelect.value]) {
|
|
||||||
evt.preventDefault();
|
|
||||||
formError.textContent = 'Please select a valid repository from the list.';
|
|
||||||
formError.style.display = 'block';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
formError.style.display = 'none';
|
formError.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .HasMore}}
|
{{if .HasMore}}
|
||||||
<div class="scroll-sentinel" hx-get="/issues?page={{.NextPage}}&org={{.SelectedOrg}}&state={{.SelectedState}}&label={{.SelectedLabel}}&repo={{.SelectedRepo}}" hx-trigger="revealed" hx-swap="outerHTML" hx-target="this">
|
<div class="scroll-sentinel" hx-get="/issues?page={{.NextPage}}&org={{.SelectedOrg}}&state={{.SelectedState}}&repo={{.SelectedRepo}}" hx-trigger="revealed" hx-swap="outerHTML" hx-target="this">
|
||||||
<div class="spinner htmx-indicator"></div>
|
<div class="spinner htmx-indicator"></div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -24,25 +24,22 @@
|
|||||||
<h1>Issues</h1>
|
<h1>Issues</h1>
|
||||||
|
|
||||||
<div class="filter-bar">
|
<div class="filter-bar">
|
||||||
<select name="org" hx-get="/issues" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='state'],[name='label']">
|
<select name="org" hx-get="/issues" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='state']">
|
||||||
<option value="">All orgs</option>
|
<option value="">All orgs</option>
|
||||||
{{range .Orgs}}
|
{{range .Orgs}}
|
||||||
<option value="{{.}}" {{if eq . $.SelectedOrg}}selected{{end}}>{{.}}</option>
|
<option value="{{.}}" {{if eq . $.SelectedOrg}}selected{{end}}>{{.}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
{{if .Repos}}
|
{{if .Repos}}
|
||||||
<select name="repo" hx-get="/issues" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='state'],[name='label']">
|
<select name="repo" hx-get="/issues" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='state']">
|
||||||
<option value="">All repos</option>
|
<option value="">All repos</option>
|
||||||
{{range .Repos}}<option value="{{.}}" {{if eq . $.SelectedRepo}}selected{{end}}>{{.}}</option>{{end}}
|
{{range .Repos}}<option value="{{.}}" {{if eq . $.SelectedRepo}}selected{{end}}>{{.}}</option>{{end}}
|
||||||
</select>
|
</select>
|
||||||
{{end}}
|
{{end}}
|
||||||
<select name="state" hx-get="/issues" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='repo'],[name='label']">
|
<select name="state" hx-get="/issues" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='repo']">
|
||||||
<option value="open" {{if eq .SelectedState "open"}}selected{{end}}>Open</option>
|
<option value="open" {{if eq .SelectedState "open"}}selected{{end}}>Open</option>
|
||||||
<option value="closed" {{if eq .SelectedState "closed"}}selected{{end}}>Closed</option>
|
<option value="closed" {{if eq .SelectedState "closed"}}selected{{end}}>Closed</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" name="label" placeholder="Filter by label..." value="{{.SelectedLabel}}"
|
|
||||||
hx-get="/issues" hx-trigger="input changed delay:400ms" hx-target="#main-content"
|
|
||||||
hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='state'],[name='repo']">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .Error}}
|
{{if .Error}}
|
||||||
|
|||||||
@@ -46,21 +46,4 @@
|
|||||||
<button type="submit" class="btn btn-primary">Submit Review</button>
|
<button type="submit" class="btn btn-primary">Submit Review</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .Comments}}
|
|
||||||
<h2>Comments</h2>
|
|
||||||
<div id="comments-list">
|
|
||||||
{{range .Comments}}
|
|
||||||
<div class="comment">
|
|
||||||
<div class="comment-header">
|
|
||||||
<strong>{{.User}}</strong>
|
|
||||||
<span>{{.CreatedAt}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="comment-body">{{.Body}}</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<p class="empty" style="margin-top:1rem;">No comments yet.</p>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .HasMore}}
|
{{if .HasMore}}
|
||||||
<div class="scroll-sentinel" hx-get="/pulls?page={{.NextPage}}&org={{.SelectedOrg}}&state={{.SelectedState}}&label={{.SelectedLabel}}&repo={{.SelectedRepo}}" hx-trigger="revealed" hx-swap="outerHTML" hx-target="this">
|
<div class="scroll-sentinel" hx-get="/pulls?page={{.NextPage}}&org={{.SelectedOrg}}&state={{.SelectedState}}&repo={{.SelectedRepo}}" hx-trigger="revealed" hx-swap="outerHTML" hx-target="this">
|
||||||
<div class="spinner htmx-indicator"></div>
|
<div class="spinner htmx-indicator"></div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -27,25 +27,22 @@
|
|||||||
<h1>Pull Requests</h1>
|
<h1>Pull Requests</h1>
|
||||||
|
|
||||||
<div class="filter-bar">
|
<div class="filter-bar">
|
||||||
<select name="org" hx-get="/pulls" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='state'],[name='label']">
|
<select name="org" hx-get="/pulls" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='state']">
|
||||||
<option value="">All orgs</option>
|
<option value="">All orgs</option>
|
||||||
{{range .Orgs}}
|
{{range .Orgs}}
|
||||||
<option value="{{.}}" {{if eq . $.SelectedOrg}}selected{{end}}>{{.}}</option>
|
<option value="{{.}}" {{if eq . $.SelectedOrg}}selected{{end}}>{{.}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
{{if .Repos}}
|
{{if .Repos}}
|
||||||
<select name="repo" hx-get="/pulls" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='state'],[name='label']">
|
<select name="repo" hx-get="/pulls" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='state']">
|
||||||
<option value="">All repos</option>
|
<option value="">All repos</option>
|
||||||
{{range .Repos}}<option value="{{.}}" {{if eq . $.SelectedRepo}}selected{{end}}>{{.}}</option>{{end}}
|
{{range .Repos}}<option value="{{.}}" {{if eq . $.SelectedRepo}}selected{{end}}>{{.}}</option>{{end}}
|
||||||
</select>
|
</select>
|
||||||
{{end}}
|
{{end}}
|
||||||
<select name="state" hx-get="/pulls" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='repo'],[name='label']">
|
<select name="state" hx-get="/pulls" hx-trigger="change" hx-target="#main-content" hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='repo']">
|
||||||
<option value="open" {{if eq .SelectedState "open"}}selected{{end}}>Open</option>
|
<option value="open" {{if eq .SelectedState "open"}}selected{{end}}>Open</option>
|
||||||
<option value="closed" {{if eq .SelectedState "closed"}}selected{{end}}>Closed</option>
|
<option value="closed" {{if eq .SelectedState "closed"}}selected{{end}}>Closed</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" name="label" placeholder="Filter by label..." value="{{.SelectedLabel}}"
|
|
||||||
hx-get="/pulls" hx-trigger="input changed delay:400ms" hx-target="#main-content"
|
|
||||||
hx-swap="innerHTML" hx-push-url="true" hx-include="[name='org'],[name='state'],[name='repo']">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .Error}}
|
{{if .Error}}
|
||||||
|
|||||||
Reference in New Issue
Block a user