mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-10-19 14:17:20 +02:00
fix: make dashboard UI consistent and compact on mobile
This commit is contained in:
1 parent
adc9dbd439
commit
282efdc16e
52 files changed
+2177
-712
No files matched your search
@@ -3,25 +3,25 @@
|
||||
<h1 class="paper-page-title">Conferences</h1>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences" class="active"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<div class="admin-summary">
|
||||
<span class="summary-total">{{ total >= 0 ? fmt.number(total) : '…' }}</span>
|
||||
<span class="summary-pill ok" title="Toggle ready filter" :class="{active: query?.ready}" @click="query.ready = !query.ready; query.page = 1">Ready <span class="count">{{ fmt?.number(statusCountFor('ready')) }}</span></span>
|
||||
<span class="summary-pill warn" title="Toggle preparing filter" :class="{active: query?.preparing}" @click="query.preparing = !query.preparing; query.page = 1">Preparing <span class="count">{{ fmt?.number(statusCountFor('preparing')) }}</span></span>
|
||||
<span class="summary-pill error" title="Toggle errored filter" :class="{active: query?.error}" @click="query.error = !query.error; query.page = 1">Errored <span class="count">{{ fmt?.number(statusCountFor('error')) }}</span></span>
|
||||
<span class="summary-pill" title="Toggle expired filter" :class="{active: query?.expired}" @click="query.expired = !query.expired; query.page = 1">Expired <span class="count">{{ fmt?.number(statusCountFor('expired')) }}</span></span>
|
||||
<span class="summary-pill" title="Toggle removed filter" :class="{active: query?.removed}" @click="query.removed = !query.removed; query.page = 1">Removed <span class="count">{{ fmt?.number(statusCountFor('removed')) }}</span></span>
|
||||
<button type="button" class="summary-pill ok" title="Toggle ready filter" :class="{active: query?.ready}" @click="query.ready = !query.ready; query.page = 1" :aria-pressed="!!(query?.ready)">Ready <span class="count">{{ fmt?.number(statusCountFor('ready')) }}</span></button>
|
||||
<button type="button" class="summary-pill warn" title="Toggle preparing filter" :class="{active: query?.preparing}" @click="query.preparing = !query.preparing; query.page = 1" :aria-pressed="!!(query?.preparing)">Preparing <span class="count">{{ fmt?.number(statusCountFor('preparing')) }}</span></button>
|
||||
<button type="button" class="summary-pill error" title="Toggle errored filter" :class="{active: query?.error}" @click="query.error = !query.error; query.page = 1" :aria-pressed="!!(query?.error)">Errored <span class="count">{{ fmt?.number(statusCountFor('error')) }}</span></button>
|
||||
<button type="button" class="summary-pill" title="Toggle expired filter" :class="{active: query?.expired}" @click="query.expired = !query.expired; query.page = 1" :aria-pressed="!!(query?.expired)">Expired <span class="count">{{ fmt?.number(statusCountFor('expired')) }}</span></button>
|
||||
<button type="button" class="summary-pill" title="Toggle removed filter" :class="{active: query?.removed}" @click="query.removed = !query.removed; query.page = 1" :aria-pressed="!!(query?.removed)">Removed <span class="count">{{ fmt?.number(statusCountFor('removed')) }}</span></button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger" style="margin: 8px 0;" v-if="fetchError">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ fetchError }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg> {{ fetchError }}
|
||||
</div>
|
||||
|
||||
<form class="w-100 admin-filter-toolbar" aria-label="Conferences" accept-charset="UTF-8" v-form="viewState">
|
||||
@@ -31,11 +31,11 @@
|
||||
<span class="admin-search-hint" v-if="!query?.search">/</span>
|
||||
</div>
|
||||
<span class="admin-filter-spacer"></span>
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><i class="fas fa-file-csv"></i> Export</button>
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-export"></use></svg> Export</button>
|
||||
<span class="admin-filter-inline" aria-label="Pagination">
|
||||
<button class="btn btn-sm" type="button" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><i class="fas fa-chevron-left"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1" aria-label="Previous page"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-left"></use></svg></button>
|
||||
<span style="font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted);">{{ query?.page }}/{{ totalPage || 1 }}</span>
|
||||
<button class="btn btn-sm" type="button" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage"><i class="fas fa-chevron-right"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage" aria-label="Next page"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<span class="admin-active-chip" v-for="(chip, index) in chips" :key="chip?.key">
|
||||
<span class="key">{{ chip?.label }}</span>
|
||||
<span>{{ chip?.value }}</span>
|
||||
<button type="button" @click="clearFilter(chip.key)"><i class="fas fa-times"></i></button>
|
||||
<button type="button" @click="clearFilter(chip.key)" aria-label="Clear filter"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-close"></use></svg></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,15 +52,15 @@
|
||||
|
||||
<div class="paper-table paper-table-conferences w-100" role="table" aria-label="Conferences">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'name'}" @click="sortBy('name')">Conference <i class="fas" :class="sortIcon('name')"></i></span></div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <i class="fas" :class="sortIcon('status')"></i></span></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'name'}" @click="sortBy('name')">Conference <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('name')" :class="{ascending: sortIcon('name') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('status')" :class="{ascending: sortIcon('status') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" class="num">Repos</div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'startDate'}" @click="sortBy('startDate')">Window <i class="fas" :class="sortIcon('startDate')"></i></span></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'startDate'}" @click="sortBy('startDate')">Window <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('startDate')" :class="{ascending: sortIcon('startDate') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div class="paper-table-row" role="row" v-for="(conference, index) in filteredConferences">
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Conf</span>
|
||||
<svg class="ui-icon record-type-icon" role="img" aria-label="Conference"><use href="#project-icon-conference"></use></svg>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" v-text="conference?.name" :href="safeUrl("/conference/" + (conference?.conferenceID))"></a>
|
||||
<div class="anon-sub">
|
||||
@@ -72,27 +72,27 @@
|
||||
<span class="status-dot" :class="{'status-removed': conference?.status == 'removed' || conference?.status == 'expired', 'status-ready': conference?.status == 'ready', 'status-error': conference?.status == 'error', 'status-preparing': conference?.status == 'preparing'}"></span>
|
||||
<span v-text="fmt?.title(conference?.status)"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell">
|
||||
<div class="cell-views num" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg>
|
||||
<a title="Show repositories in this conference" v-text="fmt?.number(conference?.repositories?.length || 0)" :href="safeUrl("/admin/?conference=" + (conference?.conferenceID))"></a>
|
||||
</div>
|
||||
<div class="cell-expires" role="cell">{{ fmt?.date(conference?.startDate) }} – {{ fmt?.date(conference?.endDate) }}</div>
|
||||
<div class="cell-expires" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg> {{ fmt?.date(conference?.startDate) }} – {{ fmt?.date(conference?.endDate) }}</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/edit")"><i class="far fa-edit"></i> Edit</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/")"><i class="fa fa-eye"></i> View</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/admin/?conference=" + (conference?.conferenceID))"><i class="fas fa-code-branch"></i> View repositories</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/edit")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/admin/?conference=" + (conference?.conferenceID))"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> View repositories</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="conference?.status != 'removed'" @click.prevent="removeConference(conference)"><i class="fas fa-trash-alt"></i> Remove</a>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="conference?.status != 'removed'" @click.prevent="removeConference(conference)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="filteredConferences?.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span>No conferences match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,10 +100,10 @@
|
||||
<div class="admin-toolbar" style="justify-content: space-between; border-bottom: none;">
|
||||
<span style="font-size: 12px; color: var(--ink-muted);">{{ fmt?.number(total) }} results</span>
|
||||
<div class="pagination-compact" v-if="totalPage > 1">
|
||||
<button class="btn btn-sm" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><i class="fas fa-chevron-left"></i> Previous</button>
|
||||
<button class="btn btn-sm" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-left"></use></svg> Previous</button>
|
||||
<input type="number" class="form-control form-control-sm" min="1" style="width: 56px;" v-field="{ state: viewState, set: value => { query.page = value }, value: query?.page, form: null, options: {} }" :max="(totalPage)">
|
||||
<span>of {{ totalPage }}</span>
|
||||
<button class="btn btn-sm" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage">Next <i class="fas fa-chevron-right"></i></button>
|
||||
<button class="btn btn-sm" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage">Next <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg></button>
|
||||
</div>
|
||||
<span class="admin-filter-inline">
|
||||
<label>Per page</label>
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
<header class="errors-header">
|
||||
<h1 class="paper-page-title">Errors</h1>
|
||||
<div class="errors-actions">
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><i class="fas fa-file-export"></i> Export CSV</button>
|
||||
<button class="btn btn-sm btn-danger" type="button" @click="clearAll()"><i class="fas fa-trash"></i> Clear all</button>
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-export"></use></svg> Export CSV</button>
|
||||
<button class="btn btn-sm btn-danger" type="button" @click="clearAll()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Clear all</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors" class="active"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<section class="kpi-grid">
|
||||
@@ -78,7 +78,7 @@
|
||||
<button type="button" :class="{active: query?.bucket === 'info'}" @click="setBucket('info')">Info</button>
|
||||
</div>
|
||||
<div class="search-wrap">
|
||||
<i class="fas fa-search search-icon"></i>
|
||||
<svg class="ui-icon search-icon" aria-hidden="true"><use href="#ui-icon-search"></use></svg>
|
||||
<input type="search" class="form-control" placeholder="code:repo_not_found module:route status:>=400" autocomplete="off" v-field="{ state: viewState, set: value => { query.search = value }, value: query?.search, form: null, options: {} }">
|
||||
<span class="filter-count" v-if="parsedFilterCount">{{ parsedFilterCount }} filter{{ parsedFilterCount > 1 ? 's' : '' }}</span>
|
||||
</div>
|
||||
@@ -101,7 +101,7 @@
|
||||
<input type="checkbox" v-field="{ state: viewState, set: value => { query.autoRefresh = value }, value: query?.autoRefresh, form: null, options: {} }">
|
||||
Auto-refresh
|
||||
</label>
|
||||
<button class="btn btn-sm btn-icon" type="button" title="Refresh now" @click="refreshNow()"><i class="fas fa-sync"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" title="Refresh now" @click="refreshNow()" aria-label="Refresh"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg></button>
|
||||
</form>
|
||||
|
||||
<div class="admin-empty" v-if="!visible?.length">No errors captured.</div>
|
||||
@@ -164,8 +164,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-actions">
|
||||
<button class="btn btn-sm" type="button" title="Copy a curl that reproduces the request" @click="copyCurl(row)"><i class="fas fa-terminal"></i> Copy curl</button>
|
||||
<button class="btn btn-sm" type="button" @click="copyJson(row)"><i class="fas fa-clipboard"></i> Copy JSON</button>
|
||||
<button class="btn btn-sm" type="button" title="Copy a curl that reproduces the request" @click="copyCurl(row)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-code"></use></svg> Copy curl</button>
|
||||
<button class="btn btn-sm" type="button" @click="copyJson(row)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-clipboard"></use></svg> Copy JSON</button>
|
||||
<span class="copy-hint" v-if="copyHint">{{ copyHint }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
</div>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/" class="active"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<div class="admin-empty" v-if="loading">Loading overview…</div>
|
||||
<div class="alert alert-danger" style="margin:12px 0" v-if="error"><i class="fas fa-exclamation-triangle"></i> {{ error }}</div>
|
||||
<div class="alert alert-danger" style="margin:12px 0" v-if="error"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg> {{ error }}</div>
|
||||
|
||||
<div v-if="data">
|
||||
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
</div>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues" class="active"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<!-- Queue overview cards -->
|
||||
<div class="q-cards">
|
||||
<div class="q-card" v-for="(q, index) in queueList" :class="{selected: selectedQueue == q?.key, paused: q?.paused}" @click="selectQueue(q.key)">
|
||||
<div class="q-card" role="button" tabindex="0" :aria-pressed="selectedQueue === q.key" @keydown.enter.prevent="selectQueue(q.key)" @keydown.space.prevent="selectQueue(q.key)" v-for="(q, index) in queueList" :class="{selected: selectedQueue == q?.key, paused: q?.paused}" @click="selectQueue(q.key)">
|
||||
<div class="q-card-head">
|
||||
<span class="q-dot" :class="{'q-dot-red': q?.paused || q?.counts?.failed > 0}"></span>
|
||||
<span class="q-card-name" v-text="q?.label"></span>
|
||||
@@ -88,7 +88,7 @@
|
||||
</div>
|
||||
|
||||
<div class="q-toast-error" v-if="actionError" @click="actionError = null">
|
||||
<i class="fas fa-exclamation-circle"></i> {{ actionError }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> {{ actionError }}
|
||||
</div>
|
||||
|
||||
<!-- Jobs table -->
|
||||
@@ -109,7 +109,7 @@
|
||||
<input type="checkbox" v-field="{ state: viewState, set: value => { query.autoRefresh = value }, value: query?.autoRefresh, form: null, options: {} }">
|
||||
Auto-refresh
|
||||
</label>
|
||||
<button class="btn btn-sm" type="button" title="Refresh now" @click="refreshNow()"><i class="fas fa-sync"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" title="Refresh now" @click="refreshNow()" aria-label="Refresh"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg></button>
|
||||
</div>
|
||||
|
||||
<table class="q-table" v-if="filteredJobs().length > 0">
|
||||
@@ -131,7 +131,7 @@
|
||||
<span class="q-delay-hint" v-if="job?._state == 'delayed' && job?.delayUntil" v-text="delayCountdown(job?.delayUntil)"></span>
|
||||
</td>
|
||||
<td class="q-cell-id">
|
||||
<i class="fas fa-chevron-right q-chevron" :class="{'q-chevron-open': expanded[job?.id]}"></i>
|
||||
<svg class="ui-icon q-chevron" :class="{'q-chevron-open': expanded[job?.id]}" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg>
|
||||
<a target="_blank" v-text="'job:' + fmt.limitTo(job?.id, 6)" @click="$event.stopPropagation()" :href="safeUrl("/r/" + (job?.id))"></a>
|
||||
</td>
|
||||
<td class="q-cell-payload">
|
||||
@@ -147,8 +147,8 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="q-cell-actions">
|
||||
<button class="btn btn-sm" title="Retry" v-if="job?._state == 'failed'" @click="retryJob(job); $event.stopPropagation()"><i class="fas fa-sync"></i></button>
|
||||
<button class="btn btn-sm" title="Remove" @click="removeJob(job); $event.stopPropagation()"><i class="fas fa-trash-alt"></i></button>
|
||||
<button class="btn btn-sm btn-icon" title="Retry" v-if="job?._state == 'failed'" @click="retryJob(job); $event.stopPropagation()" aria-label="Retry job"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg></button>
|
||||
<button class="btn btn-sm btn-icon" title="Remove" aria-label="Remove job" @click="removeJob(job); $event.stopPropagation()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="q-detail-row" v-if="expanded[job?.id]">
|
||||
@@ -205,9 +205,9 @@
|
||||
<pre class="q-error-stack" v-for="(stack, index) in job?.stacktrace" :key="index"><code v-text="stack"></code></pre>
|
||||
</div>
|
||||
<div class="q-job-detail-actions">
|
||||
<button class="btn btn-sm" v-if="job?._state == 'failed'" @click="retryJob(job)"><i class="fas fa-sync"></i> Retry</button>
|
||||
<button class="btn btn-sm" @click="removeJob(job)"><i class="fas fa-trash-alt"></i> Remove</button>
|
||||
<a class="btn btn-sm" target="_blank" :href="safeUrl("/r/" + (job?.id))"><i class="fas fa-external-link-alt"></i> View repo</a>
|
||||
<button class="btn btn-sm" v-if="job?._state == 'failed'" @click="retryJob(job)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Retry</button>
|
||||
<button class="btn btn-sm" @click="removeJob(job)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Remove</button>
|
||||
<a class="btn btn-sm" target="_blank" :href="safeUrl("/r/" + (job?.id))"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-external-link"></use></svg> View repo</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -216,7 +216,7 @@
|
||||
</table>
|
||||
|
||||
<div class="paper-table-empty" style="border:1px solid var(--border-color);border-radius:10px;background:var(--paper-card);" v-if="filteredJobs().length == 0">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check-circle"></use></svg>
|
||||
<span v-if="!query?.search">No jobs in the {{ selectedQueue }} queue.</span>
|
||||
<span v-if="query?.search">No jobs match the current filters.</span>
|
||||
</div>
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
<h1 class="paper-page-title">Repositories</h1>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories" class="active"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<div class="admin-summary">
|
||||
<span class="summary-total">{{ total >= 0 ? fmt.number(total) : '…' }}</span>
|
||||
<span class="summary-meta">{{ fmt?.humanFileSize(totalSize) }} on disk</span>
|
||||
<span class="summary-pill ok" title="Toggle ready filter" :class="{active: query?.ready}" @click="query.ready = !query.ready; query.page = 1">Ready <span class="count">{{ fmt?.number(statusCountFor('ready')) }}</span></span>
|
||||
<span class="summary-pill warn" title="Toggle preparing filter" :class="{active: query?.preparing}" @click="query.preparing = !query.preparing; query.page = 1">Preparing <span class="count">{{ fmt?.number(statusCountFor('preparing') + statusCountFor('download')) }}</span></span>
|
||||
<span class="summary-pill error" title="Toggle errored filter" :class="{active: query?.error}" @click="query.error = !query.error; query.page = 1">Errored <span class="count">{{ fmt?.number(statusCountFor('error')) }}</span></span>
|
||||
<span class="summary-pill" title="Toggle expired filter" :class="{active: query?.expired}" @click="query.expired = !query.expired; query.page = 1">Expired <span class="count">{{ fmt?.number(statusCountFor('expired') + statusCountFor('expiring')) }}</span></span>
|
||||
<span class="summary-pill" title="Toggle removed filter" :class="{active: query?.removed}" @click="query.removed = !query.removed; query.page = 1">Removed <span class="count">{{ fmt?.number(statusCountFor('removed') + statusCountFor('removing')) }}</span></span>
|
||||
<button type="button" class="summary-pill ok" title="Toggle ready filter" :class="{active: query?.ready}" @click="query.ready = !query.ready; query.page = 1" :aria-pressed="!!(query?.ready)">Ready <span class="count">{{ fmt?.number(statusCountFor('ready')) }}</span></button>
|
||||
<button type="button" class="summary-pill warn" title="Toggle preparing filter" :class="{active: query?.preparing}" @click="query.preparing = !query.preparing; query.page = 1" :aria-pressed="!!(query?.preparing)">Preparing <span class="count">{{ fmt?.number(statusCountFor('preparing') + statusCountFor('download')) }}</span></button>
|
||||
<button type="button" class="summary-pill error" title="Toggle errored filter" :class="{active: query?.error}" @click="query.error = !query.error; query.page = 1" :aria-pressed="!!(query?.error)">Errored <span class="count">{{ fmt?.number(statusCountFor('error')) }}</span></button>
|
||||
<button type="button" class="summary-pill" title="Toggle expired filter" :class="{active: query?.expired}" @click="query.expired = !query.expired; query.page = 1" :aria-pressed="!!(query?.expired)">Expired <span class="count">{{ fmt?.number(statusCountFor('expired') + statusCountFor('expiring')) }}</span></button>
|
||||
<button type="button" class="summary-pill" title="Toggle removed filter" :class="{active: query?.removed}" @click="query.removed = !query.removed; query.page = 1" :aria-pressed="!!(query?.removed)">Removed <span class="count">{{ fmt?.number(statusCountFor('removed') + statusCountFor('removing')) }}</span></button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger" style="margin: 8px 0;" v-if="fetchError">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ fetchError }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg> {{ fetchError }}
|
||||
</div>
|
||||
|
||||
<form class="w-100 admin-filter-toolbar" aria-label="Repositories" accept-charset="UTF-8" v-form="viewState">
|
||||
@@ -35,11 +35,11 @@
|
||||
<span class="admin-filter-inline"><label>Owner</label><input type="text" class="form-control form-control-sm" placeholder="username" v-field="{ state: viewState, set: value => { query.owner = value }, value: query?.owner, form: null, options: {} }"></span>
|
||||
<span class="admin-filter-inline"><label>Conference</label><input type="text" class="form-control form-control-sm" placeholder="ID" v-field="{ state: viewState, set: value => { query.conference = value }, value: query?.conference, form: null, options: {} }"></span>
|
||||
<span class="admin-filter-spacer"></span>
|
||||
<button class="btn btn-sm" type="button" title="Export current view to CSV" @click="exportCsv()"><i class="fas fa-file-csv"></i> Export</button>
|
||||
<button class="btn btn-sm" type="button" title="Export current view to CSV" @click="exportCsv()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-export"></use></svg> Export</button>
|
||||
<span class="admin-filter-inline" aria-label="Pagination">
|
||||
<button class="btn btn-sm" type="button" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><i class="fas fa-chevron-left"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1" aria-label="Previous page"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-left"></use></svg></button>
|
||||
<span style="font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted);">{{ query?.page }}/{{ totalPage || 1 }}</span>
|
||||
<button class="btn btn-sm" type="button" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage"><i class="fas fa-chevron-right"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage" aria-label="Next page"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<span class="admin-active-chip" v-for="(chip, index) in chips" :key="chip?.key">
|
||||
<span class="key">{{ chip?.label }}</span>
|
||||
<span>{{ chip?.value }}</span>
|
||||
<button type="button" aria-label="Remove filter" @click="clearFilter(chip.key)"><i class="fas fa-times"></i></button>
|
||||
<button type="button" aria-label="Remove filter" @click="clearFilter(chip.key)"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-close"></use></svg></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,8 +57,8 @@
|
||||
|
||||
<div class="bulk-bar" v-if="selectedCount() > 0">
|
||||
<span><strong>{{ selectedCount() }}</strong> selected</span>
|
||||
<button class="btn btn-sm" type="button" @click="bulkRefresh()"><i class="fas fa-sync"></i> Force refresh</button>
|
||||
<button class="btn btn-sm text-danger" type="button" @click="bulkRemoveCache()"><i class="fas fa-broom"></i> Remove cache</button>
|
||||
<button class="btn btn-sm" type="button" @click="bulkRefresh()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Force refresh</button>
|
||||
<button class="btn btn-sm text-danger" type="button" @click="bulkRemoveCache()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-broom"></use></svg> Remove cache</button>
|
||||
<button class="btn btn-sm" type="button" @click="clearSelection()">Clear</button>
|
||||
</div>
|
||||
|
||||
@@ -67,10 +67,10 @@
|
||||
<div role="columnheader" style="width: 28px;">
|
||||
<input type="checkbox" aria-label="Select all on page" @click="selectAllOnPage()" :checked="allSelected">
|
||||
</div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'source.repositoryName'}" @click="sortBy('source.repositoryName')">Repository <i class="fas" :class="sortIcon('source.repositoryName')"></i></span></div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <i class="fas" :class="sortIcon('status')"></i></span></div>
|
||||
<div role="columnheader" class="num"><span class="sortable" :class="{active: query?.sort == 'pageView'}" @click="sortBy('pageView')">Views <i class="fas" :class="sortIcon('pageView')"></i></span></div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'anonymizeDate'}" @click="sortBy('anonymizeDate')">Anonymized <i class="fas" :class="sortIcon('anonymizeDate')"></i></span></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'source.repositoryName'}" @click="sortBy('source.repositoryName')">Repository <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('source.repositoryName')" :class="{ascending: sortIcon('source.repositoryName') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('status')" :class="{ascending: sortIcon('status') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" class="num"><button type="button" class="sortable" :class="{active: query?.sort == 'pageView'}" @click="sortBy('pageView')">Views <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('pageView')" :class="{ascending: sortIcon('pageView') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'anonymizeDate'}" @click="sortBy('anonymizeDate')">Anonymized <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('anonymizeDate')" :class="{ascending: sortIcon('anonymizeDate') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div class="paper-table-row" role="row" v-for="(repo, index) in filteredRepositories" :class="{'repo-inactive': repo?.status == 'expired' || repo?.status == 'removed', 'repo-error': repo?.status == 'error', 'row-selected': selected[repo?.repoId]}">
|
||||
@@ -78,11 +78,11 @@
|
||||
<input type="checkbox" aria-label="Select repository" v-field="{ state: viewState, set: value => { selected[repo.repoId] = value }, value: selected[repo?.repoId], form: null, options: {} }">
|
||||
</div>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<svg class="ui-icon record-type-icon" role="img" aria-label="Repository"><use href="#project-type-repo"></use></svg>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" target="_blank" v-text="repo?.repoId" :href="safeUrl("/r/" + (repo?.repoId))"></a>
|
||||
<div class="anon-sub">
|
||||
<a v-text="repo?.source?.repositoryName" :href="safeUrl("https://github.com/" + (repo?.source?.repositoryName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <i class="fas fa-chalkboard-teacher"></i> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} terms</span>
|
||||
<a v-text="repo?.source?.repositoryName" :href="safeUrl("https://github.com/" + (repo?.source?.repositoryName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} terms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,31 +93,31 @@
|
||||
</span>
|
||||
<span class="status-sub" v-if="repo?.statusMessage" v-text="fmt?.statusMsg(repo?.statusMessage)" :title="(repo?.statusMessage)"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" v-text="fmt?.number(repo?.pageView || 0)"></div>
|
||||
<div class="cell-expires" role="cell" v-text="fmt?.humanTime(repo?.anonymizeDate)"></div>
|
||||
<div class="cell-views num" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> <span v-text="fmt?.number(repo?.pageView || 0)"></span></div>
|
||||
<div class="cell-expires" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg> <span v-text="fmt?.humanTime(repo?.anonymizeDate)"></span></div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/anonymize/" + (repo?.repoId))"><i class="far fa-edit"></i> Edit</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/r/" + (repo?.repoId) + "/")"><i class="fa fa-eye"></i> View repo</a>
|
||||
<a class="dropdown-item" target="_self" v-if="repo?.options?.page && repo?.status == 'ready'" :href="safeUrl("/w/" + (repo?.repoId) + "/")"><i class="fas fa-globe"></i> View page</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/anonymize/" + (repo?.repoId))"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/r/" + (repo?.repoId) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View repo</a>
|
||||
<a class="dropdown-item" target="_self" v-if="repo?.options?.page && repo?.status == 'ready'" :href="safeUrl("/w/" + (repo?.repoId) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-globe"></use></svg> View page</a>
|
||||
<a class="dropdown-item" href="#" @click.prevent="fetchGithubInfo(repo)"><i class="fab fa-github"></i> Live GitHub info</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'ready' || repo?.status == 'error'" @click.prevent="updateRepository(repo)"><i class="fas fa-sync"></i> Force update</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'removed'" @click.prevent="updateRepository(repo)"><i class="fas fa-check-circle"></i> Enable</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.statusMessage" @click.prevent="showStatusMessage(repo)"><i class="fas fa-exclamation-triangle"></i> View status message</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'ready' || repo?.status == 'error'" @click.prevent="updateRepository(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Force update</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'removed'" @click.prevent="updateRepository(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check-circle"></use></svg> Enable</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.statusMessage" @click.prevent="showStatusMessage(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg> View status message</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" @click.prevent="removeCache(repo)"><i class="fas fa-broom"></i> Remove cache</a>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="repo?.status == 'ready'" @click.prevent="removeRepository(repo)"><i class="fas fa-trash-alt"></i> Remove</a>
|
||||
<a class="dropdown-item" href="#" @click.prevent="removeCache(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-broom"></use></svg> Remove cache</a>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="repo?.status == 'ready'" @click.prevent="removeRepository(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="filteredRepositories?.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span>No repositories match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -126,12 +126,12 @@
|
||||
<span style="font-size: 12px; color: var(--ink-muted);">{{ fmt?.number(total) }} results</span>
|
||||
<div class="pagination-compact" v-if="totalPage > 1">
|
||||
<button class="btn btn-sm" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i> Previous
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-left"></use></svg> Previous
|
||||
</button>
|
||||
<input type="number" class="form-control form-control-sm" min="1" style="width: 56px;" aria-label="Page" v-field="{ state: viewState, set: value => { query.page = value }, value: query?.page, form: null, options: {} }" :max="(totalPage)">
|
||||
<span>of {{ totalPage }}</span>
|
||||
<button class="btn btn-sm" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage">
|
||||
Next <i class="fas fa-chevron-right"></i>
|
||||
Next <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<span class="admin-filter-inline">
|
||||
|
||||
@@ -3,31 +3,31 @@
|
||||
<h1 class="paper-page-title">{{ userInfo?.username || 'User' }}</h1>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users" class="active"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<div class="user-detail-card" v-if="userInfo">
|
||||
<div class="user-header">
|
||||
<img width="56" height="56" v-if="userInfo?.photo" :src="safeUrl((userInfo?.photo))">
|
||||
<div>
|
||||
<h1>
|
||||
<h2>
|
||||
{{ userInfo?.username }}
|
||||
<span class="status-dot-wrap">
|
||||
<span class="status-dot" :class="{'status-ready': userInfo?.status == 'active', 'status-removed': userInfo?.status != 'active'}"></span>
|
||||
<span v-text="fmt?.title(userInfo?.status)"></span>
|
||||
</span>
|
||||
<span class="type-badge type-repo" v-if="userInfo?.isAdmin">Admin</span>
|
||||
</h1>
|
||||
</h2>
|
||||
<div class="user-actions" style="margin-top: 4px;">
|
||||
<button class="btn btn-sm text-danger" v-if="userInfo?.status !== 'banned'" @click="banUser()"><i class="fas fa-ban"></i> Ban</button>
|
||||
<button class="btn btn-sm" v-if="userInfo?.status === 'banned' || userInfo?.status === 'removed'" @click="activateUser()"><i class="fas fa-check-circle"></i> Activate</button>
|
||||
<button class="btn btn-sm" v-if="!userInfo?.isAdmin" @click="promoteUser()"><i class="fas fa-user-shield"></i> Promote to admin</button>
|
||||
<button class="btn btn-sm text-danger" v-if="userInfo?.isAdmin" @click="demoteUser()"><i class="fas fa-user-minus"></i> Remove admin</button>
|
||||
<button class="btn btn-sm text-danger" v-if="userInfo?.status !== 'banned'" @click="banUser()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-ban"></use></svg> Ban</button>
|
||||
<button class="btn btn-sm" v-if="userInfo?.status === 'banned' || userInfo?.status === 'removed'" @click="activateUser()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check-circle"></use></svg> Activate</button>
|
||||
<button class="btn btn-sm" v-if="!userInfo?.isAdmin" @click="promoteUser()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user-shield"></use></svg> Promote to admin</button>
|
||||
<button class="btn btn-sm text-danger" v-if="userInfo?.isAdmin" @click="demoteUser()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user-minus"></use></svg> Remove admin</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,7 +65,7 @@
|
||||
{{ showRepos ? 'Hide' : 'Show' }}
|
||||
</button>
|
||||
<button class="btn btn-sm ml-1" @click="getGitHubRepositories()">
|
||||
<i class="fas fa-sync"></i> Refresh
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,13 +75,13 @@
|
||||
<div class="paper-table w-100" style="margin-top: 10px;">
|
||||
<div class="paper-table-row" style="grid-template-columns: 1fr 160px;" v-for="(repo, index) in userInfo?.repositories">
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<svg class="ui-icon record-type-icon" role="img" aria-label="Repository"><use href="#project-type-repo"></use></svg>
|
||||
<div class="anon-text">
|
||||
<span class="repo-name" v-text="repo?.name"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<i class="fas fa-database"></i> {{ fmt?.humanFileSize(repo?.size) }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-database"></use></svg> {{ fmt?.humanFileSize(repo?.size) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,7 +89,7 @@
|
||||
</div>
|
||||
|
||||
<div class="admin-section-header" v-if="userInfo && userInfo?.isAdmin && user && user?.username == userInfo?.username">
|
||||
<h2><i class="fas fa-key"></i> API tokens</h2>
|
||||
<h2><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-key"></use></svg> API tokens</h2>
|
||||
<span class="section-count">{{ tokens?.length }}</span>
|
||||
</div>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
<form class="d-flex" style="gap: 8px; margin-bottom: 12px;" v-form="viewState" @submit.prevent="submitForm($event, () => { createToken() })">
|
||||
<input type="text" class="form-control" placeholder="Token name (e.g. dev-laptop)" required v-field="{ state: viewState, set: value => { tokenForm.name = value }, value: tokenForm?.name, form: null, options: {} }">
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-plus"></i> Generate</button>
|
||||
<button type="submit" class="btn btn-primary"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-plus"></use></svg> Generate</button>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-warning" role="alert" v-if="tokenForm?.plaintext">
|
||||
@@ -119,28 +119,28 @@
|
||||
<div role="cell" v-text="fmt?.humanTime(t?.createdAt)"></div>
|
||||
<div role="cell"><span v-if="t?.lastUsedAt">{{ fmt?.humanTime(t?.lastUsedAt) }}</span><span class="text-muted" v-if="!t?.lastUsedAt">never</span></div>
|
||||
<div role="cell">
|
||||
<button class="btn btn-sm text-danger" title="Revoke" @click="revokeToken(t)"><i class="fas fa-trash-alt"></i></button>
|
||||
<button class="btn btn-sm text-danger btn-icon" title="Revoke" aria-label="Revoke token" @click="revokeToken(t)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="!tokens?.length">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span>No tokens yet.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section-header">
|
||||
<h2><i class="fas fa-code-branch"></i> Anonymized repositories</h2>
|
||||
<h2><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Anonymized repositories</h2>
|
||||
<span class="section-count">{{ repositories?.length }}</span>
|
||||
</div>
|
||||
|
||||
<div class="admin-summary">
|
||||
<span class="summary-total">{{ fmt?.number(repositories?.length) }}</span>
|
||||
<span class="summary-pill ok" title="Toggle ready filter" :class="{active: filters?.status?.ready === false}" @click="filters.status.ready = !filters.status.ready">Ready <span class="count">{{ fmt?.number(statusCountFor('ready')) }}</span></span>
|
||||
<span class="summary-pill warn" title="Toggle preparing filter" :class="{active: filters?.status?.preparing === false}" @click="filters.status.preparing = !filters.status.preparing">Preparing <span class="count">{{ fmt?.number(statusCountFor('preparing')) }}</span></span>
|
||||
<span class="summary-pill error" title="Toggle errored filter" :class="{active: filters?.status?.error === false}" @click="filters.status.error = !filters.status.error">Errored <span class="count">{{ fmt?.number(statusCountFor('error')) }}</span></span>
|
||||
<span class="summary-pill" title="Toggle expired filter" :class="{active: filters?.status?.expired === false}" @click="filters.status.expired = !filters.status.expired">Expired <span class="count">{{ fmt?.number(statusCountFor('expired')) }}</span></span>
|
||||
<span class="summary-pill" title="Toggle removed filter" :class="{active: filters?.status?.removed === false}" @click="filters.status.removed = !filters.status.removed">Removed <span class="count">{{ fmt?.number(statusCountFor('removed')) }}</span></span>
|
||||
<button type="button" class="summary-pill ok" title="Toggle ready filter" :class="{active: filters?.status?.ready === false}" @click="filters.status.ready = !filters.status.ready" :aria-pressed="!!(filters?.status?.ready === false)">Ready <span class="count">{{ fmt?.number(statusCountFor('ready')) }}</span></button>
|
||||
<button type="button" class="summary-pill warn" title="Toggle preparing filter" :class="{active: filters?.status?.preparing === false}" @click="filters.status.preparing = !filters.status.preparing" :aria-pressed="!!(filters?.status?.preparing === false)">Preparing <span class="count">{{ fmt?.number(statusCountFor('preparing')) }}</span></button>
|
||||
<button type="button" class="summary-pill error" title="Toggle errored filter" :class="{active: filters?.status?.error === false}" @click="filters.status.error = !filters.status.error" :aria-pressed="!!(filters?.status?.error === false)">Errored <span class="count">{{ fmt?.number(statusCountFor('error')) }}</span></button>
|
||||
<button type="button" class="summary-pill" title="Toggle expired filter" :class="{active: filters?.status?.expired === false}" @click="filters.status.expired = !filters.status.expired" :aria-pressed="!!(filters?.status?.expired === false)">Expired <span class="count">{{ fmt?.number(statusCountFor('expired')) }}</span></button>
|
||||
<button type="button" class="summary-pill" title="Toggle removed filter" :class="{active: filters?.status?.removed === false}" @click="filters.status.removed = !filters.status.removed" :aria-pressed="!!(filters?.status?.removed === false)">Removed <span class="count">{{ fmt?.number(statusCountFor('removed')) }}</span></button>
|
||||
</div>
|
||||
|
||||
<form class="w-100 admin-filter-toolbar" aria-label="Repositories" accept-charset="UTF-8" v-form="viewState">
|
||||
@@ -149,14 +149,14 @@
|
||||
<input type="search" class="form-control" aria-label="Search repositories" placeholder="Search repoId, source repo, error message…" autocomplete="off" v-field="{ state: viewState, set: value => { search = value }, value: search, form: null, options: {} }">
|
||||
</div>
|
||||
<span class="admin-filter-spacer"></span>
|
||||
<button class="btn btn-sm" type="button" title="Export current view to CSV" @click="exportCsv()"><i class="fas fa-file-csv"></i> Export</button>
|
||||
<button class="btn btn-sm" type="button" title="Export current view to CSV" @click="exportCsv()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-export"></use></svg> Export</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="bulk-bar" v-if="selectedCount() > 0">
|
||||
<span><strong>{{ selectedCount() }}</strong> selected</span>
|
||||
<button class="btn btn-sm" type="button" @click="bulkRefresh()"><i class="fas fa-sync"></i> Force refresh</button>
|
||||
<button class="btn btn-sm text-danger" type="button" @click="bulkRemoveCache()"><i class="fas fa-broom"></i> Remove cache</button>
|
||||
<button class="btn btn-sm" type="button" @click="bulkRefresh()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Force refresh</button>
|
||||
<button class="btn btn-sm text-danger" type="button" @click="bulkRemoveCache()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-broom"></use></svg> Remove cache</button>
|
||||
<button class="btn btn-sm" type="button" @click="clearSelection()">Clear</button>
|
||||
</div>
|
||||
|
||||
@@ -165,10 +165,10 @@
|
||||
<div role="columnheader" style="width: 28px;">
|
||||
<input type="checkbox" aria-label="Select all on page" @click="selectAllOnPage()" :checked="allSelected">
|
||||
</div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'source.repositoryName'}" @click="sortBy('source.repositoryName')">Repository <i class="fas" :class="sortIcon('source.repositoryName')"></i></span></div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <i class="fas" :class="sortIcon('status')"></i></span></div>
|
||||
<div role="columnheader" class="num"><span class="sortable" :class="{active: query?.sort == 'pageView'}" @click="sortBy('pageView')">Views <i class="fas" :class="sortIcon('pageView')"></i></span></div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'anonymizeDate'}" @click="sortBy('anonymizeDate')">Anonymized <i class="fas" :class="sortIcon('anonymizeDate')"></i></span></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'source.repositoryName'}" @click="sortBy('source.repositoryName')">Repository <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('source.repositoryName')" :class="{ascending: sortIcon('source.repositoryName') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('status')" :class="{ascending: sortIcon('status') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" class="num"><button type="button" class="sortable" :class="{active: query?.sort == 'pageView'}" @click="sortBy('pageView')">Views <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('pageView')" :class="{ascending: sortIcon('pageView') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'anonymizeDate'}" @click="sortBy('anonymizeDate')">Anonymized <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('anonymizeDate')" :class="{ascending: sortIcon('anonymizeDate') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div class="paper-table-row" role="row" v-for="(repo, index) in filteredRepositories" :class="{'repo-inactive': repo?.status == 'expired' || repo?.status == 'removed', 'repo-error': repo?.status == 'error', 'row-selected': selected[repo?.repoId]}">
|
||||
@@ -176,11 +176,11 @@
|
||||
<input type="checkbox" aria-label="Select repository" v-field="{ state: viewState, set: value => { selected[repo.repoId] = value }, value: selected[repo?.repoId], form: null, options: {} }">
|
||||
</div>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<svg class="ui-icon record-type-icon" role="img" aria-label="Repository"><use href="#project-type-repo"></use></svg>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" target="_blank" v-text="repo?.repoId" :href="safeUrl("/r/" + (repo?.repoId))"></a>
|
||||
<div class="anon-sub">
|
||||
<a v-text="repo?.source?.fullName" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <i class="fas fa-chalkboard-teacher"></i> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} terms</span>
|
||||
<a v-text="repo?.source?.fullName" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} terms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,26 +196,26 @@
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/anonymize/" + (repo?.repoId))"><i class="far fa-edit"></i> Edit</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/r/" + (repo?.repoId) + "/")"><i class="fa fa-eye"></i> View repo</a>
|
||||
<a class="dropdown-item" target="_self" v-if="repo?.options?.page && repo?.status == 'ready'" :href="safeUrl("/w/" + (repo?.repoId) + "/")"><i class="fas fa-globe"></i> View page</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/anonymize/" + (repo?.repoId))"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/r/" + (repo?.repoId) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View repo</a>
|
||||
<a class="dropdown-item" target="_self" v-if="repo?.options?.page && repo?.status == 'ready'" :href="safeUrl("/w/" + (repo?.repoId) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-globe"></use></svg> View page</a>
|
||||
<a class="dropdown-item" href="#" @click.prevent="fetchGithubInfo(repo)"><i class="fab fa-github"></i> Live GitHub info</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'ready' || repo?.status == 'error'" @click.prevent="updateRepository(repo)"><i class="fas fa-sync"></i> Force update</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'removed'" @click.prevent="updateRepository(repo)"><i class="fas fa-check-circle"></i> Enable</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.statusMessage" @click.prevent="showStatusMessage(repo)"><i class="fas fa-exclamation-triangle"></i> View status message</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'ready' || repo?.status == 'error'" @click.prevent="updateRepository(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Force update</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.status == 'removed'" @click.prevent="updateRepository(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check-circle"></use></svg> Enable</a>
|
||||
<a class="dropdown-item" href="#" v-show="repo?.statusMessage" @click.prevent="showStatusMessage(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg> View status message</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" @click.prevent="removeCache(repo)"><i class="fas fa-broom"></i> Remove cache</a>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="repo?.status == 'ready'" @click.prevent="removeRepository(repo)"><i class="fas fa-trash-alt"></i> Remove</a>
|
||||
<a class="dropdown-item" href="#" @click.prevent="removeCache(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-broom"></use></svg> Remove cache</a>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="repo?.status == 'ready'" @click.prevent="removeRepository(repo)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="filteredRepositories?.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span>No repositories match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
<h1 class="paper-page-title">Users</h1>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
||||
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users" class="active"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
||||
<a href="/admin/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chart"></use></svg> Overview</a>
|
||||
<a href="/admin/repositories"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Repositories</a>
|
||||
<a href="/admin/users" class="active"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-users"></use></svg> Users</a>
|
||||
<a href="/admin/conferences"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conferences</a>
|
||||
<a href="/admin/queues"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-list"></use></svg> Queues</a>
|
||||
<a href="/admin/errors"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Errors</a>
|
||||
</nav>
|
||||
|
||||
<div class="admin-summary">
|
||||
<span class="summary-total">{{ total >= 0 ? fmt.number(total) : '…' }}</span>
|
||||
<span class="summary-pill ok" :class="{active: query?.status == 'active'}" @click="query.status = query.status == 'active' ? '' : 'active'; query.page = 1">Active <span class="count">{{ fmt?.number(statusCountFor('active')) }}</span></span>
|
||||
<span class="summary-pill error" :class="{active: query?.status == 'banned'}" @click="query.status = query.status == 'banned' ? '' : 'banned'; query.page = 1">Banned <span class="count">{{ fmt?.number(statusCountFor('banned')) }}</span></span>
|
||||
<span class="summary-pill" :class="{active: query?.status == 'removed'}" @click="query.status = query.status == 'removed' ? '' : 'removed'; query.page = 1">Removed <span class="count">{{ fmt?.number(statusCountFor('removed')) }}</span></span>
|
||||
<button type="button" class="summary-pill ok" :class="{active: query?.status == 'active'}" @click="query.status = query.status == 'active' ? '' : 'active'; query.page = 1" :aria-pressed="!!(query?.status == 'active')">Active <span class="count">{{ fmt?.number(statusCountFor('active')) }}</span></button>
|
||||
<button type="button" class="summary-pill error" :class="{active: query?.status == 'banned'}" @click="query.status = query.status == 'banned' ? '' : 'banned'; query.page = 1" :aria-pressed="!!(query?.status == 'banned')">Banned <span class="count">{{ fmt?.number(statusCountFor('banned')) }}</span></button>
|
||||
<button type="button" class="summary-pill" :class="{active: query?.status == 'removed'}" @click="query.status = query.status == 'removed' ? '' : 'removed'; query.page = 1" :aria-pressed="!!(query?.status == 'removed')">Removed <span class="count">{{ fmt?.number(statusCountFor('removed')) }}</span></button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger" style="margin: 8px 0;" v-if="fetchError">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ fetchError }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg> {{ fetchError }}
|
||||
</div>
|
||||
|
||||
<form class="w-100 admin-filter-toolbar" aria-label="Users" accept-charset="UTF-8" v-form="viewState">
|
||||
@@ -32,11 +32,11 @@
|
||||
<select class="form-control form-control-sm" v-field="{ state: viewState, set: value => { query.role = value }, value: query?.role, form: null, options: {} }"><option value="">Any</option><option value="admin">Admin</option></select>
|
||||
</span>
|
||||
<span class="admin-filter-spacer"></span>
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><i class="fas fa-file-csv"></i> Export</button>
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-export"></use></svg> Export</button>
|
||||
<span class="admin-filter-inline" aria-label="Pagination">
|
||||
<button class="btn btn-sm" type="button" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><i class="fas fa-chevron-left"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1" aria-label="Previous page"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-left"></use></svg></button>
|
||||
<span style="font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted);">{{ query?.page }}/{{ totalPage || 1 }}</span>
|
||||
<button class="btn btn-sm" type="button" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage"><i class="fas fa-chevron-right"></i></button>
|
||||
<button class="btn btn-sm btn-icon" type="button" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage" aria-label="Next page"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<span class="admin-active-chip" v-for="(chip, index) in chips" :key="chip?.key">
|
||||
<span class="key">{{ chip?.label }}</span>
|
||||
<span>{{ chip?.value }}</span>
|
||||
<button type="button" @click="clearFilter(chip.key)"><i class="fas fa-times"></i></button>
|
||||
<button type="button" @click="clearFilter(chip.key)" aria-label="Clear filter"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-close"></use></svg></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
<div class="bulk-bar" v-if="selectedCount() > 0">
|
||||
<span><strong>{{ selectedCount() }}</strong> selected</span>
|
||||
<button class="btn btn-sm text-danger" type="button" @click="bulkBan()"><i class="fas fa-ban"></i> Ban</button>
|
||||
<button class="btn btn-sm text-danger" type="button" @click="bulkBan()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-ban"></use></svg> Ban</button>
|
||||
<button class="btn btn-sm" type="button" @click="selected = {}; allSelected = false">Clear</button>
|
||||
</div>
|
||||
|
||||
@@ -62,9 +62,9 @@
|
||||
<div role="columnheader" style="width: 28px;">
|
||||
<input type="checkbox" aria-label="Select all on page" @click="selectAllOnPage()" :checked="allSelected">
|
||||
</div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'username'}" @click="sortBy('username')">User <i class="fas" :class="sortIcon('username')"></i></span></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'username'}" @click="sortBy('username')">User <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('username')" :class="{ascending: sortIcon('username') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader" class="num">Repos</div>
|
||||
<div role="columnheader"><span class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <i class="fas" :class="sortIcon('status')"></i></span></div>
|
||||
<div role="columnheader"><button type="button" class="sortable" :class="{active: query?.sort == 'status'}" @click="sortBy('status')">Status <svg class="ui-icon sort-direction" aria-hidden="true" v-if="sortIcon('status')" :class="{ascending: sortIcon('status') === 'fa-arrow-up'}"><use href="#project-icon-arrow"></use></svg></button></div>
|
||||
<div role="columnheader">Role</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
@@ -81,7 +81,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell">
|
||||
<div class="cell-views num" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg>
|
||||
<a title="Show this user's repositories" v-text="fmt?.number((u?.repoCount || 0))" :href="safeUrl("/admin/?owner=" + (u?.username))"></a>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
@@ -95,20 +95,20 @@
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/admin/users/" + (u?.username))"><i class="far fa-eye"></i> View details</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/admin/?owner=" + (u?.username))"><i class="fas fa-code-branch"></i> View repositories</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/admin/users/" + (u?.username))"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View details</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/admin/?owner=" + (u?.username))"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> View repositories</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="u?.status == 'active'" @click.prevent="banUser(u)"><i class="fas fa-ban"></i> Ban</a>
|
||||
<a class="dropdown-item" href="#" v-show="u?.status == 'removed' || u?.status == 'banned'" @click.prevent="activateUser(u)"><i class="fas fa-check-circle"></i> Activate</a>
|
||||
<a class="dropdown-item text-danger" href="#" v-show="u?.status == 'active'" @click.prevent="banUser(u)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-ban"></use></svg> Ban</a>
|
||||
<a class="dropdown-item" href="#" v-show="u?.status == 'removed' || u?.status == 'banned'" @click.prevent="activateUser(u)"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check-circle"></use></svg> Activate</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="filteredUsers?.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span>No users match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,10 +116,10 @@
|
||||
<div class="admin-toolbar" style="justify-content: space-between; border-bottom: none;">
|
||||
<span style="font-size: 12px; color: var(--ink-muted);">{{ fmt?.number(total) }} results</span>
|
||||
<div class="pagination-compact" v-if="totalPage > 1">
|
||||
<button class="btn btn-sm" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><i class="fas fa-chevron-left"></i> Previous</button>
|
||||
<button class="btn btn-sm" @click="query.page = Math.max(1, query.page - 1)" :disabled="query?.page <= 1"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-left"></use></svg> Previous</button>
|
||||
<input type="number" class="form-control form-control-sm" min="1" style="width: 56px;" v-field="{ state: viewState, set: value => { query.page = value }, value: query?.page, form: null, options: {} }" :max="(totalPage)">
|
||||
<span>of {{ totalPage }}</span>
|
||||
<button class="btn btn-sm" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage">Next <i class="fas fa-chevron-right"></i></button>
|
||||
<button class="btn btn-sm" @click="query.page = Math.min(totalPage, query.page + 1)" :disabled="query?.page >= totalPage">Next <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-chevron-right"></use></svg></button>
|
||||
</div>
|
||||
<span class="admin-filter-inline">
|
||||
<label>Per page</label>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<div class="anonymize-page h-100">
|
||||
<div class="anonymize-page h-100" :class="{'mobile-preview-active': mobilePane === 'preview'}">
|
||||
<!-- ===== STATE 1: No URL — centered input ===== -->
|
||||
<div class="anonymize-landing" v-show="!(sourceUrl)">
|
||||
<div class="anonymize-landing" v-show="!workspaceOpen && !isUpdate">
|
||||
<div class="anonymize-landing-inner">
|
||||
<div class="paper-crumbs">My work / <span class="here">New anonymization</span></div>
|
||||
<h1 class="paper-page-title">New <em>anonymization</em></h1>
|
||||
<p class="paper-page-lede">
|
||||
Paste a GitHub repository, pull-request, or gist URL. We’ll fetch
|
||||
it, strip every trace of identity, and hand you back a stable link.
|
||||
it, redact the terms you choose in text files, and give you a share link.
|
||||
Images and PDFs are served unchanged when enabled. Review them before sharing.
|
||||
</p>
|
||||
<div class="form-group mt-4 mb-2">
|
||||
<div class="source-url-label-row">
|
||||
<label class="paper-field-label" for="sourceUrl-landing">Source URL</label>
|
||||
<aside class="repo-access" aria-label="GitHub access" v-if="!sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected && githubConnections.oauthConnected">
|
||||
<div class="repo-access-switch" role="group" aria-label="Repository connection" v-if="!isUpdate && detectedType !== 'gist'">
|
||||
<button type="button" :aria-pressed="githubConnection === 'github-app'" :disabled="githubConnection === 'github-app'" @click="chooseGitHubConnection('github-app')"><i class="fas fa-shield-alt" aria-hidden="true"></i> <span class="sr-only">Read-only </span>GitHub App</button>
|
||||
<button type="button" :aria-pressed="githubConnection === 'github-app'" :disabled="githubConnection === 'github-app'" @click="chooseGitHubConnection('github-app')"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> <span class="sr-only">Read-only </span>GitHub App</button>
|
||||
<button type="button" :aria-pressed="githubConnection === 'oauth'" :disabled="githubConnection === 'oauth'" @click="chooseGitHubConnection('oauth')">Legacy OAuth</button>
|
||||
</div>
|
||||
<span class="connection-badge" v-if="isUpdate || detectedType === 'gist'">{{ detectedType !== 'gist' && githubConnection === 'github-app' ? 'Read-only GitHub App' : 'Legacy OAuth' }}</span>
|
||||
@@ -21,6 +22,8 @@
|
||||
</div>
|
||||
<input id="sourceUrl-landing" type="text" class="form-control form-control-lg" placeholder="https://github.com/owner/repository" v-field="{ state: viewState, set: value => { sourceUrl = value }, value: sourceUrl, form: null, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }, change: () => { urlSelected() } }">
|
||||
</div>
|
||||
<div class="draft-status" role="status" v-if="draftRestored">Your unsaved draft was restored. <button type="button" class="draft-discard" @click="discardDraft()">Discard draft</button></div>
|
||||
<p class="text-danger" role="alert" v-if="sourceUrlError">{{ sourceUrlError }}</p>
|
||||
<small class="form-text" style="color: var(--ink-muted);">
|
||||
Repository, pull request (…/pull/42) and gist (gist.github.com/…) URLs are all accepted.
|
||||
</small>
|
||||
@@ -33,17 +36,17 @@
|
||||
<aside class="repo-access-details" aria-label="GitHub access" v-if="!sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected && githubConnections.oauthConnected">
|
||||
<div class="repo-access-main">
|
||||
<div class="repo-access-controls">
|
||||
<button type="button" class="btn btn-outline-ink" v-if="detectedType !== 'gist' && githubConnection !== 'oauth'" @click="grantGitHubAccess()">Manage access <i class="fas fa-external-link-alt" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn btn-outline-ink" v-if="detectedType !== 'gist' && githubConnection !== 'oauth'" @click="grantGitHubAccess()">Manage access <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-external-link"></use></svg></button>
|
||||
<a class="connection-link" href="/connections" v-if="isUpdate || detectedType === 'gist' || githubConnection === 'oauth'">{{ isUpdate ? 'Change connection' : 'Manage connections' }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-access-note"><span v-if="detectedType !== 'gist' && githubConnection !== 'oauth'">Select repositories on GitHub, then return here.</span><span><i class="fas fa-info-circle" aria-hidden="true"></i> Selected content will be published, including content from private repositories.</span></div>
|
||||
<div class="repo-access-note"><span v-if="detectedType !== 'gist' && githubConnection !== 'oauth'">Select repositories on GitHub, then return here.</span><span><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Selected content will be published, including content from private repositories.</span></div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== STATE 2: URL provided — form (left) + preview (right) ===== -->
|
||||
<div class="anonymize-workspace" v-show="sourceUrl">
|
||||
<div class="anonymize-workspace" v-show="workspaceOpen || isUpdate">
|
||||
<header class="anonymize-topbar">
|
||||
<div class="anonymize-topbar-inner">
|
||||
<div class="paper-crumbs">
|
||||
@@ -62,9 +65,14 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="anonymize-mobile-tabs" role="tablist" aria-label="Anonymization workspace">
|
||||
<button id="workspace-settings-tab" role="tab" type="button" :aria-selected="mobilePane === 'settings'" aria-controls="workspace-settings" :tabindex="mobilePane === 'settings' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="selectMobilePane('settings')">Settings</button>
|
||||
<button id="workspace-preview-tab" role="tab" type="button" :aria-selected="mobilePane === 'preview'" aria-controls="workspace-preview" :tabindex="mobilePane === 'preview' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="selectMobilePane('preview')">Reviewer preview</button>
|
||||
</nav>
|
||||
<div class="anonymize-preview-empty" id="workspace-preview" v-if="detectedType !== 'repo' && !details" role="status">{{ sourceLoading ? 'Loading source…' : 'Choose an accessible source to see its preview.' }}</div>
|
||||
<div class="anonymize-split">
|
||||
<!-- Form column (left) -->
|
||||
<div class="anonymize-form-col overflow-auto">
|
||||
<div id="workspace-settings" :role="mobileWorkspace ? 'tabpanel' : null" :aria-labelledby="mobileWorkspace ? 'workspace-settings-tab' : null" class="anonymize-form-col overflow-auto">
|
||||
<form class="form needs-validation paper-settings-main" name="anonymize" novalidate v-form="viewState">
|
||||
|
||||
<section class="paper-settings-section">
|
||||
@@ -75,7 +83,7 @@
|
||||
<label class="paper-field-label" for="sourceUrl">GitHub URL</label>
|
||||
<aside class="repo-access" aria-label="GitHub access" v-if="sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected && githubConnections.oauthConnected">
|
||||
<div class="repo-access-switch" role="group" aria-label="Repository connection" v-if="!isUpdate && detectedType !== 'gist'">
|
||||
<button type="button" :aria-pressed="githubConnection === 'github-app'" :disabled="githubConnection === 'github-app'" @click="chooseGitHubConnection('github-app')"><i class="fas fa-shield-alt" aria-hidden="true"></i> <span class="sr-only">Read-only </span>GitHub App</button>
|
||||
<button type="button" :aria-pressed="githubConnection === 'github-app'" :disabled="githubConnection === 'github-app'" @click="chooseGitHubConnection('github-app')"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> <span class="sr-only">Read-only </span>GitHub App</button>
|
||||
<button type="button" :aria-pressed="githubConnection === 'oauth'" :disabled="githubConnection === 'oauth'" @click="chooseGitHubConnection('oauth')">Legacy OAuth</button>
|
||||
</div>
|
||||
<span class="connection-badge" v-if="isUpdate || detectedType === 'gist'">{{ detectedType !== 'gist' && githubConnection === 'github-app' ? 'Read-only GitHub App' : 'Legacy OAuth' }}</span>
|
||||
@@ -99,11 +107,11 @@
|
||||
<aside class="repo-access-details" aria-label="GitHub access" v-if="sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected && githubConnections.oauthConnected">
|
||||
<div class="repo-access-main">
|
||||
<div class="repo-access-controls">
|
||||
<button type="button" class="btn btn-outline-ink" v-if="detectedType !== 'gist' && githubConnection !== 'oauth'" @click="grantGitHubAccess()">Manage access <i class="fas fa-external-link-alt" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn btn-outline-ink" v-if="detectedType !== 'gist' && githubConnection !== 'oauth'" @click="grantGitHubAccess()">Manage access <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-external-link"></use></svg></button>
|
||||
<a class="connection-link" href="/connections" v-if="isUpdate || detectedType === 'gist' || githubConnection === 'oauth'">{{ isUpdate ? 'Change connection' : 'Manage connections' }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-access-note"><span v-if="detectedType !== 'gist' && githubConnection !== 'oauth'">Select repositories on GitHub, then return here.</span><span><i class="fas fa-info-circle" aria-hidden="true"></i> Selected content will be published, including content from private repositories.</span></div>
|
||||
<div class="repo-access-note"><span v-if="detectedType !== 'gist' && githubConnection !== 'oauth'">Select repositories on GitHub, then return here.</span><span><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Selected content will be published, including content from private repositories.</span></div>
|
||||
</aside>
|
||||
|
||||
<aside class="repo-access-details" v-if="isUpdate && detectedType === 'repo' && role === 'owner' && sourceUrl === 'https://github.com/' + _originalFullName">
|
||||
@@ -120,8 +128,8 @@
|
||||
<option v-for="(b, index) in branches" v-text="b?.name" :value="(b?.name)"></option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button class="btn" type="button" title="Refresh" data-toggle="tooltip" data-placement="bottom" @click="getBranches(true)">
|
||||
<i class="fas fa-sync" aria-hidden="true"></i>
|
||||
<button class="btn btn-icon" type="button" title="Refresh" data-toggle="tooltip" data-placement="bottom" @click="getBranches(true)" aria-label="Refresh">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,10 +215,12 @@
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="image" name="image" v-field="{ state: viewState, set: value => { options.image = value }, value: options?.image, form: "anonymize", options: {} }">
|
||||
<label class="form-check-label" for="image">Display images</label>
|
||||
<small class="form-text text-muted">Images are served unchanged and may contain names or affiliations.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="pdf" name="pdf" v-field="{ state: viewState, set: value => { options.pdf = value }, value: options?.pdf, form: "anonymize", options: {} }">
|
||||
<label class="form-check-label" for="pdf">Display PDFs</label>
|
||||
<small class="form-text text-muted">PDF contents and metadata are served unchanged. <a href="/faq#formats">About supported formats</a></small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" v-field="{ state: viewState, set: value => { options.notebook = value }, value: options?.notebook, form: "anonymize", options: {} }">
|
||||
@@ -219,10 +229,10 @@
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="page" name="page" v-field="{ state: viewState, set: value => { options.page = value }, value: options?.page, form: "anonymize", options: {} }" :disabled="!details?.hasPage || (details?.pageSource && details?.pageSource?.branch !== source?.branch)">
|
||||
<label class="form-check-label" for="page">GitHub Pages</label>
|
||||
<small class="form-text text-muted d-block" v-show="!details?.hasPage">
|
||||
<small class="form-text text-muted" v-if="!details?.hasPage">
|
||||
{{ fmt?.translate('WARNINGS.page_not_enabled_on_repo') }}
|
||||
</small>
|
||||
<small class="form-text text-muted d-block" v-show="details?.hasPage && details?.pageSource && details?.pageSource?.branch !== source?.branch">
|
||||
<small class="form-text text-muted" v-if="details?.hasPage && details?.pageSource?.branch && source?.branch && details.pageSource.branch !== source.branch">
|
||||
{{ fmt?.translate('WARNINGS.page_branch_mismatch', { pageBranch: details?.pageSource?.branch, selectedBranch: source?.branch }) }}
|
||||
</small>
|
||||
</div>
|
||||
@@ -335,8 +345,8 @@
|
||||
<img alt="" style="width: 24px; height: 24px; border-radius: 50%;" v-if="c?.photo" :src="safeUrl((c?.photo))">
|
||||
<a target="_blank" v-text="c?.username" :href="safeUrl("https://github.com/" + (c?.username))"></a>
|
||||
<span class="type-badge type-coauthor">Co-author</span>
|
||||
<button type="button" class="btn btn-sm" style="margin-left: auto;" title="Remove co-author" v-show="role === 'owner' || role === 'admin'" @click="removeCoauthor(c)">
|
||||
<i class="fas fa-times"></i>
|
||||
<button type="button" class="btn btn-sm btn-icon" style="margin-left: auto;" title="Remove co-author" v-show="role === 'owner' || role === 'admin'" @click="removeCoauthor(c)" aria-label="Remove co-author">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-close"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-text text-muted" v-show="!coauthors || coauthors?.length === 0">
|
||||
@@ -347,44 +357,61 @@
|
||||
|
||||
<div class="alert alert-danger" role="alert" v-if="error" v-text="error"></div>
|
||||
|
||||
<div class="draft-status" role="status" v-if="draftRestored">Your unsaved draft was restored. <button type="button" class="draft-discard" @click="discardDraft()">Discard draft</button></div>
|
||||
<div class="draft-status" v-else-if="unsavedChanges">{{ draftSaved ? "Draft saved in this tab for 30 minutes." : "Unsaved changes. Keep this tab open until you save." }} <button type="button" class="draft-discard" @click="discardDraft()">{{ isUpdate ? "Restore saved settings" : "Discard changes" }}</button></div>
|
||||
<p class="anonymize-review-note" v-if="detectedType === 'repo' && (options.image || options.pdf)">Review enabled images and PDFs before sharing. Their contents are not anonymized.</p>
|
||||
<button type="button" class="btn btn-outline-ink mobile-preview-jump" @click="selectMobilePane('preview')">Review preview <span aria-hidden="true">→</span></button>
|
||||
<div class="anonymize-submit-bar" v-show="detectedType">
|
||||
<button type="submit" class="btn btn-ink" v-if="detectedType === 'repo' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizeRepo($event) })">
|
||||
<i class="fas fa-user-secret mr-1"></i> Anonymize Repository
|
||||
<button type="submit" :disabled="sourceLoading" class="btn btn-ink" v-if="detectedType === 'repo' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizeRepo($event) })">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymize Repository
|
||||
</button>
|
||||
<button type="submit" class="btn btn-ink" v-if="detectedType === 'repo' && isUpdate" @click.prevent="submitForm($event, () => { anonymizeRepo($event) })">
|
||||
<i class="fas fa-save mr-1"></i> Update Repository
|
||||
<button type="submit" :disabled="sourceLoading" class="btn btn-ink" v-if="detectedType === 'repo' && isUpdate" @click.prevent="submitForm($event, () => { anonymizeRepo($event) })">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-save"></use></svg> Update Repository
|
||||
</button>
|
||||
<button type="submit" class="btn btn-ink" v-if="detectedType === 'pr' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
||||
<i class="fas fa-user-secret mr-1"></i> Anonymize Pull Request
|
||||
<button type="submit" :disabled="sourceLoading" class="btn btn-ink" v-if="detectedType === 'pr' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymize Pull Request
|
||||
</button>
|
||||
<button type="submit" class="btn btn-ink" v-if="detectedType === 'pr' && isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
||||
<i class="fas fa-save mr-1"></i> Update Pull Request
|
||||
<button type="submit" :disabled="sourceLoading" class="btn btn-ink" v-if="detectedType === 'pr' && isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-save"></use></svg> Update Pull Request
|
||||
</button>
|
||||
<button type="submit" class="btn btn-ink" v-if="detectedType === 'gist' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizeGist($event) })">
|
||||
<i class="fas fa-user-secret mr-1"></i> Anonymize Gist
|
||||
<button type="submit" :disabled="sourceLoading" class="btn btn-ink" v-if="detectedType === 'gist' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizeGist($event) })">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymize Gist
|
||||
</button>
|
||||
<button type="submit" class="btn btn-ink" v-if="detectedType === 'gist' && isUpdate" @click.prevent="submitForm($event, () => { anonymizeGist($event) })">
|
||||
<i class="fas fa-save mr-1"></i> Update Gist
|
||||
<button type="submit" :disabled="sourceLoading" class="btn btn-ink" v-if="detectedType === 'gist' && isUpdate" @click.prevent="submitForm($event, () => { anonymizeGist($event) })">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-save"></use></svg> Update Gist
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Preview column (right) -->
|
||||
<div class="anonymize-preview-col" v-if="detectedType === 'repo' && html_readme">
|
||||
<div id="workspace-preview" :role="mobileWorkspace ? 'tabpanel' : null" :aria-labelledby="mobileWorkspace ? 'workspace-preview-tab' : null" class="anonymize-preview-col" v-if="detectedType === 'repo'">
|
||||
<div class="anonymize-preview-head">
|
||||
<span class="paper-eyebrow">Live preview</span>
|
||||
<span class="anonymize-preview-sub">README with redactions applied</span>
|
||||
<span class="anonymize-preview-sub">{{ previewStatus === 'ready' ? 'README with redactions applied' : 'README preview' }}</span>
|
||||
</div>
|
||||
<div class="anonymize-preview-body markdown-body body" v-html="sanitize(html_readme)"></div>
|
||||
<div class="anonymize-preview-status" role="status" v-if="sourceLoading && previewStatus === 'idle'">Loading source…</div>
|
||||
<div class="anonymize-preview-status" role="status" v-if="previewStatus === 'loading'">Updating the anonymized preview…</div>
|
||||
<div class="anonymize-preview-status" role="alert" v-if="previewStatus === 'error'">
|
||||
<p>Unable to generate the anonymized preview. The latest redactions have not been verified.</p>
|
||||
<button class="btn btn-outline-ink" type="button" @click="retryPreview()">Retry preview</button>
|
||||
</div>
|
||||
<div class="anonymize-preview-status" v-if="!sourceLoading && !readme && previewStatus === 'idle'">No README is available to preview. Review the published files before sharing.</div>
|
||||
<div class="anonymize-preview-body markdown-body body" v-if="previewStatus === 'ready'" v-html="sanitize(html_readme)"></div>
|
||||
</div>
|
||||
|
||||
<div class="anonymize-preview-col" v-if="detectedType === 'gist' && details">
|
||||
<div id="workspace-preview" :role="mobileWorkspace ? 'tabpanel' : null" :aria-labelledby="mobileWorkspace ? 'workspace-preview-tab' : null" class="anonymize-preview-col" v-if="detectedType === 'gist' && details">
|
||||
<div class="anonymize-preview-head">
|
||||
<span class="paper-eyebrow">Live preview</span>
|
||||
<span class="anonymize-preview-sub">Gist with redactions applied</span>
|
||||
<span class="anonymize-preview-sub">{{ previewStatus === 'ready' ? 'Gist with redactions applied' : 'Gist preview' }}</span>
|
||||
</div>
|
||||
<div class="anonymize-preview-body">
|
||||
<div class="anonymize-preview-status" role="status" v-if="sourceLoading && previewStatus === 'idle'">Loading source…</div>
|
||||
<div class="anonymize-preview-status" role="status" v-if="previewStatus === 'loading'">Updating the anonymized preview…</div>
|
||||
<div class="anonymize-preview-status" role="alert" v-if="previewStatus === 'error'">
|
||||
<p>Unable to generate the anonymized preview. The latest redactions have not been verified.</p>
|
||||
<button class="btn btn-outline-ink" type="button" @click="retryPreview()">Retry preview</button>
|
||||
</div>
|
||||
<div class="anonymize-preview-body" v-if="previewStatus === 'ready'">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
|
||||
<h2 class="pr-title mb-1">
|
||||
<span v-if="options?.title">{{ anonymizeGistContent(details?.gist?.description) || 'Untitled gist' }}</span>
|
||||
@@ -413,7 +440,7 @@
|
||||
<li class="pr-comment" v-for="(comment, index) in details?.gist?.comments">
|
||||
<div class="pr-comment-head">
|
||||
<span class="pr-comment-author" v-if="options?.username">
|
||||
<i class="far fa-user"></i> @<span v-text="anonymizeGistContent(comment?.author)"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user"></use></svg> @<span v-text="anonymizeGistContent(comment?.author)"></span>
|
||||
</span>
|
||||
<span class="pr-comment-date" v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></span>
|
||||
</div>
|
||||
@@ -426,12 +453,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="anonymize-preview-col" v-if="detectedType === 'pr' && details">
|
||||
<div id="workspace-preview" :role="mobileWorkspace ? 'tabpanel' : null" :aria-labelledby="mobileWorkspace ? 'workspace-preview-tab' : null" class="anonymize-preview-col" v-if="detectedType === 'pr' && details">
|
||||
<div class="anonymize-preview-head">
|
||||
<span class="paper-eyebrow">Live preview</span>
|
||||
<span class="anonymize-preview-sub">Pull request with redactions applied</span>
|
||||
<span class="anonymize-preview-sub">{{ previewStatus === 'ready' ? 'Pull request with redactions applied' : 'Pull request preview' }}</span>
|
||||
</div>
|
||||
<div class="anonymize-preview-body">
|
||||
<div class="anonymize-preview-status" role="status" v-if="sourceLoading && previewStatus === 'idle'">Loading source…</div>
|
||||
<div class="anonymize-preview-status" role="status" v-if="previewStatus === 'loading'">Updating the anonymized preview…</div>
|
||||
<div class="anonymize-preview-status" role="alert" v-if="previewStatus === 'error'">
|
||||
<p>Unable to generate the anonymized preview. The latest redactions have not been verified.</p>
|
||||
<button class="btn btn-outline-ink" type="button" @click="retryPreview()">Retry preview</button>
|
||||
</div>
|
||||
<div class="anonymize-preview-body" v-if="previewStatus === 'ready'">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
|
||||
<h2 class="pr-title mb-1">
|
||||
<span v-if="options?.title">{{ anonymizePrContent(details?.pullRequest?.title) }}</span>
|
||||
@@ -446,24 +479,25 @@
|
||||
<markdown :content="anonymizePrContent(details?.pullRequest?.body)" :options="options" :terms="terms"></markdown>
|
||||
</div>
|
||||
<nav class="paper-tabs" role="tablist" v-if="options?.diff || options?.comments">
|
||||
<button class="paper-tab" type="button" role="tab" v-if="options?.diff" :class="{'active': prTabState?.active == 'diff'}" @click="prTabState.active = 'diff'">
|
||||
<i class="fas fa-code"></i> Diff
|
||||
<button class="paper-tab" type="button" role="tab" v-if="options?.diff" :class="{'active': prTabState?.active == 'diff'}" id="preview-pr-diff-tab" :aria-selected="prTabState?.active === 'diff'" aria-controls="preview-pr-diff-panel" :tabindex="prTabState?.active === 'diff' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="prTabState.active = 'diff'">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-code"></use></svg> Diff
|
||||
</button>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="options?.comments" :class="{'active': prTabState?.active == 'comments'}" @click="prTabState.active = 'comments'">
|
||||
<i class="far fa-comment-dots"></i>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="options?.comments" :class="{'active': prTabState?.active == 'comments'}" id="preview-pr-comments-tab" :aria-selected="prTabState?.active === 'comments'" aria-controls="preview-pr-comments-panel" :tabindex="prTabState?.active === 'comments' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="prTabState.active = 'comments'">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-comment"></use></svg>
|
||||
<span v-text="fmt.plural(details?.pullRequest?.comments?.length, {'0': 'No comments', 'one': '1 comment', 'other': '{} comments'})"></span>
|
||||
</button>
|
||||
</nav>
|
||||
<div class="anonymize-preview-status" v-if="!options?.diff && !options?.comments">Diff and comments are hidden by your display settings.</div>
|
||||
<div class="paper-tab-content">
|
||||
<div v-if="options?.diff && prTabState?.active == 'diff'">
|
||||
<div role="tabpanel" id="preview-pr-diff-panel" aria-labelledby="preview-pr-diff-tab" v-if="options?.diff && prTabState?.active == 'diff'">
|
||||
<div class="pr-diff" v-html="sanitize(fmt?.diff(anonymizePrContent(details?.pullRequest?.diff)))"></div>
|
||||
</div>
|
||||
<div v-if="options?.comments && prTabState?.active == 'comments'">
|
||||
<div role="tabpanel" id="preview-pr-comments-panel" aria-labelledby="preview-pr-comments-tab" v-if="options?.comments && prTabState?.active == 'comments'">
|
||||
<ul class="pr-comments">
|
||||
<li class="pr-comment" v-for="(comment, index) in details?.pullRequest?.comments">
|
||||
<div class="pr-comment-head">
|
||||
<span class="pr-comment-author" v-if="options?.username">
|
||||
<i class="far fa-user"></i> @<span v-text="anonymizePrContent(comment?.author)"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user"></use></svg> @<span v-text="anonymizePrContent(comment?.author)"></span>
|
||||
</span>
|
||||
<span class="pr-comment-date" v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></span>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
|
||||
<div class="paper-section-eyebrow anonymize-section-title">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conference ID
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conference ID
|
||||
</div>
|
||||
<!-- Conference -->
|
||||
<div class="form-group">
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
|
||||
<div class="paper-section-eyebrow anonymize-section-title">
|
||||
<i class="fas fa-shield-alt"></i> Anonymization Options
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymization Options
|
||||
</div>
|
||||
<!-- Pull Request ID -->
|
||||
<div class="form-group">
|
||||
@@ -112,7 +112,7 @@
|
||||
<div class="card-header" id="headingOne">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
<i class="fas fa-cog mr-1"></i> Advanced options
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-filter"></use></svg> Advanced options
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
@@ -169,10 +169,10 @@
|
||||
<div class="alert alert-danger" role="alert" v-if="error" v-text="error"></div>
|
||||
<div class="anonymize-submit-bar" v-show="pullRequestUrl">
|
||||
<button id="submit" type="submit" class="btn btn-ink btn-block" v-if="!isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
||||
<i class="fas fa-user-secret mr-1"></i> Anonymize
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymize
|
||||
</button>
|
||||
<button id="submit" type="submit" class="btn btn-ink btn-block" v-if="isUpdate" @click.prevent="submitForm($event, () => { updatePullRequest($event) })">
|
||||
<i class="fas fa-save mr-1"></i> Update
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-save"></use></svg> Update
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="container paper-page">
|
||||
<div class="container paper-page claim-page">
|
||||
<div class="paper-crumbs"><a href="/dashboard">My work</a> / <span class="here">Claim</span></div>
|
||||
<h1 class="paper-page-title">Claim an <em>anonymization</em></h1>
|
||||
<p class="paper-page-lede">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="container page paper-page">
|
||||
<div class="container page paper-page conference-page">
|
||||
<div>
|
||||
<div class="paper-crumbs">
|
||||
<a href="/dashboard">My work</a> / <a href="/conferences">Conferences</a> / <span class="here">{{ conference?.conferenceID }}</span>
|
||||
@@ -15,7 +15,7 @@
|
||||
<span class="status-dot" aria-hidden="true" :class="'status-' + conference?.status"></span>
|
||||
<span v-text="fmt?.statusLabel(conference?.status)"></span>
|
||||
</span>
|
||||
<a class="btn btn-outline-ink" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/edit")"><i class="far fa-edit mr-1" aria-hidden="true"></i> Edit conference</a>
|
||||
<a class="btn btn-outline-ink" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/edit")"><svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit conference</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-meta-rule">
|
||||
@@ -99,7 +99,7 @@
|
||||
</div>
|
||||
<div class="paper-table-row" role="row" v-for="(repo, index) in filteredRepositories" :key="repo?.repoId" :class="{'repo-inactive': repo?.status == 'expired' || repo?.status == 'removed', 'repo-error': repo?.status == 'error'}">
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<svg class="ui-icon record-type-icon" role="img" aria-label="Repository"><use href="#project-type-repo"></use></svg>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" v-text="repo?.repoId" :href="safeUrl("/r/" + (repo?.repoId) + "/")"></a>
|
||||
<div class="anon-sub">
|
||||
@@ -116,8 +116,8 @@
|
||||
</div>
|
||||
<div class="status-sub" v-if="repo?.anonymizeDate">anonymized {{ fmt?.humanTime(repo?.anonymizeDate) }}</div>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" v-text="fmt?.number(repo?.pageView)"></div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<div class="cell-views num" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> <span v-text="fmt?.number(repo?.pageView)"></span></div>
|
||||
<div class="cell-expires" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg>
|
||||
<span class="expires-never" v-if="repo?.status == 'ready' && (repo?.options?.expirationMode === 'never' || !repo?.options?.expirationDate)">Never</span>
|
||||
<span v-if="repo?.status == 'ready' && repo?.options?.expirationMode !== 'never' && repo?.options?.expirationDate" v-text="fmt?.humanTime(repo?.options?.expirationDate)"></span>
|
||||
<span class="expires-past" v-if="repo?.status == 'expired'">Expired<span v-if="repo?.options?.expirationDate"> {{ fmt?.humanTime(repo?.options?.expirationDate) }}</span></span>
|
||||
@@ -126,21 +126,21 @@
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :aria-label=""Actions for " + (repo?.repoId)">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/r/" + (repo?.repoId) + "/")">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i> View
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View
|
||||
</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/anonymize/" + (repo?.repoId))">
|
||||
<i class="far fa-edit" aria-hidden="true"></i> Edit
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="filteredRepositories?.length == 0">
|
||||
<i class="fas fa-inbox" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span v-if="!conference?.repositories?.length">No repository has been submitted to this conference yet.</span>
|
||||
<span v-if="conference?.repositories?.length">Nothing matches the current filters.</span>
|
||||
<div class="paper-table-empty-actions" v-if="conference?.repositories?.length">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="container page paper-page">
|
||||
<div class="container page paper-page conferences-page">
|
||||
<div class="row">
|
||||
<div class="w-100">
|
||||
<div class="paper-crumbs"><a href="/dashboard">My work</a> / <span class="here">Conferences</span></div>
|
||||
@@ -8,7 +8,7 @@
|
||||
<p class="paper-page-lede">Group anonymizations by venue, give chairs a shared dashboard, and set one expiry for every submission.</p>
|
||||
</div>
|
||||
<a href="/conference/new" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> New conference
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-plus"></use></svg> New conference
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="conference-feedback" role="alert" v-if="error"><span>{{ error }}</span><button type="button" class="btn btn-outline-ink" @click="loadConferences()">Retry</button></div>
|
||||
<div class="conference-feedback" role="status" v-if="loading">Loading conferences…</div>
|
||||
<div class="paper-table paper-table-conferences w-100" role="table" aria-label="Conferences">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Conference</div>
|
||||
@@ -80,7 +82,7 @@
|
||||
</div>
|
||||
<div class="paper-table-row row-clickable" role="row" v-for="(conference, index) in filteredConferences" :key="conference?.conferenceID" :class="{'repo-inactive': conference?.status == 'expired' || conference?.status == 'removed'}">
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Conf</span>
|
||||
<svg class="ui-icon record-type-icon" role="img" aria-label="Conference"><use href="#project-icon-conference"></use></svg>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" v-text="conference?.name" :href="safeUrl("/conference/" + (conference?.conferenceID))"></a>
|
||||
<div class="anon-sub">
|
||||
@@ -94,40 +96,40 @@
|
||||
<span class="status-word" v-text="fmt?.statusLabel(conference?.status)"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" v-text="fmt?.number(conference?.nbRepositories || 0)"></div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<div class="cell-views num" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> <span v-text="fmt?.number(conference?.nbRepositories || 0)"></span></div>
|
||||
<div class="cell-expires" role="cell"><svg class="ui-icon mobile-meta-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg>
|
||||
<span v-if="conference?.startDate">{{ fmt?.date(conference?.startDate, 'mediumDate') }} – {{ fmt?.date(conference?.endDate, 'mediumDate') }}</span>
|
||||
<span class="empty-dash" aria-label="No review window" v-if="!conference?.startDate">—</span>
|
||||
</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :aria-label=""Actions for " + (conference?.name)">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :disabled="conference?.removing" :aria-label=""Actions for " + (conference?.name)">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/")">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i> View
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View
|
||||
</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/conference/" + (conference?.conferenceID) + "/edit")">
|
||||
<i class="far fa-edit" aria-hidden="true"></i> Edit
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit
|
||||
</a>
|
||||
<div v-if="conference?.status != 'removed'">
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button" class="dropdown-item dropdown-item-danger" @click="removeConference(conference)">
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i> Remove
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" v-if="filteredConferences?.length == 0">
|
||||
<i class="fas fa-inbox" aria-hidden="true"></i>
|
||||
<div class="paper-table-empty" v-if="!loading && !error && filteredConferences?.length == 0">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span v-if="conferences?.length == 0">You have not created a conference yet.</span>
|
||||
<span v-if="conferences?.length > 0">Nothing matches the current filters.</span>
|
||||
<div class="paper-table-empty-actions">
|
||||
<button type="button" class="btn btn-outline-ink" v-if="conferences?.length > 0" @click="search = ''; filters.status.ready = true; filters.status.expired = true; filters.status.removed = true;">Clear filters</button>
|
||||
<a href="/conference/new" class="btn btn-ink" v-if="conferences?.length == 0"><i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> New conference</a>
|
||||
<a href="/conference/new" class="btn btn-ink" v-if="conferences?.length == 0"><svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-plus"></use></svg> New conference</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
<p>Read-only access to the repositories you choose.</p>
|
||||
<p class="paper-inline-alert paper-inline-alert-error" v-if="connections.appError" role="alert">{{ connections.appError }}. Reconnect to restore access.</p>
|
||||
<div class="connection-actions">
|
||||
<a class="btn btn-ink" v-if="connections.appConnected && !connections.appError" target="_self" href="/github/app/install">Manage repository access <i class="fas fa-external-link-alt" aria-hidden="true"></i></a>
|
||||
<a class="btn btn-ink" v-if="connections.appConnected && !connections.appError" target="_self" href="/github/app/install">Manage repository access <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-external-link"></use></svg></a>
|
||||
<a :class="connections.appConnected && !connections.appError ? 'connection-link' : 'btn btn-ink'" target="_self" href="/github/app/login">{{ connections.appConnected ? 'Reconnect account' : 'Connect GitHub account' }}</a>
|
||||
</div>
|
||||
<p class="connection-help">Choose repositories on GitHub. Organization access may need an administrator's approval.</p>
|
||||
<ul class="connection-installations" v-if="connections.installations?.length">
|
||||
<li v-for="installation in connections.installations" :key="installation.id"><div><strong>{{ installation.account }}</strong><span v-if="installation.suspended"> · Suspended</span></div><a target="_self" :href="'/github/app/install?installationId=' + installation.id">Manage access <span class="sr-only">for {{ installation.account }}</span> →</a></li>
|
||||
</ul>
|
||||
<button class="connection-link" :disabled="busy" @click="loadConnections()"><i class="fas fa-sync-alt" aria-hidden="true"></i> Refresh access</button>
|
||||
<button class="connection-link" :disabled="busy" @click="loadConnections()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Refresh access</button>
|
||||
</section>
|
||||
<section>
|
||||
<div class="connection-top"><i class="fas fa-key" aria-hidden="true"></i><span class="connection-badge connection-badge-muted">Legacy</span></div>
|
||||
<div class="connection-top"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-key"></use></svg><span class="connection-badge connection-badge-muted">Legacy</span></div>
|
||||
<div class="connection-title"><h2>GitHub OAuth</h2><span class="connection-status" :class="{'is-connected': connections.oauthConnected}">{{ connections.oauthConnected ? 'Connected' : 'Not connected' }}</span></div>
|
||||
<p>For existing OAuth anonymizations and gists.</p>
|
||||
<p class="connection-help">This connection uses GitHub's broader repository permissions. Existing work keeps using it until you switch below.</p>
|
||||
@@ -40,7 +40,7 @@
|
||||
<section class="connection-resources">
|
||||
<div class="connection-title"><h2>Repository connections</h2><span class="connection-count">{{ connections.resources?.length || 0 }} resources</span></div>
|
||||
<p>Check access, then switch each resource to the GitHub App. After reinstalling the App, reconnect existing resources here to keep their anonymous URLs and published snapshots.</p>
|
||||
<div class="connection-empty" v-if="!connections.resources?.length"><i class="fas fa-code-branch" aria-hidden="true"></i><div><h3>No repository connections yet</h3><p>Your repositories and pull requests will appear here once you anonymize them.</p></div><a class="connection-link" href="/anonymize">New anonymization →</a></div>
|
||||
<div class="connection-empty" v-if="!connections.resources?.length"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg><div><h3>No repository connections yet</h3><p>Your repositories and pull requests will appear here once you anonymize them.</p></div><a class="connection-link" href="/anonymize">New anonymization →</a></div>
|
||||
<div class="connection-resource" v-for="resource in connections.resources" :key="resource.type + resource.id">
|
||||
<div class="connection-resource-name"><strong>{{ resource.name || resource.id }}</strong><small>{{ resource.type }} · {{ resource.id }}</small></div>
|
||||
<span class="connection-badge" :class="{'connection-badge-muted': resource.connection === 'oauth'}">{{ resource.connection === 'github-app' ? 'Read-only App' : 'Legacy OAuth' }}</span>
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
<button v-if="!['archived', 'removed', 'preparing', 'queue', 'removing', 'expiring'].includes(resource.status) && (resource.status !== 'download' || (resource.type === 'pull-request' && (!resource.statusDate || new Date(resource.statusDate).getTime() <= Date.now() - 300000))) && resource.connection === 'github-app' && connections.oauthConnected" class="connection-link" :disabled="busy" @click="changeConnection(resource, 'oauth', false)">Use OAuth</button>
|
||||
</div>
|
||||
<p class="connection-footnote"><i class="fas fa-info-circle" aria-hidden="true"></i> Removing access stops new fetches from GitHub. Published content keeps its current expiration and removal settings.</p>
|
||||
<p class="connection-footnote"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Removing access stops new fetches from GitHub. Published content keeps its current expiration and removal settings.</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
+103
-79
@@ -1,64 +1,59 @@
|
||||
<div class="container page dashboard-page paper-page">
|
||||
<div class="row">
|
||||
<div class="w-100">
|
||||
<div class="paper-crumbs">My work / <span class="here">Dashboard</span></div>
|
||||
<div class="d-flex align-items-end justify-content-between flex-wrap" style="gap: 12px;">
|
||||
<div class="dashboard-heading">
|
||||
<div>
|
||||
<h1 class="paper-page-title">Your <em>anonymizations</em></h1>
|
||||
<p class="paper-page-lede">Every repository, pull request, and gist you’ve mirrored, with live status and stats.</p>
|
||||
<h1 class="paper-page-title">Projects</h1>
|
||||
<p class="paper-page-lede">Manage your anonymous links.</p>
|
||||
<details class="dashboard-quota" :open="quotaExpanded" @toggle="quotaExpanded = $event.target.open" @keydown.esc.prevent.stop="closeQuota()">
|
||||
<summary aria-controls="dashboard-usage" :aria-expanded="quotaExpanded">
|
||||
<svg class="quota-summary-ring" viewBox="0 0 24 24" aria-hidden="true" :class="quota ? 'quota-' + quota.repository.level : ''">
|
||||
<circle class="quota-ring-track" cx="12" cy="12" r="9"></circle>
|
||||
<circle class="quota-ring-fill" cx="12" cy="12" r="9" pathLength="100" v-if="quota && !quota.repository.unlimited" :stroke-dasharray="quota.repository.percent + ' 100'"></circle>
|
||||
<path v-if="quota?.repository.unlimited" d="M7 12c0-4 4-4 5 0s5 4 5 0-4-4-5 0-5 4-5 0" class="quota-ring-unlimited"></path>
|
||||
</svg>
|
||||
<span class="quota-summary-label">Usage</span>
|
||||
<span class="quota-summary-value" v-if="quota">{{ fmt.number(quota.repository.used) }}<span v-if="!quota.repository.unlimited"> / {{ fmt.number(quota.repository.total) }}</span> repos</span>
|
||||
<svg class="project-type-svg quota-summary-chevron" aria-hidden="true"><use href="#project-icon-chevron"></use></svg>
|
||||
</summary>
|
||||
<div class="quota-panel" id="dashboard-usage">
|
||||
<div class="quota-panel-heading"><h2>Account usage</h2><button type="button" class="project-close" aria-label="Close usage" @click="closeQuota()"><svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-close"></use></svg></button></div>
|
||||
<quota-usage :quota="quota" :fmt="fmt" v-if="quota"></quota-usage>
|
||||
<p class="quota-loading" role="status" v-else>Loading usage…</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="dashboard-heading-actions">
|
||||
<a href="/anonymize" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> New anonymization
|
||||
<a href="/anonymize" class="btn btn-ink" aria-label="New anonymization">
|
||||
<svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-plus"></use></svg><span class="dashboard-new-label">New link</span><span class="dashboard-new-mobile" aria-hidden="true">New</span>
|
||||
</a>
|
||||
<a href="/connections" class="dashboard-connections-link">
|
||||
<i class="fab fa-github" aria-hidden="true"></i> GitHub connections <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quota -->
|
||||
<div class="quota-row" v-if="quota">
|
||||
<div class="quota-item" v-for="(q, index) in [
|
||||
{key: 'repository', label: 'Repositories', kind: 'count'},
|
||||
{key: 'storage', label: 'Storage', kind: 'bytes'},
|
||||
{key: 'file', label: 'Files', kind: 'count'}
|
||||
]">
|
||||
<div class="quota-header">
|
||||
<span class="quota-label">{{ q?.label }}</span>
|
||||
<span class="quota-value" v-if="q?.kind === 'count'">
|
||||
{{ fmt?.number(quota[q?.key].used) }}<span v-if="!quota[q?.key].unlimited"> / {{ fmt?.number(quota[q?.key].total) }}</span>
|
||||
<span class="quota-unlimited-tag" v-if="quota[q?.key].unlimited">Unlimited</span>
|
||||
</span>
|
||||
<span class="quota-value" v-if="q?.kind === 'bytes'">
|
||||
{{ fmt?.humanFileSize(quota[q?.key].used) }}<span v-if="!quota[q?.key].unlimited"> / {{ fmt?.humanFileSize(quota[q?.key].total) }}</span>
|
||||
<span class="quota-unlimited-tag" v-if="quota[q?.key].unlimited">Unlimited</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="quota-track" role="progressbar" aria-valuemin="0" :class="'quota-' + quota[q?.key].level" :aria-label="(q?.label) + " quota"" :aria-valuenow="(quota[q?.key].used)" :aria-valuemax="(quota[q?.key].unlimited ? quota[q?.key].used : quota[q?.key].total)" :aria-valuetext="(quota[q?.key].unlimited ? 'unlimited' : fmt.number(quota[q?.key].percent, 0) + '% used')">
|
||||
<div class="quota-fill" v-if="!quota[q?.key].unlimited" :style="{width: quota[q?.key].percent + '%'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search + filters -->
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Filter anonymizations" accept-charset="UTF-8" v-form="viewState" @submit.prevent="submitForm($event, () => { $event.preventDefault() })">
|
||||
<div class="search-wrap">
|
||||
<input type="search" id="search" class="form-control" aria-label="Search anonymizations" placeholder="Search by name, source, or conference…" autocomplete="off" v-field="{ state: viewState, set: value => { search = value }, value: search, form: null, options: {} }">
|
||||
<input type="search" id="search" class="form-control" aria-label="Search anonymizations" placeholder="Search projects, IDs, or conferences…" autocomplete="off" v-field="{ state: viewState, set: value => { search = value }, value: search, form: null, options: {} }">
|
||||
</div>
|
||||
<div class="dashboard-filter-controls">
|
||||
<button class="btn dashboard-filters-toggle" type="button" :aria-expanded="filtersExpanded" aria-controls="dashboard-filter-controls" @click="filtersExpanded = !filtersExpanded">Filters<svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-filter"></use></svg></button>
|
||||
<div id="dashboard-filter-controls" class="dashboard-filter-controls" :class="{'expanded': filtersExpanded}">
|
||||
<div class="btn-group" role="group" aria-label="Type">
|
||||
<button type="button" class="btn" :class="{'btn-primary': typeFilter === 'all'}" @click="typeFilter = 'all'" :aria-pressed="(typeFilter === 'all')">All</button>
|
||||
<button type="button" class="btn" :class="{'btn-primary': typeFilter === 'repo'}" @click="typeFilter = 'repo'" :aria-pressed="(typeFilter === 'repo')">Repos</button>
|
||||
<button type="button" class="btn" :class="{'btn-primary': typeFilter === 'pr'}" @click="typeFilter = 'pr'" :aria-pressed="(typeFilter === 'pr')">PRs</button>
|
||||
<button type="button" class="btn" :class="{'btn-primary': typeFilter === 'gist'}" @click="typeFilter = 'gist'" :aria-pressed="(typeFilter === 'gist')">Gists</button>
|
||||
<button type="button" class="btn dashboard-type-filter" title="Repositories" :class="{'btn-primary': typeFilter === 'repo'}" @click="typeFilter = 'repo'" :aria-pressed="(typeFilter === 'repo')"><svg class="project-type-svg" aria-hidden="true"><use href="#project-type-repo"></use></svg><span class="type-filter-label">Repos</span></button>
|
||||
<button type="button" class="btn dashboard-type-filter" title="Pull requests" :class="{'btn-primary': typeFilter === 'pr'}" @click="typeFilter = 'pr'" :aria-pressed="(typeFilter === 'pr')"><svg class="project-type-svg" aria-hidden="true"><use href="#project-type-pr"></use></svg><span class="type-filter-label">PRs</span></button>
|
||||
<button type="button" class="btn dashboard-type-filter" title="Gists" :class="{'btn-primary': typeFilter === 'gist'}" @click="typeFilter = 'gist'" :aria-pressed="(typeFilter === 'gist')"><svg class="project-type-svg" aria-hidden="true"><use href="#project-type-gist"></use></svg><span class="type-filter-label">Gists</span></button>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="dashboard-extra-filters" class="dashboard-extra-filters" :class="{'expanded': filtersExpanded}">
|
||||
<div class="dropdown">
|
||||
<button class="btn dropdown-toggle" type="button" id="dropdownSort" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="filter-btn-label">Sort</span>
|
||||
<span class="filter-btn-value">{{ sortLabel() }}</span>
|
||||
<i class="fas" aria-hidden="true" :class="sortDesc() ? 'fa-arrow-down' : 'fa-arrow-up'"></i>
|
||||
<svg class="project-type-svg sort-direction" aria-hidden="true" :class="{'ascending': !sortDesc()}"><use href="#project-icon-arrow"></use></svg>
|
||||
<span class="sr-only">{{ sortDesc() ? 'descending' : 'ascending' }}</span>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
@@ -69,7 +64,6 @@
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button" class="dropdown-item" @click="toggleSortDirection()">
|
||||
<i class="fas fa-exchange-alt fa-rotate-90" aria-hidden="true"></i>
|
||||
{{ sortDesc() ? 'Switch to ascending' : 'Switch to descending' }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -90,43 +84,48 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Result count + active filter chips -->
|
||||
<div class="dashboard-meta" v-if="!loading">
|
||||
<div class="dashboard-view-tabs" role="group" aria-label="Project view">
|
||||
<button type="button" :aria-pressed="projectView === 'all'" @click="setProjectView('all')" aria-label="All projects"><span class="dashboard-view-long">All projects</span><span class="dashboard-view-short" aria-hidden="true">All</span></button>
|
||||
<button type="button" :aria-pressed="projectView === 'attention'" @click="setProjectView('attention')" :aria-label="'Needs attention ' + attentionCount()"><span class="dashboard-attention-prefix">Needs </span><span>attention</span> <span class="attention-count" :class="{'has-attention': attentionCount() > 0}">{{ attentionCount() }}</span></button>
|
||||
</div>
|
||||
<span class="dashboard-count">
|
||||
<span v-if="filteredItems?.length === items?.length">{{ fmt?.number(items?.length) }} anonymization<span v-if="items?.length !== 1">s</span></span>
|
||||
<span v-if="filteredItems?.length === items?.length">{{ fmt?.number(items?.length) }} project<span v-if="items?.length !== 1">s</span></span>
|
||||
<span v-if="filteredItems?.length !== items?.length">{{ fmt?.number(filteredItems?.length) }} of {{ fmt?.number(items?.length) }} shown</span>
|
||||
</span>
|
||||
<template v-for="(v, f, index) in filters?.status"><span class="filter-chip" v-if="v === false">
|
||||
Hiding {{ statusKeyLabels[f] }}
|
||||
<button type="button" class="filter-chip-close" @click="filters.status[f] = true;" :aria-label=""Show " + (statusKeyLabels[f]) + " again"">×</button>
|
||||
</span></template>
|
||||
<button type="button" class="btn-link-inline" v-if="hasActiveFilters()" @click="clearFilters()">Clear filters</button>
|
||||
<div class="dashboard-active-filters">
|
||||
<template v-for="(v, f, index) in filters?.status"><button type="button" class="filter-chip" v-if="v === false" @click="filters.status[f] = true" :aria-label="'Show ' + statusKeyLabels[f] + ' again'" :title="'Show ' + statusKeyLabels[f] + ' again'">{{ statusKeyLabels[f] }} hidden<svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-close"></use></svg></button></template>
|
||||
<button type="button" class="filter-chip" v-if="typeFilter !== 'all'" @click="typeFilter = 'all'" aria-label="Show all project types">{{ typeFilter === 'repo' ? 'Repositories' : typeFilter === 'pr' ? 'Pull requests' : 'Gists' }}<svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-close"></use></svg></button>
|
||||
<button type="button" class="btn-link-inline" v-if="showClearFilters()" @click="clearFilters()">Clear filters</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="paper-table paper-table-dashboard w-100" role="table" aria-label="Anonymizations" :aria-busy="(loading)">
|
||||
<div class="paper-table paper-table-dashboard" role="table" aria-label="Anonymizations" :aria-busy="(loading)">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader" :aria-sort="(isSortedBy('_name') ? (sortDesc() ? 'descending' : 'ascending') : 'none')">
|
||||
<button type="button" class="sortable" :class="{active: isSortedBy('_name')}" @click="setSort('_name')">
|
||||
Anonymization <i class="fas" aria-hidden="true" v-if="isSortedBy('_name')" :class="sortDesc() ? 'fa-arrow-down' : 'fa-arrow-up'"></i>
|
||||
<div role="columnheader" :aria-sort="(isSortedBy('_label') ? (sortDesc() ? 'descending' : 'ascending') : 'none')">
|
||||
<button type="button" class="sortable" :class="{active: isSortedBy('_label')}" @click="setSort('_label')">
|
||||
Project <svg class="project-type-svg sort-direction" aria-hidden="true" v-if="isSortedBy('_label')" :class="{'ascending': !sortDesc()}"><use href="#project-icon-arrow"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div role="columnheader" :aria-sort="(isSortedBy('status') ? (sortDesc() ? 'descending' : 'ascending') : 'none')">
|
||||
<button type="button" class="sortable" :class="{active: isSortedBy('status')}" @click="setSort('status')">
|
||||
Status <i class="fas" aria-hidden="true" v-if="isSortedBy('status')" :class="sortDesc() ? 'fa-arrow-down' : 'fa-arrow-up'"></i>
|
||||
Status <svg class="project-type-svg sort-direction" aria-hidden="true" v-if="isSortedBy('status')" :class="{'ascending': !sortDesc()}"><use href="#project-icon-arrow"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div role="columnheader" class="num" :aria-sort="(isSortedBy('pageView') ? (sortDesc() ? 'descending' : 'ascending') : 'none')">
|
||||
<button type="button" class="sortable" :class="{active: isSortedBy('pageView')}" @click="setSort('pageView')">
|
||||
Views <i class="fas" aria-hidden="true" v-if="isSortedBy('pageView')" :class="sortDesc() ? 'fa-arrow-down' : 'fa-arrow-up'"></i>
|
||||
Views <svg class="project-type-svg sort-direction" aria-hidden="true" v-if="isSortedBy('pageView')" :class="{'ascending': !sortDesc()}"><use href="#project-icon-arrow"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div role="columnheader" :aria-sort="(isSortedBy('options.expirationDate') ? (sortDesc() ? 'descending' : 'ascending') : 'none')">
|
||||
<button type="button" class="sortable" :class="{active: isSortedBy('options.expirationDate')}" @click="setSort('options.expirationDate')">
|
||||
Expires <i class="fas" aria-hidden="true" v-if="isSortedBy('options.expirationDate')" :class="sortDesc() ? 'fa-arrow-down' : 'fa-arrow-up'"></i>
|
||||
Expires <svg class="project-type-svg sort-direction" aria-hidden="true" v-if="isSortedBy('options.expirationDate')" :class="{'ascending': !sortDesc()}"><use href="#project-icon-arrow"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div role="columnheader"><span class="sr-only">Actions</span></div>
|
||||
@@ -143,27 +142,21 @@
|
||||
|
||||
<template v-for="(item, index) in filteredItems" :key="(item?._type + ':' + (item?._id || index))"><div class="paper-table-row" role="row" v-if="!loading" :class="{'repo-inactive': item?._statusKey == 'expired' || item?._statusKey == 'removed', 'repo-error': item?.status == 'error', 'row-clickable': !!item?._viewUrl}" @click="openItem(item, $event)">
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge" :class="{'type-repo': item?._type === 'repo', 'type-pr': item?._type === 'pr', 'type-gist': item?._type === 'gist'}">{{ item?._type === 'repo' ? 'Repo' : item?._type === 'pr' ? 'PR' : 'Gist' }}</span>
|
||||
<span class="project-type-icon" role="img" :aria-label="item?._type === 'repo' ? 'Repository' : item?._type === 'pr' ? 'Pull request' : 'Gist'" :title="item?._type === 'repo' ? 'Repository' : item?._type === 'pr' ? 'Pull request' : 'Gist'"><svg class="project-type-svg" aria-hidden="true"><use :href="'#project-type-' + item._type"></use></svg></span>
|
||||
<div class="anon-text">
|
||||
<div class="anon-title">
|
||||
<a class="repo-name" v-if="item?._viewUrl" v-text="item?._name" :href="safeUrl((item?._viewUrl))"></a>
|
||||
<span class="repo-name repo-name-static" v-if="!item?._viewUrl" v-text="item?._name"></span>
|
||||
<a class="repo-name" v-if="item?._viewUrl" :title="item._label" v-text="item?._label" :href="safeUrl((item?._viewUrl))"></a>
|
||||
<span class="repo-name repo-name-static" v-if="!item?._viewUrl" v-text="item?._label"></span>
|
||||
<span class="type-badge type-coauthor" title="You are a co-author on this anonymization" v-if="item?.role === 'coauthor'">Co-author</span>
|
||||
</div>
|
||||
<div class="anon-sub" v-if="!item?._broken || item?.conference">
|
||||
<span class="anon-source" v-if="item?._type === 'repo' && item?.source?.fullName && !item?._broken">
|
||||
<a target="_blank" rel="noopener" v-text="item?.source?.fullName" :href="safeUrl("https://github.com/" + (item?.source?.fullName) + "/")"></a><span v-if="item?.options?.update && item?.source?.branch"> · <a target="_blank" rel="noopener" v-text="item?.source?.branch" :href="safeUrl("https://github.com/" + (item?.source?.fullName) + "/tree/" + (item?.source?.branch))"></a></span><span v-if="item?.source?.commit"> · <a class="commit-hash" target="_blank" rel="noopener" :href="safeUrl("https://github.com/" + (item?.source?.fullName) + "/tree/" + (item?.source?.commit))">@{{ item?.source?.commit?.substring(0, 8) }}</a></span>
|
||||
</span>
|
||||
<span class="anon-source" v-if="item?._type === 'pr' && item?.source?.repositoryFullName && !item?._broken">
|
||||
<a target="_blank" rel="noopener" v-text="item?._source" :href="safeUrl("https://github.com/" + (item?.source?.repositoryFullName) + "/pull/" + (item?.source?.pullRequestId))"></a>
|
||||
</span>
|
||||
<span class="anon-source" v-if="item?._type === 'gist' && item?.source?.gistId && !item?._broken">
|
||||
<a target="_blank" rel="noopener" v-text="item?._source" :href="safeUrl("https://gist.github.com/" + (item?.source?.gistId))"></a>
|
||||
</span>
|
||||
<span class="cell-conf conf-tag" v-if="item?.conference" :title="(item?.conference)"><i class="fas fa-university" aria-hidden="true"></i> <span v-text="item?.conference"></span></span>
|
||||
<div class="anon-sub">
|
||||
<code class="anonymous-id" v-if="item?._id" :title="item._id">{{ item._id }}</code>
|
||||
<span class="commit-hash" v-if="item?._type === 'repo' && item?.source?.commit" :title="item.source.commit">{{ item.source.commit.substring(0, 8) }}</span>
|
||||
<span class="cell-conf conf-tag" v-if="item?.conference" :title="(item?.conference)"><svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-conference"></use></svg> <span v-text="item?.conference"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-row-meta" role="presentation">
|
||||
<div class="cell-status" role="cell">
|
||||
<div class="status-line">
|
||||
<span class="status-dot" aria-hidden="true" :class="'status-' + item?._statusKey"></span>
|
||||
@@ -173,60 +166,91 @@
|
||||
<div class="status-sub status-sub-warn" v-if="item?._stale" :title=""Last activity " + (fmt?.humanTime(item?.anonymizeDate || item?.lastView))">
|
||||
Last activity {{ fmt?.humanTime(item?.anonymizeDate || item?.lastView) }} · may be stuck
|
||||
</div>
|
||||
<div class="status-sub" v-if="item?.status != 'error' && !item?._stale && item?.anonymizeDate">
|
||||
<a class="project-attention-action" v-if="item.status === 'error' && item._editUrl" :href="safeUrl(item._editUrl)">Fix source</a>
|
||||
<button type="button" class="project-attention-action" v-if="item._stale || item._broken" @click.stop="showProjectDetails(item)">{{ item._broken ? 'Review details' : 'Check progress' }}</button>
|
||||
<div class="status-sub status-sub-date" v-if="item?.status != 'error' && !item?._stale && item?.anonymizeDate">
|
||||
anonymized {{ fmt?.humanTime(item?.anonymizeDate) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" v-text="fmt?.number(item?.pageView)"></div>
|
||||
<div class="cell-views num" role="cell" :aria-label="fmt.number(item.pageView) + ' views'"><svg class="project-type-svg mobile-meta-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg><span>{{ fmt.number(item.pageView) }}</span></div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<svg class="project-type-svg mobile-meta-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg>
|
||||
<span class="expires-never" v-if="item?._expiry?.kind === 'never'">Never</span>
|
||||
<span v-if="item?._expiry?.kind === 'date'" v-text="fmt?.humanTime(item?._expiry?.date)"></span>
|
||||
<span class="expires-past" v-if="item?._expiry?.kind === 'expired' && item?._expiry?.date">Expired {{ fmt?.humanTime(item?._expiry?.date) }}</span>
|
||||
<span class="expires-past" v-if="item?._expiry?.kind === 'expired' && !item?._expiry?.date">Expired</span>
|
||||
<span class="empty-dash" aria-label="Not applicable" v-if="item?._expiry?.kind === 'none'">—</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<button type="button" class="copy-link-inline" v-if="item._viewUrl" @click.stop="copyLink(item)" :disabled="!!copyingId" :title="'Copy anonymous link for ' + item._label" :aria-label="'Copy anonymous link for ' + item._label"><svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-copy"></use></svg></button>
|
||||
<button type="button" class="project-details-button" v-if="item._broken" @click.stop="showProjectDetails(item)" :data-project-actions="encodeURIComponent(item._type + ':' + item._id)" :aria-controls="projectDetailsId(item)" :aria-expanded="selectedProject === item">Details</button>
|
||||
<div class="dropdown" v-if="!item?._broken">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :aria-label=""Actions for " + (item?._name)">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :data-project-actions="encodeURIComponent(item._type + ':' + item._id)" :aria-label=""Actions for " + (item?._name)">
|
||||
<svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-more"></use></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<button type="button" class="dropdown-item" @click="showProjectDetails(item)" :aria-controls="projectDetailsId(item)" :aria-expanded="selectedProject === item">Project details & name</button>
|
||||
<a class="dropdown-item" :href="safeUrl((item?._viewUrl))">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i> View
|
||||
View
|
||||
</a>
|
||||
<a class="dropdown-item" target="_self" v-if="item?._type === 'repo' && item?.options?.page && item?.status == 'ready'" :href="safeUrl("/w/" + (item?.repoId) + "/")">
|
||||
<i class="fas fa-globe" aria-hidden="true"></i> View page
|
||||
View page
|
||||
</a>
|
||||
<button type="button" class="dropdown-item" @click="copyLink(item)" :disabled="!!copyingId" v-if="item?._viewUrl">
|
||||
Copy anonymous link
|
||||
</button>
|
||||
<a class="dropdown-item" :href="safeUrl((item?._editUrl))">
|
||||
<i class="far fa-edit" aria-hidden="true"></i> Edit
|
||||
Edit
|
||||
</a>
|
||||
<button type="button" class="dropdown-item" v-if="item?.status == 'ready' || item?.status == 'error'" @click="refreshItem(item)">
|
||||
<i class="fas fa-sync" aria-hidden="true"></i> Force update
|
||||
Force update
|
||||
</button>
|
||||
<button type="button" class="dropdown-item" v-if="item?.status == 'removed'" @click="refreshItem(item)">
|
||||
<i class="fas fa-check-circle" aria-hidden="true"></i> Enable
|
||||
Enable
|
||||
</button>
|
||||
<button type="button" class="dropdown-item" v-if="item?.status == 'expired'" @click="extendItem(item)">
|
||||
<i class="fas fa-calendar-plus" aria-hidden="true"></i> Extend 6 months
|
||||
Extend 6 months
|
||||
</button>
|
||||
<div v-if="(item?.status == 'ready' || item?.status == 'expired' || item?.status == 'error') && item?.role !== 'coauthor'">
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button" class="dropdown-item dropdown-item-danger" @click="removeItem(item)">
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i> Remove
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-details-row" role="row" v-if="!loading && selectedProject === item">
|
||||
<div class="project-details" role="cell" aria-colspan="5" :id="projectDetailsId(item)" tabindex="-1" @keydown.esc.stop="closeProjectDetails(item)">
|
||||
<div class="project-details-heading"><h2>Project details</h2><button type="button" class="project-close" @click="closeProjectDetails(item)" aria-label="Close project details"><svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-close"></use></svg></button></div>
|
||||
<dl class="project-details-list">
|
||||
<div><dt>Source</dt><dd>{{ item._source || 'Not available' }}</dd></div>
|
||||
<div><dt>Anonymous ID</dt><dd><code>{{ item._id || 'Missing identifier' }}</code></dd></div>
|
||||
<div v-if="item.source?.commit"><dt>Commit</dt><dd><code>{{ item.source.commit }}</code></dd></div>
|
||||
<div v-if="item.conference"><dt>Conference</dt><dd>{{ item.conference }}</dd></div>
|
||||
<div><dt>Status</dt><dd>{{ fmt.statusLabel(item.status) }}<span v-if="item.statusMessage"> · {{ fmt.statusMsg(item.statusMessage) }}</span></dd></div>
|
||||
<div v-if="item.anonymizeDate"><dt>Last anonymized</dt><dd>{{ fmt.humanTime(item.anonymizeDate) }}</dd></div>
|
||||
</dl>
|
||||
<p class="project-progress-help" v-if="item._stale">This download has not progressed recently. Check the source access and selected branch before retrying.</p>
|
||||
<a class="project-attention-action" v-if="item._editUrl" :href="safeUrl(item._editUrl)">{{ needsAttention(item) ? 'Check source settings' : 'Edit anonymization settings' }}</a>
|
||||
<form class="project-name-form" v-if="item._id" @submit.prevent="saveProjectName(item)">
|
||||
<label :for="'project-name-' + item._type + '-' + item._id">Project name</label>
|
||||
<p :id="'project-name-help-' + item._type + '-' + item._id">Only you see this name. Leave it blank to use the default.</p>
|
||||
<div class="project-name-controls"><input class="form-control" type="text" :id="'project-name-' + item._type + '-' + item._id" :aria-describedby="'project-name-help-' + item._type + '-' + item._id" maxlength="100" v-model="projectName" :disabled="nameSaving"><button class="btn btn-ink" type="submit" :disabled="nameSaving">{{ nameSaving ? 'Saving…' : 'Save name' }}</button></div>
|
||||
<p class="project-name-error" role="alert" v-if="nameError">{{ nameError }}</p><p role="status" v-if="nameMessage">{{ nameMessage }}</p>
|
||||
</form>
|
||||
</div>
|
||||
</div></template>
|
||||
|
||||
<div class="paper-table-empty" v-if="!loading && filteredItems?.length == 0">
|
||||
<i class="fas fa-inbox" aria-hidden="true"></i>
|
||||
<svg class="project-type-svg project-empty-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span v-if="items?.length == 0">You have no anonymizations yet.</span>
|
||||
<span v-if="items?.length > 0">Nothing matches the current filters.</span>
|
||||
<div class="paper-table-empty-actions">
|
||||
<button type="button" class="btn btn-outline-ink" v-if="hasActiveFilters()" @click="clearFilters()">Clear filters</button>
|
||||
<a href="/anonymize" class="btn btn-ink" v-if="items?.length == 0"><i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> New anonymization</a>
|
||||
<a href="/anonymize" class="btn btn-ink" v-if="items?.length == 0"><svg class="project-type-svg" aria-hidden="true"><use href="#project-icon-plus"></use></svg> New anonymization</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="explorer-page">
|
||||
<button class="sidebar-toggle" v-show="files?.length" @click="sidebarCollapsed = !sidebarCollapsed" :aria-label="(sidebarCollapsed ? 'Show files' : 'Hide files')">
|
||||
<i class="fas" :class="sidebarCollapsed ? 'fa-folder-open' : 'fa-times'"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use :href="sidebarCollapsed ? '#ui-icon-folder' : '#project-icon-close'"></use></svg>
|
||||
<span v-text="sidebarCollapsed ? 'Files' : 'Close'"></span>
|
||||
</button>
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
<div class="leftCol-head">
|
||||
<span class="leftCol-eyebrow">Files</span>
|
||||
<button class="leftCol-close" aria-label="Close files" @click="sidebarCollapsed = true">
|
||||
<i class="fas fa-times"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-close"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="leftCol-search">
|
||||
<div class="tree-search-box">
|
||||
<i class="fas tree-search-icon" :class="fileSearchLoading ? 'fa-spinner fa-spin' : 'fa-search'"></i>
|
||||
<svg class="ui-icon tree-search-icon" aria-hidden="true" :class="{'fa-spin': fileSearchLoading}"><use :href="fileSearchLoading ? '#ui-icon-spinner' : '#ui-icon-search'"></use></svg>
|
||||
<input type="text" class="tree-search-input" placeholder="Search files" aria-label="Search files" v-field="{ state: viewState, set: value => { fileSearchQuery = value }, value: fileSearchQuery, form: null, options: { debounce: 300 }, change: () => { onFileSearchChange() } }">
|
||||
<kbd class="tree-search-kbd" v-show="!(fileSearchQuery)">{{ isMac ? '⌘' : 'Ctrl+' }}K</kbd>
|
||||
<button class="tree-search-clear" aria-label="Clear search" v-show="fileSearchQuery" @click="fileSearchQuery = ''; onFileSearchChange()">×</button>
|
||||
@@ -25,11 +25,11 @@
|
||||
</div>
|
||||
<div class="leftCol-body">
|
||||
<div class="paper-inline-warning" role="alert" v-if="options?.truncatedFolders?.length > 0">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg>
|
||||
{{ fmt?.translate('WARNINGS.repo_truncated') }}
|
||||
</div>
|
||||
<div class="paper-inline-warning" role="alert" v-if="options?.hasSubmodules">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg>
|
||||
{{ fmt?.translate('WARNINGS.submodules_not_included') }}
|
||||
</div>
|
||||
<tree class="files" :file="files" :search-query="fileSearchQuery" :search-results="fileSearchResults" :page="viewState"></tree>
|
||||
@@ -58,19 +58,19 @@
|
||||
<li class="breadcrumb-item" v-for="(p, index) in paths" v-text="p"></li>
|
||||
</ol>
|
||||
<div class="status-bar-actions">
|
||||
<a class="btn btn-sm" aria-label="Edit" v-if="options?.isAdmin || options?.isOwner" :href="safeUrl("/anonymize/" + (repoId))"><i class="far fa-edit"></i><span class="d-none d-md-inline"> Edit</span></a>
|
||||
<a class="btn btn-sm" aria-label="Edit" v-if="options?.isAdmin || options?.isOwner" :href="safeUrl("/anonymize/" + (repoId))"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg><span class="d-none d-md-inline"> Edit</span></a>
|
||||
<button class="btn btn-sm" aria-label="Allow this document to run JavaScript" title="Scripts in this file are blocked by default. Enable them only if you trust the repository — the document stays isolated from your session either way." v-if="type == 'html-doc' && !showSource" :class="{'btn-active': allowScripts}" @click="toggleAllowScripts()">
|
||||
<i class="fab fa-js"></i><span class="d-none d-md-inline">
|
||||
{{ allowScripts ? "JS on" : "JS off" }}</span>
|
||||
</button>
|
||||
<button class="btn btn-sm" aria-label="Toggle between the rendered document and its source" title="Toggle between the rendered document and its source" v-if="type == 'html-doc'" @click="toggleSource()">
|
||||
<i class="fas" :class="showSource ? 'fa-eye' : 'fa-code'"></i><span class="d-none d-md-inline">
|
||||
<svg class="ui-icon" aria-hidden="true"><use :href="showSource ? '#project-icon-eye' : '#ui-icon-code'"></use></svg><span class="d-none d-md-inline">
|
||||
{{ showSource ? "Rendered" : "Source" }}</span>
|
||||
</button>
|
||||
<a target="_self" class="btn btn-sm" aria-label="View raw current file" title="View the raw content of the current file" v-show="content != null" :href="safeUrl((url))"><i class="fas fa-file-alt"></i><span class="d-none d-md-inline"> Raw</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Download current file" title="Download the current file" v-show="content != null" :href="safeUrl((url) + "&download=true")"><i class="fas fa-download"></i><span class="d-none d-md-inline"> Download</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Download full repository as ZIP" title="Download the full repository as a ZIP archive" v-if="options?.download" :href="safeUrl("/api/repo/" + (repoId) + "/zip")"><i class="fas fa-file-archive"></i><span class="d-none d-md-inline"> Full repo ZIP</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Website" v-if="options?.hasWebsite" :href="safeUrl("/w/" + (repoId) + "/")"><i class="fas fa-globe"></i><span class="d-none d-md-inline"> Website</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="View raw current file" title="View the raw content of the current file" v-show="content != null" :href="safeUrl((url))"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-file"></use></svg><span class="d-none d-md-inline"> Raw</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Download current file" title="Download the current file" v-show="content != null" :href="safeUrl((url) + "&download=true")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-download"></use></svg><span class="d-none d-md-inline"> Download</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Download full repository as ZIP" title="Download the full repository as a ZIP archive" v-if="options?.download" :href="safeUrl("/api/repo/" + (repoId) + "/zip")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-archive"></use></svg><span class="d-none d-md-inline"> Full repo ZIP</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Website" v-if="options?.hasWebsite" :href="safeUrl("/w/" + (repoId) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-globe"></use></svg><span class="d-none d-md-inline"> Website</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="explorer-content">
|
||||
|
||||
+8
-27
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- General -->
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-info-circle mr-2"></i>General
|
||||
<svg class="ui-icon mr-2" aria-hidden="true"><use href="#ui-icon-info"></use></svg>General
|
||||
</h4>
|
||||
<div class="panel-group" id="faq-general" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default mb-3">
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
<!-- Features -->
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-cogs mr-2"></i>Features
|
||||
<svg class="ui-icon mr-2" aria-hidden="true"><use href="#project-icon-filter"></use></svg>Features
|
||||
</h4>
|
||||
<div class="panel-group" id="faq-features" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default mb-3">
|
||||
@@ -285,7 +285,7 @@ anonymous_github</code></pre>
|
||||
|
||||
<!-- Limitations -->
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>Limitations
|
||||
<svg class="ui-icon mr-2" aria-hidden="true"><use href="#ui-icon-warning"></use></svg>Limitations
|
||||
</h4>
|
||||
<div class="panel-group" id="faq-limitations" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default mb-3">
|
||||
@@ -322,7 +322,7 @@ anonymous_github</code></pre>
|
||||
|
||||
<!-- Privacy & Security -->
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-shield-alt mr-2"></i>Privacy & Security
|
||||
<svg class="ui-icon mr-2" aria-hidden="true"><use href="#ui-icon-shield"></use></svg>Privacy & Security
|
||||
</h4>
|
||||
<div class="panel-group" id="faq-privacy" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default mb-3">
|
||||
@@ -355,28 +355,9 @@ anonymous_github</code></pre>
|
||||
</div>
|
||||
<div id="permissions" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingPermissions">
|
||||
<div class="panel-body p-3">
|
||||
<p>
|
||||
Anonymous GitHub only reads your repositories — it
|
||||
never pushes commits, opens issues, modifies settings, or
|
||||
deletes anything. From your perspective as a user, the
|
||||
service is read-only.
|
||||
</p>
|
||||
<p>
|
||||
However, GitHub's OAuth scopes do not offer a read-only
|
||||
option for private repositories: the only scope that grants
|
||||
access to private repos is <code>repo</code>, which is
|
||||
documented as full read/write access. To support users who
|
||||
want to anonymize a private repository, Anonymous GitHub
|
||||
must request that scope, and GitHub then displays the
|
||||
broader permission text at sign-in. The application itself
|
||||
only ever performs read operations against the GitHub API.
|
||||
</p>
|
||||
<p>
|
||||
If you only anonymize public repositories, the source code
|
||||
is open and can be audited on the
|
||||
<a href="https://github.com/tdurieux/anonymous_github/" target="_blank">GitHub repository</a>,
|
||||
or you can self-host your own instance.
|
||||
</p>
|
||||
<p>The GitHub App has read-only access to the repositories you choose. You can change repository access on your <a href="/connections">GitHub connections</a> page.</p>
|
||||
<p>Legacy GitHub OAuth includes write access to private repositories because GitHub's OAuth scopes do not offer a read-only option for them. Existing OAuth anonymizations and gists can still use this connection.</p>
|
||||
<p>Anonymous GitHub reads repository content. It never pushes, modifies, or deletes files on GitHub.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -403,7 +384,7 @@ anonymous_github</code></pre>
|
||||
|
||||
<!-- Self-Hosting -->
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-server mr-2"></i>Self-Hosting
|
||||
<svg class="ui-icon mr-2" aria-hidden="true"><use href="#project-icon-storage"></use></svg>Self-Hosting
|
||||
</h4>
|
||||
<div class="panel-group" id="faq-hosting" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default mb-3">
|
||||
|
||||
+15
-13
@@ -6,40 +6,42 @@
|
||||
</div>
|
||||
|
||||
<header class="pr-header">
|
||||
<div class="pr-title-row">
|
||||
<h1 class="paper-page-title pr-title">
|
||||
<span v-if="details?.description" v-text="details?.description"></span>
|
||||
<span class="text-muted" v-if="!details?.description">Untitled gist</span>
|
||||
<a class="btn btn-sm" aria-label="Edit" v-if="options?.isAdmin || options?.isOwner" :href="safeUrl("/gist-anonymize/" + (gistId))"><i class="far fa-edit"></i><span class="d-none d-md-inline"> Edit</span></a>
|
||||
</h1>
|
||||
<a class="btn btn-sm" aria-label="Edit" v-if="options?.isAdmin || options?.isOwner" :href="safeUrl("/gist-anonymize/" + (gistId))"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg><span class="d-none d-md-inline"> Edit</span></a>
|
||||
</div>
|
||||
<div class="pr-header-meta">
|
||||
<span class="paper-pill" :class="{'good': details?.isPublic, 'warn': !details?.isPublic}">
|
||||
{{ details?.isPublic ? 'Public' : 'Secret' }}
|
||||
</span>
|
||||
<span class="pr-meta-item" v-if="details?.ownerLogin">
|
||||
<i class="far fa-user"></i> @<span v-text="details?.ownerLogin"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user"></use></svg> @<span v-text="details?.ownerLogin"></span>
|
||||
</span>
|
||||
<span class="pr-meta-item" v-if="details?.updatedDate">
|
||||
<i class="far fa-clock"></i> <span v-text="fmt?.date(details?.updatedDate)"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg> <span v-text="fmt?.date(details?.updatedDate)"></span>
|
||||
</span>
|
||||
<span class="pr-meta-item" v-if="details?.anonymizeDate">
|
||||
<i class="fas fa-user-secret"></i> Anonymized <span v-text="fmt?.date(details?.anonymizeDate)"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymized <span v-text="fmt?.date(details?.anonymizeDate)"></span>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="paper-tabs" role="tablist" v-if="(details?.files && details?.files?.length) || (details?.comments && details?.comments?.length)">
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.files" :class="{'active': tabState?.active == 'files'}" @click="tabState.active = 'files'">
|
||||
<i class="fas fa-file-code"></i>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.files" :class="{'active': tabState?.active == 'files'}" id="gist-files-tab" :aria-selected="tabState?.active === 'files'" aria-controls="gist-files-panel" :tabindex="tabState?.active === 'files' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="tabState.active = 'files'">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-type-gist"></use></svg>
|
||||
<span v-text="fmt.plural(details?.files?.length, {'0': 'No files', 'one': '1 file', 'other': '{} files'})"></span>
|
||||
</button>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.comments" :class="{'active': tabState?.active == 'comments'}" @click="tabState.active = 'comments'">
|
||||
<i class="far fa-comment-dots"></i>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.comments" :class="{'active': tabState?.active == 'comments'}" id="gist-comments-tab" :aria-selected="tabState?.active === 'comments'" aria-controls="gist-comments-panel" :tabindex="tabState?.active === 'comments' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="tabState.active = 'comments'">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-comment"></use></svg>
|
||||
<span v-text="fmt.plural(details?.comments?.length, {'0': 'No comments', 'one': '1 comment', 'other': '{} comments'})"></span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="paper-tab-content">
|
||||
<div v-if="details?.files && tabState?.active =='files'">
|
||||
<div role="tabpanel" id="gist-files-panel" aria-labelledby="gist-files-tab" v-if="details?.files && tabState?.active =='files'">
|
||||
<ul class="pr-comments">
|
||||
<li class="pr-comment" v-for="(file, index) in details?.files">
|
||||
<div class="pr-comment-head">
|
||||
@@ -49,18 +51,18 @@
|
||||
<gist-file :file="file"></gist-file>
|
||||
</li>
|
||||
<li class="paper-table-empty" v-if="!details?.files?.length">
|
||||
<i class="fas fa-file"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-file"></use></svg>
|
||||
<span>No files in this gist.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="details?.comments && tabState?.active =='comments'">
|
||||
<div role="tabpanel" id="gist-comments-panel" aria-labelledby="gist-comments-tab" v-if="details?.comments && tabState?.active =='comments'">
|
||||
<ul class="pr-comments">
|
||||
<li class="pr-comment" v-for="(comment, index) in details?.comments">
|
||||
<div class="pr-comment-head">
|
||||
<span class="pr-comment-author" v-if="comment?.author">
|
||||
<i class="far fa-user"></i> @<span v-text="comment?.author"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user"></use></svg> @<span v-text="comment?.author"></span>
|
||||
</span>
|
||||
<span class="pr-comment-date" v-if="comment?.updatedDate" v-text="fmt?.date(comment?.updatedDate)"></span>
|
||||
</div>
|
||||
@@ -69,7 +71,7 @@
|
||||
</div>
|
||||
</li>
|
||||
<li class="paper-table-empty" v-if="!details?.comments?.length">
|
||||
<i class="far fa-comment-dots"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-comment"></use></svg>
|
||||
<span>No comments on this gist.</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
+16
-12
@@ -13,28 +13,28 @@
|
||||
</li>
|
||||
<li class="nav-item" v-if="user">
|
||||
<a class="nav-link" href="/dashboard" :class="{'active': path == '/dashboard'}">
|
||||
<i class="fas fa-th-large d-lg-none mr-1"></i>
|
||||
<svg class="ui-icon d-lg-none mr-1" aria-hidden="true"><use href="#ui-icon-grid"></use></svg>
|
||||
My work
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" v-if="user">
|
||||
<a class="nav-link" href="/anonymize" :class="{'active':path == '/anonymize' || path == '/pull-request-anonymize'}">
|
||||
<i class="fas fa-user-secret d-lg-none mr-1"></i>
|
||||
<svg class="ui-icon d-lg-none mr-1" aria-hidden="true"><use href="#ui-icon-shield"></use></svg>
|
||||
Anonymize
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" v-if="user">
|
||||
<a class="nav-link" href="/conferences" :class="{'active':path == '/conferences'}">
|
||||
<i class="fas fa-chalkboard-teacher d-lg-none mr-1"></i>
|
||||
<svg class="ui-icon d-lg-none mr-1" aria-hidden="true"><use href="#project-icon-conference"></use></svg>
|
||||
Conferences
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/faq" :class="{'active':path == '/faq'}"><i class="fas fa-question-circle d-lg-none mr-1"></i>FAQ</a>
|
||||
<a class="nav-link" href="/faq" :class="{'active':path == '/faq'}"><svg class="ui-icon d-lg-none mr-1" aria-hidden="true"><use href="#ui-icon-help"></use></svg>FAQ</a>
|
||||
</li>
|
||||
<li class="nav-item" v-if="user && user?.isAdmin">
|
||||
<a class="nav-link" href="/admin/" :class="{'active':path?.indexOf('/admin') === 0}">
|
||||
<i class="fas fa-cog d-lg-none mr-1"></i>
|
||||
<svg class="ui-icon d-lg-none mr-1" aria-hidden="true"><use href="#project-icon-filter"></use></svg>
|
||||
Admin
|
||||
</a>
|
||||
</li>
|
||||
@@ -47,19 +47,19 @@
|
||||
anonymized repository often think it is about the paper, so the
|
||||
label names the service and the tooltip says who to contact. -->
|
||||
<li class="nav-item d-none d-lg-block">
|
||||
<a class="nav-link" target="_blank" rel="noopener" href="https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml" title="Report a problem with the Anonymous GitHub service. Questions about an anonymized repository or paper go to its authors." data-offset="30"><i class="fas fa-bug d-lg-none mr-1" aria-hidden="true"></i>Report a bug
|
||||
<a class="nav-link" target="_blank" rel="noopener" href="https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml" title="Report a problem with the Anonymous GitHub service. Questions about an anonymized repository or paper go to its authors." data-offset="30"><svg class="ui-icon d-lg-none mr-1" aria-hidden="true"><use href="#ui-icon-bug"></use></svg>Report a bug
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" v-if="!isDarkMode">
|
||||
<a class="nav-link nav-icon" href="#" title="Switch to dark mode" aria-label="Switch to dark mode" role="button" @click.prevent="darkMode(true);">
|
||||
<i class="fas fa-moon" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-moon"></use></svg>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" v-if="isDarkMode">
|
||||
<a class="nav-link nav-icon" href="#" title="Switch to light mode" aria-label="Switch to light mode" role="button" @click.prevent="darkMode(false);">
|
||||
<i class="fas fa-sun" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-sun"></use></svg>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<div class="dropdown-menu dropdown-menu-right user-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||
<h6 class="dropdown-header">Signed in as @{{ user?.username }}</h6>
|
||||
<a class="dropdown-item" href="/profile">
|
||||
<i class="fas fa-sliders-h" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-filter"></use></svg>
|
||||
<span>Settings<small>Defaults, quotas, account</small></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="/connections">
|
||||
@@ -84,21 +84,25 @@
|
||||
<span>GitHub connections<small>Repository access and OAuth</small></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="/claim">
|
||||
<i class="fas fa-link" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-link"></use></svg>
|
||||
<span>Claim an anonymization<small>Attach an existing mirror to your account</small></span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml" target="_blank" rel="noopener">
|
||||
<i class="fas fa-bug" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-bug"></use></svg>
|
||||
<span>Report a bug<small>About this service, not a paper</small></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="https://github.com/tdurieux/anonymous_github/" target="_blank" rel="noopener">
|
||||
<i class="fab fa-github" aria-hidden="true"></i>
|
||||
<span>Source code</span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="https://ko-fi.com/tdurieux" target="_blank" rel="noopener">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-heart"></use></svg>
|
||||
<span>Support the project</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="/api/user/logout" target="_self">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-logout"></use></svg>
|
||||
<span>Sign out</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
+14
-14
@@ -9,8 +9,8 @@
|
||||
</h1>
|
||||
<p class="hero-subtitle">
|
||||
Anonymous GitHub makes a read-only mirror of your repository, pull
|
||||
request, or gist with every trace of identity removed, so you can
|
||||
attach a link to your submission without breaking anonymity.
|
||||
request, or gist. It redacts the terms you choose in text files,
|
||||
so reviewers can browse your artifact through an anonymous link.
|
||||
</p>
|
||||
|
||||
<div class="paper-cta-row">
|
||||
@@ -34,7 +34,7 @@
|
||||
<span class="dots"><i></i><i></i><i></i></span>
|
||||
<span class="url">anonymous.4open.science/anonymize</span>
|
||||
</div>
|
||||
<img src="/imgs/anonymize.png" width="2880" height="1800" alt="The anonymize form with a live README preview showing the redactions">
|
||||
<img src="/imgs/redaction-preview.png" width="1360" height="960" alt="A reviewer preview with the author, affiliation, and email replaced by anonymous terms">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -117,9 +117,9 @@
|
||||
<h2>Anonymize, review, <em>manage.</em></h2>
|
||||
</div>
|
||||
<p>
|
||||
Every mirror is read-only and passes through the same filter, from the
|
||||
README to a PDF's link targets. Monitor it, update it, or remove it the
|
||||
day the decision is out.
|
||||
Terms are redacted in text files. Enabled images and PDFs are served
|
||||
unchanged, so review them for identifying content before sharing.
|
||||
Monitor your mirror, update it, or remove it when the review is over.
|
||||
</p>
|
||||
</section>
|
||||
<section class="paper-features paper-wrap" aria-label="Product features">
|
||||
@@ -160,19 +160,19 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="q">
|
||||
<a href="/faq#howWork">Why does GitHub ask for write access at sign-in?</a>
|
||||
<a href="/faq#permissions">What access does GitHub sign-in require?</a>
|
||||
<p>
|
||||
GitHub's OAuth scopes have no read-only option for private
|
||||
repositories. Anonymous GitHub only ever reads your repositories; it
|
||||
never pushes, modifies, or deletes anything.
|
||||
The GitHub App has read-only access to the repositories you choose.
|
||||
Legacy OAuth uses broader permissions and is still needed for gists.
|
||||
Anonymous GitHub never writes to your repositories.
|
||||
</p>
|
||||
</div>
|
||||
<div class="q">
|
||||
<a href="/faq#formats">Which file formats are supported?</a>
|
||||
<p>
|
||||
Source code with syntax highlighting, Markdown, Jupyter notebooks,
|
||||
PDFs, images, and GitHub Pages sites. Everything passes through the
|
||||
same redaction filter.
|
||||
PDFs, images, and GitHub Pages sites. Text files are anonymized;
|
||||
enabled images and PDFs are served unchanged.
|
||||
</p>
|
||||
</div>
|
||||
<div class="q">
|
||||
@@ -191,7 +191,7 @@
|
||||
<div class="paper-footer-mark">Anonymous <em>GitHub</em></div>
|
||||
<p class="paper-footer-tag">
|
||||
A read-only, identity-stripped mirror of your repository, built for
|
||||
double-blind peer review.
|
||||
double-anonymous peer review.
|
||||
</p>
|
||||
</div>
|
||||
<div class="paper-footer-col">
|
||||
@@ -215,7 +215,7 @@
|
||||
</div>
|
||||
<div class="paper-footer-rule"></div>
|
||||
<div class="paper-footer-foot">
|
||||
<span>© {{ year || 2026 }} Anonymous GitHub · MIT licensed</span>
|
||||
<span>© {{ year || 2026 }} Anonymous GitHub · GPL-3.0 licensed</span>
|
||||
<span class="paper-footer-meta">Built for reviewers, by researchers.</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<svg class="ui-icon-definitions" aria-hidden="true" focusable="false">
|
||||
<defs>
|
||||
<symbol id="project-type-repo" viewBox="0 0 24 24">
|
||||
<path d="M6 3h13v18H6a3 3 0 0 1 0-6h13M3 18V6a3 3 0 0 1 3-3M8 7h6"></path>
|
||||
</symbol>
|
||||
<symbol id="project-type-pr" viewBox="0 0 24 24">
|
||||
<circle cx="6" cy="5" r="2.5"></circle><circle cx="6" cy="19" r="2.5"></circle><circle cx="18" cy="19" r="2.5"></circle>
|
||||
<path d="M6 7.5v9M18 16.5V9a4 4 0 0 0-4-4h-3m3-3-3 3 3 3"></path>
|
||||
</symbol>
|
||||
<symbol id="project-type-gist" viewBox="0 0 24 24">
|
||||
<path d="M14 3H5v18h14V8l-5-5v5h5M9 12l-2 2 2 2m6-4 2 2-2 2"></path>
|
||||
</symbol>
|
||||
<symbol id="project-icon-copy" viewBox="0 0 24 24"><rect x="8" y="8" width="12" height="13" rx="2"></rect><path d="M15 8V3H3v13h5"></path></symbol>
|
||||
<symbol id="project-icon-more" viewBox="0 0 24 24"><circle cx="5" cy="12" r="1"></circle><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle></symbol>
|
||||
<symbol id="project-icon-eye" viewBox="0 0 24 24"><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12Z"></path><circle cx="12" cy="12" r="3"></circle></symbol>
|
||||
<symbol id="project-icon-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"></circle><path d="M12 7v5l3 2"></path></symbol>
|
||||
<symbol id="project-icon-conference" viewBox="0 0 24 24"><path d="m3 8 9-5 9 5H3Zm2 3v7m5-7v7m4-7v7m5-7v7M3 21h18"></path></symbol>
|
||||
<symbol id="project-icon-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"></path></symbol>
|
||||
<symbol id="project-icon-filter" viewBox="0 0 24 24"><path d="M3 6h18M3 12h18M3 18h18"></path><circle cx="8" cy="6" r="2"></circle><circle cx="16" cy="12" r="2"></circle><circle cx="9" cy="18" r="2"></circle></symbol>
|
||||
<symbol id="project-icon-arrow" viewBox="0 0 24 24"><path d="M12 4v16m-5-5 5 5 5-5"></path></symbol>
|
||||
<symbol id="project-icon-close" viewBox="0 0 24 24"><path d="m6 6 12 12M6 18 18 6"></path></symbol>
|
||||
<symbol id="project-icon-chevron" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"></path></symbol>
|
||||
<symbol id="project-icon-storage" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="7" rx="2"></rect><rect x="3" y="13" width="18" height="7" rx="2"></rect><path d="M7 7.5h.01M7 16.5h.01M11 7.5h6M11 16.5h6"></path></symbol>
|
||||
<symbol id="project-icon-file" viewBox="0 0 24 24"><path d="M14 3H5v18h14V8l-5-5v5h5M9 12h6M9 16h6"></path></symbol>
|
||||
<symbol id="project-icon-inbox" viewBox="0 0 24 24"><path d="m3 14 3-9h12l3 9v6H3v-6h5l2 3h4l2-3h5"></path></symbol>
|
||||
<symbol id="ui-icon-arrow-left" viewBox="0 0 24 24"><path d="M20 12H4m6-6-6 6 6 6"/></symbol>
|
||||
<symbol id="ui-icon-arrow-right" viewBox="0 0 24 24"><path d="M4 12h16m-6-6 6 6-6 6"/></symbol>
|
||||
<symbol id="ui-icon-check" viewBox="0 0 24 24"><path d="m5 12 4 4L19 6"/></symbol>
|
||||
<symbol id="ui-icon-check-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="m7 12 3 3 7-7"/></symbol>
|
||||
<symbol id="ui-icon-edit" viewBox="0 0 24 24"><path d="m15 4 5 5M4 20l5-1L21 7l-5-5L4 14v6Z"/></symbol>
|
||||
<symbol id="ui-icon-external-link" viewBox="0 0 24 24"><path d="M14 3h7v7m0-7L10 14M10 3H3v18h18v-7"/></symbol>
|
||||
<symbol id="ui-icon-key" viewBox="0 0 24 24"><circle cx="8" cy="8" r="5"/><path d="m12 12 9 9m-6-6 3-3m0 6 3-3"/></symbol>
|
||||
<symbol id="ui-icon-refresh" viewBox="0 0 24 24"><path d="M20 8a8 8 0 0 0-14-3L3 8m0-5v5h5M4 16a8 8 0 0 0 14 3l3-3m0 5v-5h-5"/></symbol>
|
||||
<symbol id="ui-icon-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 11v6M12 7h.01"/></symbol>
|
||||
<symbol id="ui-icon-warning" viewBox="0 0 24 24"><path d="m12 3 10 18H2L12 3Zm0 6v5m0 3h.01"/></symbol>
|
||||
<symbol id="ui-icon-help" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9 9a3 3 0 0 1 6 0c0 2-3 2-3 4m0 4h.01"/></symbol>
|
||||
<symbol id="ui-icon-user" viewBox="0 0 24 24"><circle cx="12" cy="7" r="4"/><path d="M4 21v-3a8 8 0 0 1 16 0v3"/></symbol>
|
||||
<symbol id="ui-icon-users" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M2 21v-3a7 7 0 0 1 14 0v3M16 3a4 4 0 0 1 0 8m3 3a6 6 0 0 1 3 5v2"/></symbol>
|
||||
<symbol id="ui-icon-shield" viewBox="0 0 24 24"><path d="m12 3 9 4v6c0 4-9 9-9 9S3 17 3 13V7l9-4Z"/></symbol>
|
||||
<symbol id="ui-icon-code" viewBox="0 0 24 24"><path d="m7 6-6 6 6 6m10-12 6 6-6 6m-4-14-2 16"/></symbol>
|
||||
<symbol id="ui-icon-comment" viewBox="0 0 24 24"><path d="M21 3H3v14h5l4 4v-4h9V3Z"/><path d="M7 8h10M7 12h6"/></symbol>
|
||||
<symbol id="ui-icon-save" viewBox="0 0 24 24"><path d="M3 3h14l4 4v14H3V3Zm4 0v7h10V3M7 21v-7h10v7"/></symbol>
|
||||
<symbol id="ui-icon-heart" viewBox="0 0 24 24"><path d="M12 21 3 12C-2 6 7-1 12 6c5-7 14 0 9 6l-9 9Z"/></symbol>
|
||||
<symbol id="ui-icon-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><ellipse cx="12" cy="12" rx="4" ry="9"/><path d="M3 12h18"/></symbol>
|
||||
<symbol id="ui-icon-hourglass" viewBox="0 0 24 24"><path d="M5 3h14M5 21h14M6 3v4l6 5 6-5V3M6 21v-4l6-5 6 5v4"/></symbol>
|
||||
<symbol id="ui-icon-moon" viewBox="0 0 24 24"><path d="M20 14A9 9 0 0 1 10 3a9 9 0 1 0 10 11Z"/></symbol>
|
||||
<symbol id="ui-icon-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 1v2m0 18v2M1 12h2m18 0h2M4 4l2 2m12 12 2 2M4 20l2-2M18 6l2-2"/></symbol>
|
||||
<symbol id="ui-icon-grid" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></symbol>
|
||||
<symbol id="ui-icon-logout" viewBox="0 0 24 24"><path d="M9 3H3v18h6M9 12h12m-4-4 4 4-4 4"/></symbol>
|
||||
<symbol id="ui-icon-link" viewBox="0 0 24 24"><path d="m10 7 3-3a5 5 0 0 1 7 7l-3 3M7 10l-3 3a5 5 0 0 0 7 7l3-3M8 16l8-8"/></symbol>
|
||||
<symbol id="ui-icon-chart" viewBox="0 0 24 24"><path d="M3 3v18h18M7 16V9m5 7V5m5 11v-5"/></symbol>
|
||||
<symbol id="ui-icon-list" viewBox="0 0 24 24"><path d="M9 5h12M9 12h12M9 19h12m-18-14 1 1 2-2m-3 8 1 1 2-2m-3 8 1 1 2-2"/></symbol>
|
||||
<symbol id="ui-icon-bug" viewBox="0 0 24 24"><rect x="7" y="7" width="10" height="14" rx="5"/><path d="M9 7V5a3 3 0 0 1 6 0v2M12 7v14M3 8l4 2m-4 5h4m-4 7 4-4M21 8l-4 2m4 5h-4m4 7-4-4"/></symbol>
|
||||
<symbol id="ui-icon-chevron-left" viewBox="0 0 24 24"><path d="m15 5-7 7 7 7"/></symbol>
|
||||
<symbol id="ui-icon-chevron-right" viewBox="0 0 24 24"><path d="m9 5 7 7-7 7"/></symbol>
|
||||
<symbol id="ui-icon-download" viewBox="0 0 24 24"><path d="M12 3v12m-5-5 5 5 5-5M3 16v5h18v-5"/></symbol>
|
||||
<symbol id="ui-icon-export" viewBox="0 0 24 24"><path d="M13 3H3v18h14v-6M12 9h9m-4-4 4 4-4 4"/></symbol>
|
||||
<symbol id="ui-icon-clipboard" viewBox="0 0 24 24"><path d="M8 5H4v16h16V5h-4"/><rect x="8" y="2" width="8" height="6" rx="2"/></symbol>
|
||||
<symbol id="ui-icon-ban" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="m6 6 12 12"/></symbol>
|
||||
<symbol id="ui-icon-trash" viewBox="0 0 24 24"><path d="M3 6h18M9 6V3h6v3M5 6l1 15h12l1-15M10 10v7m4-7v7"/></symbol>
|
||||
<symbol id="ui-icon-broom" viewBox="0 0 24 24"><path d="m16 3-5 8m-7 5 6-6 6 4-3 7H3l1-5Zm1 5 4-6"/></symbol>
|
||||
<symbol id="ui-icon-database" viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 4 18 4 18 0V5M3 12c0 4 18 4 18 0"/></symbol>
|
||||
<symbol id="ui-icon-user-minus" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M2 21v-3a7 7 0 0 1 14 0v3m0-11h7"/></symbol>
|
||||
<symbol id="ui-icon-user-shield" viewBox="0 0 24 24"><circle cx="8" cy="6" r="4"/><path d="M1 21v-4a7 7 0 0 1 10-6m7 0 5 2v4c0 3-5 6-5 6s-5-3-5-6v-4l5-2Z"/></symbol>
|
||||
<symbol id="ui-icon-spinner" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-9-9"/></symbol>
|
||||
<symbol id="ui-icon-archive" viewBox="0 0 24 24"><path d="M3 3h18v5H3V3Zm2 5v13h14V8M10 3v3m4 3v3m-4 3v3m4 0v3"/></symbol>
|
||||
<symbol id="ui-icon-calendar" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M7 2v6m10-6v6M3 11h18"/></symbol>
|
||||
<symbol id="ui-icon-search" viewBox="0 0 24 24"><circle cx="10" cy="10" r="7"></circle><path d="m15 15 7 7"></path></symbol>
|
||||
<symbol id="ui-icon-folder" viewBox="0 0 24 24"><path d="M3 20V4h6l2 3h10v13H3Z"/></symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -37,7 +37,7 @@
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="url">Website</label>
|
||||
<input type="url" class="form-control" name="url" id="url" placeholder="https://example.org" v-field="{ state: viewState, set: value => { options.url = value }, value: options?.url, form: "conference", options: {} }" :class="{'is-invalid': conference?.url?.invalid && (conference?.url?.touched || conference?.submitted)}">
|
||||
<div class="invalid-feedback" v-show="conference?.url?.errors?.invalid">
|
||||
<div class="invalid-feedback" v-show="conference?.url?.errors?.url">
|
||||
The url of the conference is invalid.
|
||||
</div>
|
||||
</div>
|
||||
@@ -189,8 +189,8 @@
|
||||
<div class="paper-settings-footer">
|
||||
<div class="alert alert-danger" role="alert" v-if="error" v-text="error"></div>
|
||||
<div class="alert alert-success" role="alert" v-if="message" v-text="message"></div>
|
||||
<button id="send" type="submit" class="btn btn-ink" @click.prevent="submitForm($event, () => { submit($event) })">
|
||||
{{ editionMode ? "Update" : "Create" }} conference
|
||||
<button id="send" :disabled="saving" type="submit" class="btn btn-ink" @click.prevent="submitForm($event, () => { submit($event) })">
|
||||
{{ saving ? (editionMode ? "Updating…" : "Creating…") : (editionMode ? "Update conference" : "Create conference") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="file-error container d-flex h-100" v-if="type == 'rate_limited'">
|
||||
<div class="paper-ratelimit-card m-auto" style="max-width:520px;">
|
||||
<div class="paper-ratelimit-head">
|
||||
<i class="fas fa-hourglass-half"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-hourglass"></use></svg>
|
||||
<div>
|
||||
<div class="paper-error-eyebrow">Temporarily paused</div>
|
||||
<div class="paper-error-title">GitHub API rate limit reached</div>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
</div>
|
||||
<div class="d-flex flex-wrap" style="gap: 6px">
|
||||
<a href="/anonymize" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> Anonymize Repo
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-plus"></use></svg> Anonymize Repo
|
||||
</a>
|
||||
<a href="/pull-request-anonymize" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> Anonymize PR
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-plus"></use></svg> Anonymize PR
|
||||
</a>
|
||||
<a title="Claim the ownership of an existing anonymized repository." data-toggle="tooltip" data-placement="bottom" href="/claim" class="btn">
|
||||
Claim
|
||||
@@ -113,19 +113,19 @@
|
||||
</div>
|
||||
<div class="repo-meta">
|
||||
<span title="Conference" v-if="pr?.conference">
|
||||
<i class="fas fa-chalkboard-teacher"></i> {{ pr?.conference }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ pr?.conference }}
|
||||
</span>
|
||||
<span data-toggle="tooltip" data-placement="bottom" :title=""Terms: " + (pr?.options?.terms?.join(', '))">
|
||||
<i class="fas fa-shield-alt"></i> {{ fmt?.number(pr?.options?.terms?.length) }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> {{ fmt?.number(pr?.options?.terms?.length) }}
|
||||
</span>
|
||||
<span data-toggle="tooltip" data-placement="bottom" :title=""Views: " + (fmt?.number(pr?.pageView))">
|
||||
<i class="far fa-eye"></i> {{ fmt?.number(pr?.pageView) }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> {{ fmt?.number(pr?.pageView) }}
|
||||
</span>
|
||||
<span data-toggle="tooltip" data-placement="bottom" :title=""Last view: " + (fmt?.date(pr?.lastView))">
|
||||
<i class="far fa-calendar-alt"></i> {{ fmt?.humanTime(pr?.lastView) }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-calendar"></use></svg> {{ fmt?.humanTime(pr?.lastView) }}
|
||||
</span>
|
||||
<span v-if="pr?.options?.expirationMode!='never' && pr?.status == 'ready'">
|
||||
<i class="far fa-clock"></i> Expire: {{ fmt?.humanTime(pr?.options?.expirationDate) }}
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg> Expire: {{ fmt?.humanTime(pr?.options?.expirationDate) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,26 +136,26 @@
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" :href="safeUrl("/pull-request-anonymize/" + (pr?.pullRequestId))">
|
||||
<i class="far fa-edit" aria-hidden="true"></i> Edit
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" v-show="pr?.status == 'ready' || pr?.status == 'error'" @click.prevent="updatePullRequest(pr)">
|
||||
<i class="fas fa-sync"></i> Force update
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-refresh"></use></svg> Force update
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" v-show="pr?.status == 'removed'" @click.prevent="updatePullRequest(pr)">
|
||||
<i class="fas fa-check-circle"></i> Enable
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check-circle"></use></svg> Enable
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" v-show="pr?.status == 'ready'" @click.prevent="removePullRequest(pr)">
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Remove
|
||||
</a>
|
||||
<a class="dropdown-item" :href="safeUrl("/pr/" + (pr?.pullRequestId) + "/")">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i> View PR
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> View PR
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="repo-list-empty" v-if="filteredPullRequests?.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-inbox"></use></svg>
|
||||
<span>No pull requests to display.</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -17,32 +17,11 @@
|
||||
</aside>
|
||||
|
||||
<div class="paper-settings-main">
|
||||
<!-- Quota: same markup as the dashboard, driven by quotaService -->
|
||||
<!-- Account usage shares the dashboard component. -->
|
||||
<section id="settings-quota" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Quota</div>
|
||||
<p class="paper-section-copy">What you are using across all your anonymizations. Conferences can lift these limits during a review window.</p>
|
||||
<div class="quota-row" v-if="quota">
|
||||
<div class="quota-item" v-for="(q, index) in [
|
||||
{key: 'repository', label: 'Repositories', kind: 'count'},
|
||||
{key: 'storage', label: 'Storage', kind: 'bytes'},
|
||||
{key: 'file', label: 'Files', kind: 'count'}
|
||||
]">
|
||||
<div class="quota-header">
|
||||
<span class="quota-label">{{ q?.label }}</span>
|
||||
<span class="quota-value" v-if="q?.kind === 'count'">
|
||||
{{ fmt?.number(quota[q?.key].used) }}<span v-if="!quota[q?.key].unlimited"> / {{ fmt?.number(quota[q?.key].total) }}</span>
|
||||
<span class="quota-unlimited-tag" v-if="quota[q?.key].unlimited">Unlimited</span>
|
||||
</span>
|
||||
<span class="quota-value" v-if="q?.kind === 'bytes'">
|
||||
{{ fmt?.humanFileSize(quota[q?.key].used) }}<span v-if="!quota[q?.key].unlimited"> / {{ fmt?.humanFileSize(quota[q?.key].total) }}</span>
|
||||
<span class="quota-unlimited-tag" v-if="quota[q?.key].unlimited">Unlimited</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="quota-track" role="progressbar" aria-valuemin="0" :class="'quota-' + quota[q?.key].level" :aria-label="(q?.label) + " quota"" :aria-valuenow="(quota[q?.key].used)" :aria-valuemax="(quota[q?.key].unlimited ? quota[q?.key].used : quota[q?.key].total)" :aria-valuetext="(quota[q?.key].unlimited ? 'unlimited' : fmt.number(quota[q?.key].percent, 0) + '% used')">
|
||||
<div class="quota-fill" v-if="!quota[q?.key].unlimited" :style="{width: quota[q?.key].percent + '%'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<quota-usage :quota="quota" :fmt="fmt" v-if="quota"></quota-usage>
|
||||
<div class="quota-row" aria-hidden="true" v-if="!quota">
|
||||
<div class="quota-item" v-for="(i, index) in [1, 2, 3]">
|
||||
<div class="quota-header"><span class="skeleton skeleton-line" style="width: 40%"></span><span class="skeleton skeleton-line" style="width: 25%"></span></div>
|
||||
@@ -143,11 +122,13 @@ jane.smith@example.org" v-field="{ state: viewState, set: value => { terms =
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="draft-status" role="status" v-if="draftRestored">Your unsaved draft was restored. <button type="button" class="draft-discard" @click="discardDraft()">Discard draft</button></div>
|
||||
<div class="draft-status" v-else-if="unsavedChanges">{{ draftSaved ? "Draft saved in this tab for 30 minutes." : "Unsaved changes. Keep this tab open until you save." }} <button type="button" class="draft-discard" @click="discardDraft()">{{ isUpdate ? "Restore saved settings" : "Discard changes" }}</button></div>
|
||||
<div class="paper-settings-footer">
|
||||
<div class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="error"><i class="fas fa-exclamation-circle" aria-hidden="true"></i> <span v-text="error"></span></div>
|
||||
<div class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="error"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> <span v-text="error"></span></div>
|
||||
<div class="paper-settings-actions">
|
||||
<button id="save" type="submit" class="btn btn-ink" :disabled="saving">
|
||||
<i class="fas fa-check mr-1" aria-hidden="true" v-if="message"></i>{{ saving ? 'Saving…' : (message ? 'Saved' : 'Save defaults') }}
|
||||
<svg class="ui-icon mr-1" aria-hidden="true" v-if="message"><use href="#ui-icon-check"></use></svg>{{ saving ? 'Saving…' : (message ? 'Saved' : 'Save defaults') }}
|
||||
</button>
|
||||
<span class="paper-settings-hint">Applies to new anonymizations only. Existing ones keep their settings.</span>
|
||||
</div>
|
||||
@@ -178,9 +159,9 @@ jane.smith@example.org" v-field="{ state: viewState, set: value => { terms =
|
||||
revokes the application's access to your GitHub account, and erases
|
||||
your personal data (username, email, access tokens). This cannot be undone.
|
||||
</p>
|
||||
<div class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="deleteError"><i class="fas fa-exclamation-circle" aria-hidden="true"></i> <span v-text="deleteError"></span></div>
|
||||
<div class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="deleteError"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> <span v-text="deleteError"></span></div>
|
||||
<button type="button" class="btn btn-outline-danger" @click="deleteAccount()" :disabled="deletingAccount">
|
||||
<i class="fas fa-trash-alt mr-1" aria-hidden="true"></i>{{ deletingAccount ? 'Deleting…' : 'Delete my account' }}
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-trash"></use></svg>{{ deletingAccount ? 'Deleting…' : 'Delete my account' }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
</div>
|
||||
|
||||
<header class="pr-header">
|
||||
<div class="pr-title-row">
|
||||
<h1 class="paper-page-title pr-title">
|
||||
<span v-if="details?.title" v-text="details?.title"></span>
|
||||
<span class="text-muted" v-if="!details?.title">Untitled pull request</span>
|
||||
<a class="btn btn-sm" aria-label="Edit" v-if="options?.isAdmin || options?.isOwner" :href="safeUrl("/pull-request-anonymize/" + (pullRequestId))"><i class="far fa-edit"></i><span class="d-none d-md-inline"> Edit</span></a>
|
||||
</h1>
|
||||
<a class="btn btn-sm" aria-label="Edit" v-if="options?.isAdmin || options?.isOwner" :href="safeUrl("/pull-request-anonymize/" + (pullRequestId))"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-edit"></use></svg><span class="d-none d-md-inline"> Edit</span></a>
|
||||
</div>
|
||||
<div class="pr-header-meta">
|
||||
<span class="paper-pill" :class="{'good': details?.merged, 'warn': details?.state == 'open', 'bad': details?.state == 'closed' && !details?.merged}">
|
||||
<span class="status-dot" :class="{'status-ready': details?.merged, 'status-error': details?.state == 'closed' && !details?.merged}"></span>
|
||||
@@ -20,10 +22,10 @@
|
||||
<i class="fab fa-github"></i> <span v-text="details?.baseRepositoryFullName"></span>
|
||||
</span>
|
||||
<span class="pr-meta-item" v-if="details?.updatedDate">
|
||||
<i class="far fa-clock"></i> <span v-text="fmt?.date(details?.updatedDate)"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg> <span v-text="fmt?.date(details?.updatedDate)"></span>
|
||||
</span>
|
||||
<span class="pr-meta-item" v-if="details?.anonymizeDate">
|
||||
<i class="fas fa-user-secret"></i> Anonymized <span v-text="fmt?.date(details?.anonymizeDate)"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymized <span v-text="fmt?.date(details?.anonymizeDate)"></span>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
@@ -34,26 +36,26 @@
|
||||
</section>
|
||||
|
||||
<nav class="paper-tabs" role="tablist" v-if="details?.diff || details?.comments">
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.diff" :class="{'active': tabState?.active == 'diff'}" @click="tabState.active = 'diff'">
|
||||
<i class="fas fa-code"></i> Diff
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.diff" :class="{'active': tabState?.active == 'diff'}" id="pr-diff-tab" :aria-selected="tabState?.active === 'diff'" aria-controls="pr-diff-panel" :tabindex="tabState?.active === 'diff' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="tabState.active = 'diff'">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-code"></use></svg> Diff
|
||||
</button>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.comments" :class="{'active': tabState?.active == 'comments'}" @click="tabState.active = 'comments'">
|
||||
<i class="far fa-comment-dots"></i>
|
||||
<button class="paper-tab" type="button" role="tab" v-if="details?.comments" :class="{'active': tabState?.active == 'comments'}" id="pr-comments-tab" :aria-selected="tabState?.active === 'comments'" aria-controls="pr-comments-panel" :tabindex="tabState?.active === 'comments' ? 0 : -1" @keydown="previewTabKeydown($event)" @click="tabState.active = 'comments'">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-comment"></use></svg>
|
||||
<span v-text="fmt.plural(details?.comments?.length, {'0': 'No comments', 'one': '1 comment', 'other': '{} comments'})"></span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="paper-tab-content">
|
||||
<div v-if="details?.diff && tabState?.active =='diff'">
|
||||
<div role="tabpanel" id="pr-diff-panel" aria-labelledby="pr-diff-tab" v-if="details?.diff && tabState?.active =='diff'">
|
||||
<div class="pr-diff" v-html="sanitize(fmt?.diff(details?.diff))"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="details?.comments && tabState?.active =='comments'">
|
||||
<div role="tabpanel" id="pr-comments-panel" aria-labelledby="pr-comments-tab" v-if="details?.comments && tabState?.active =='comments'">
|
||||
<ul class="pr-comments">
|
||||
<li class="pr-comment" v-for="(comment, index) in details?.comments">
|
||||
<div class="pr-comment-head">
|
||||
<span class="pr-comment-author" v-if="comment?.author">
|
||||
<i class="far fa-user"></i> @<span v-text="comment?.author"></span>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-user"></use></svg> @<span v-text="comment?.author"></span>
|
||||
</span>
|
||||
<span class="pr-comment-date" v-if="comment?.updatedDate" v-text="fmt?.date(comment?.updatedDate)"></span>
|
||||
</div>
|
||||
@@ -62,7 +64,7 @@
|
||||
</div>
|
||||
</li>
|
||||
<li class="paper-table-empty" v-if="!details?.comments?.length">
|
||||
<i class="far fa-comment-dots"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-comment"></use></svg>
|
||||
<span>No comments on this pull request.</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="quota-row quota-usage" v-if="quota">
|
||||
<div class="quota-item" :class="'quota-' + quota[q.key].level" :key="q.key" v-for="q in [
|
||||
{key: 'repository', label: 'Repositories', kind: 'count', icon: 'project-type-repo'},
|
||||
{key: 'storage', label: 'Storage', kind: 'bytes', icon: 'project-icon-storage'},
|
||||
{key: 'file', label: 'Files', kind: 'count', icon: 'project-icon-file'}
|
||||
]">
|
||||
<div class="quota-header">
|
||||
<span class="quota-label"><svg class="project-type-svg" aria-hidden="true"><use :href="'#' + q.icon"></use></svg>{{ q.label }}</span>
|
||||
<span class="quota-percent" v-if="!quota[q.key].unlimited">{{ fmt.number(quota[q.key].percent, 0) }}% used</span>
|
||||
<span class="quota-unlimited-tag" v-if="quota[q.key].unlimited">Unlimited</span>
|
||||
</div>
|
||||
<div class="quota-value"><strong>{{ formatAmount(quota[q.key].used, q.kind) }}</strong><span v-if="!quota[q.key].unlimited"> / {{ formatAmount(quota[q.key].total, q.kind) }}</span></div>
|
||||
<div class="quota-track" v-if="!quota[q.key].unlimited" role="progressbar" aria-valuemin="0" :class="'quota-' + quota[q.key].level" :aria-label="q.label + ' quota'" :aria-valuenow="Math.min(quota[q.key].used, quota[q.key].total)" :aria-valuemax="quota[q.key].total" :aria-valuetext="fmt.number(quota[q.key].percent, 0) + '% used'">
|
||||
<div class="quota-fill" v-if="!quota[q?.key].unlimited" :style="{width: quota[q.key].percent + '%'}"></div>
|
||||
</div>
|
||||
<p class="quota-remaining" v-if="!quota[q.key].unlimited && quota[q.key].used < quota[q.key].total">{{ formatAmount(quota[q.key].total - quota[q.key].used, q.kind) }} {{ q.key === 'repository' ? 'repositories available' : q.key === 'file' ? 'files available' : 'available' }}</p>
|
||||
<p class="quota-remaining quota-limit" v-if="!quota[q.key].unlimited && quota[q.key].used >= quota[q.key].total">{{ quota[q.key].used > quota[q.key].total ? 'Limit exceeded' : 'Limit reached' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="container paper-page auth-page">
|
||||
<a class="auth-back" href="/"><i class="fas fa-arrow-left" aria-hidden="true"></i> Anonymous GitHub</a>
|
||||
<a class="auth-back" href="/"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-arrow-left"></use></svg> Anonymous GitHub</a>
|
||||
<div class="auth-layout">
|
||||
<header class="auth-intro">
|
||||
<div class="paper-eyebrow">Your research, ready for review</div>
|
||||
@@ -18,16 +18,16 @@
|
||||
<h2>Continue with GitHub</h2>
|
||||
<p>Connect through the GitHub App with read-only repository access.</p>
|
||||
<ul class="auth-permissions">
|
||||
<li><i class="fas fa-check" aria-hidden="true"></i> You choose which repositories to share</li>
|
||||
<li><i class="fas fa-check" aria-hidden="true"></i> No permission to write to your code</li>
|
||||
<li><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check"></use></svg> You choose which repositories to share</li>
|
||||
<li><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-check"></use></svg> No permission to write to your code</li>
|
||||
</ul>
|
||||
<a class="btn btn-ink auth-continue" target="_self" href="/github/app/login">Sign in with GitHub <i class="fas fa-arrow-right" aria-hidden="true"></i></a>
|
||||
<a class="btn btn-ink auth-continue" target="_self" href="/github/app/login">Sign in with GitHub <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-arrow-right"></use></svg></a>
|
||||
<small>Your existing anonymizations and connections stay with your account.</small>
|
||||
</section>
|
||||
<section class="auth-legacy" v-if="!site_options?.GITHUB_APP_ENABLED && site_options?.GITHUB_OAUTH_ENABLED && !accountRecovery">
|
||||
<div class="connection-title"><h2>Continue with GitHub</h2></div>
|
||||
<p>Sign in to access your account. GitHub OAuth includes write access to private repositories.</p>
|
||||
<a class="connection-link" target="_self" href="/github/login">Sign in with GitHub <i class="fas fa-arrow-right" aria-hidden="true"></i></a>
|
||||
<a class="connection-link" target="_self" href="/github/login">Sign in with GitHub <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-arrow-right"></use></svg></a>
|
||||
</section>
|
||||
<p role="status" v-if="site_options && !site_options.GITHUB_APP_ENABLED && !site_options.GITHUB_OAUTH_ENABLED">GitHub sign-in is currently unavailable. Please try again later.</p>
|
||||
</div>
|
||||
|
||||
+14
-14
@@ -1,4 +1,4 @@
|
||||
<div class="container paper-page">
|
||||
<div class="container paper-page status-page">
|
||||
<div class="paper-crumbs">Anonymization / <span class="here">Status</span></div>
|
||||
<div class="d-flex align-items-end justify-content-between flex-wrap" style="gap: 12px;">
|
||||
<div>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<div class="paper-ratelimit-card" role="status" v-if="rateLimitResetAt">
|
||||
<div class="paper-ratelimit-head">
|
||||
<i class="fas fa-hourglass-half"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-hourglass"></use></svg>
|
||||
<div>
|
||||
<div class="paper-error-eyebrow">Temporarily paused</div>
|
||||
<div class="paper-error-title">GitHub API rate limit reached</div>
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<div class="paper-error-card" role="alert" v-if="repo?.status == 'error' && !rateLimitResetAt">
|
||||
<div class="paper-error-head">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-warning"></use></svg>
|
||||
<div>
|
||||
<div class="paper-error-eyebrow">Anonymization failed</div>
|
||||
<div class="paper-error-title">Something went wrong while preparing this repository.</div>
|
||||
@@ -55,9 +55,9 @@
|
||||
<li>If you just signed in, the access token may need a moment to propagate — try again.</li>
|
||||
</ul>
|
||||
<div class="paper-error-actions">
|
||||
<a class="btn btn-ink" :href="safeUrl("/anonymize/" + (repoId))"><i class="far fa-edit mr-1"></i> Edit anonymization</a>
|
||||
<a class="btn" href="/faq"><i class="far fa-question-circle mr-1"></i> Read the FAQ</a>
|
||||
<a class="btn" href="https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml" target="_blank" rel="noopener"><i class="fas fa-bug mr-1" aria-hidden="true"></i> Report a bug in Anonymous GitHub</a>
|
||||
<a class="btn btn-ink" :href="safeUrl("/anonymize/" + (repoId))"><svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-edit"></use></svg> Edit anonymization</a>
|
||||
<a class="btn" href="/faq"><svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-help"></use></svg> Read the FAQ</a>
|
||||
<a class="btn" href="https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml" target="_blank" rel="noopener"><svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-bug"></use></svg> Report a bug in Anonymous GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -74,10 +74,10 @@
|
||||
|
||||
<div class="anonymize-submit-bar" v-if="repo?.status == 'ready'">
|
||||
<a class="btn btn-ink" target="_self" :href="safeUrl("/r/" + (repoId) + "/")">
|
||||
<i class="far fa-eye mr-1"></i> Go to anonymized repository
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-eye"></use></svg> Go to anonymized repository
|
||||
</a>
|
||||
<a class="btn" target="_self" v-if="repo?.options?.page" :href="safeUrl("/w/" + (repoId) + "/")">
|
||||
<i class="fas fa-globe mr-1"></i> Go to anonymized GitHub page
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-globe"></use></svg> Go to anonymized GitHub page
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
@@ -91,19 +91,19 @@
|
||||
|
||||
<div class="paper-support-grid">
|
||||
<a class="paper-support-card" href="https://github.com/tdurieux/anonymous_github/" target="_blank" rel="noopener">
|
||||
<div class="paper-support-eyebrow"><i class="fas fa-code-branch"></i> Contribute</div>
|
||||
<div class="paper-support-eyebrow"><svg class="ui-icon" aria-hidden="true"><use href="#project-type-repo"></use></svg> Contribute</div>
|
||||
<p>Collaborate by implementing new features and fixing bugs. Help with new file formats or deployment is welcome.</p>
|
||||
<span class="paper-support-cta">Open on GitHub <i class="fas fa-arrow-right"></i></span>
|
||||
<span class="paper-support-cta">Open on GitHub <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-arrow-right"></use></svg></span>
|
||||
</a>
|
||||
<a class="paper-support-card" href="https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml" target="_blank" rel="noopener">
|
||||
<div class="paper-support-eyebrow"><i class="far fa-comment-dots"></i> Feedback</div>
|
||||
<div class="paper-support-eyebrow"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-comment"></use></svg> Feedback</div>
|
||||
<p>Tell us about bugs and missing features in Anonymous GitHub itself. Questions about an anonymized repository go to its authors.</p>
|
||||
<span class="paper-support-cta">File an issue <i class="fas fa-arrow-right"></i></span>
|
||||
<span class="paper-support-cta">File an issue <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-arrow-right"></use></svg></span>
|
||||
</a>
|
||||
<a class="paper-support-card" href="https://github.com/sponsors/tdurieux/" target="_blank" rel="noopener">
|
||||
<div class="paper-support-eyebrow"><i class="fas fa-heart"></i> Sponsor</div>
|
||||
<div class="paper-support-eyebrow"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-heart"></use></svg> Sponsor</div>
|
||||
<p>Server costs hover around $25 a month. Any contribution helps keep the lights on.</p>
|
||||
<span class="paper-support-cta">GitHub Sponsors <i class="fas fa-arrow-right"></i></span>
|
||||
<span class="paper-support-cta">GitHub Sponsors <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-arrow-right"></use></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user