Polish website UX: unify dashboards, clean up layout, modernize styling (#668)

This commit is contained in:
Thomas Durieux
2026-04-15 09:30:19 +02:00
committed by GitHub
parent 8198a4b44a
commit 261eaa8d79
9 changed files with 1378 additions and 1925 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+357 -31
View File
@@ -193,18 +193,97 @@ a:hover {
}
.rgba-gradient {
background: -moz-linear-gradient(45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%);
background: -webkit-linear-gradient(45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%);
background: linear-gradient(to 45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%);
background: linear-gradient(135deg, var(--primary-bg), #2a2d4a);
color: var(--header-color);
}
/* Hero section */
.hero-title {
font-size: 2.8rem;
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 0;
}
.hero-subtitle {
font-size: 1.15rem;
opacity: 0.85;
max-width: 500px;
margin-left: auto;
margin-right: auto;
line-height: 1.5;
}
.btn-hero {
border: 2px solid rgba(255, 255, 255, 0.8);
color: #fff;
padding: 10px 28px;
font-size: 1rem;
font-weight: 500;
border-radius: 6px;
transition: background 0.2s, border-color 0.2s;
}
.btn-hero:hover {
background: rgba(255, 255, 255, 0.12);
border-color: #fff;
color: #fff;
}
/* Home content */
.home-content {
max-width: 900px;
}
.usage-steps {
font-size: 1.05rem;
line-height: 1.8;
padding-left: 1.2em;
}
.usage-example {
font-size: 0.9rem;
opacity: 0.7;
margin-top: 8px;
}
.featurette-lead {
font-size: 1.05rem;
line-height: 1.7;
opacity: 0.85;
}
/* Metrics grid */
.metrics-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 48px;
}
.metric-card {
text-align: center;
padding: 24px 16px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--admin-stat-bg);
}
.metric-value {
font-size: 2rem;
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.02em;
}
.metric-label {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.5px;
opacity: 0.6;
margin-top: 4px;
}
.dropdown-menu,
.body,
.file-content,
@@ -220,7 +299,11 @@ a:hover {
}
.badge {
font-size: 45%;
font-size: 75%;
font-weight: 500;
padding: 3px 8px;
border-radius: 10px;
vertical-align: middle;
}
.warning-feedback {
@@ -549,11 +632,13 @@ loc .lang {
/* Space out the Bootstrap <hr> more */
}
/* Thin out the marketing headings */
/* Featurette headings */
.featurette-heading {
font-weight: 300;
line-height: 1;
letter-spacing: -0.05rem;
font-weight: 600;
font-size: 1.5rem;
line-height: 1.3;
letter-spacing: -0.02em;
margin-bottom: 12px;
}
.dark-mode .shadow {
@@ -577,15 +662,7 @@ loc .lang {
}
.dark-mode .rgba-gradient {
background: -moz-linear-gradient(45deg,
rgb(45 45 64 / 82%),
rgb(23 26 49) 100%);
background: -webkit-linear-gradient(45deg,
rgb(45 45 64 / 82%),
rgb(23 26 49) 100%);
background: linear-gradient(to 45deg,
rgb(45 45 64 / 82%),
rgb(23 26 49) 100%);
background: linear-gradient(135deg, #2a2d4a, #151728);
}
@@ -626,6 +703,236 @@ code {
background: rgba(200, 100, 100, 0.5);
}
/* ===== Dashboard & List Styles ===== */
.dashboard-page {
max-width: 960px;
}
.dashboard-title {
font-size: 1.4rem;
font-weight: 600;
letter-spacing: -0.01em;
}
/* Quota bars */
.quota-row {
display: flex;
gap: 16px;
margin-bottom: 16px;
padding: 12px 16px;
background: var(--admin-stat-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
}
.quota-item {
flex: 1;
min-width: 0;
}
.quota-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 4px;
}
.quota-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
opacity: 0.7;
}
.quota-value {
font-size: 0.8rem;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
.quota-progress {
height: 6px;
border-radius: 3px;
background: var(--hover-bg-color);
}
.quota-progress .progress-bar {
border-radius: 3px;
}
/* Filter chips */
.filter-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 10px;
font-size: 0.8rem;
font-weight: 500;
background: var(--primary-bg);
color: var(--primary-color);
border-radius: 12px;
line-height: 1.4;
}
.filter-chip-close {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: inherit;
opacity: 0.7;
cursor: pointer;
padding: 0;
margin-left: 2px;
font-size: 1rem;
line-height: 1;
}
.filter-chip-close:hover {
opacity: 1;
}
/* Repository/PR list */
.repo-list {
list-style: none;
padding: 0;
margin: 0;
}
.repo-list-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 16px 0;
border-bottom: 1px solid var(--border-color);
gap: 12px;
}
.repo-list-item:first-child {
border-top: 1px solid var(--border-color);
}
.repo-list-item.repo-inactive {
opacity: 0.6;
}
.repo-list-item-content {
flex: 1;
min-width: 0;
}
.repo-list-item-header {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 4px;
}
.repo-name {
font-size: 1.1rem;
font-weight: 600;
color: var(--link-color);
word-break: break-word;
}
.repo-name:hover {
color: var(--link-hover-color);
}
.repo-source {
font-size: 0.85rem;
opacity: 0.8;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
}
.repo-date {
opacity: 0.7;
}
.repo-meta {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 8px;
font-size: 0.8rem;
opacity: 0.7;
}
.repo-meta span {
display: inline-flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.repo-list-item-actions {
flex-shrink: 0;
padding-top: 2px;
}
.repo-list-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 48px 20px;
opacity: 0.5;
font-size: 0.95rem;
list-style: none;
border-bottom: 1px solid var(--border-color);
border-top: 1px solid var(--border-color);
}
.repo-list-empty i {
font-size: 1.8rem;
}
/* Type badges (Repo / PR) */
.type-badge {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
padding: 2px 8px;
border-radius: 4px;
white-space: nowrap;
}
.type-badge.type-repo {
background: rgba(74, 80, 123, 0.12);
color: var(--primary-bg);
}
.type-badge.type-pr {
background: rgba(40, 167, 69, 0.12);
color: #28a745;
}
.dark-mode .type-badge.type-repo {
background: rgba(84, 155, 245, 0.15);
color: #549bf5;
}
.dark-mode .type-badge.type-pr {
background: rgba(40, 167, 69, 0.15);
color: #5cb85c;
}
/* Type filter button group */
.btn-group .btn:not(.btn-primary) {
background: var(--hover-bg-color);
}
/* ===== Anonymize Page Layout ===== */
.anonymize-page .sidePanel {
@@ -730,12 +1037,19 @@ code {
h3 { font-size: 1.15rem; }
/* Dashboard list items */
.col-12.d-flex.px-0.py-3 {
.repo-list-item {
flex-direction: column;
}
.col-12.d-flex.px-0.py-3 > .d-flex:last-child {
margin-top: 8px;
.repo-list-item-actions {
align-self: flex-start;
}
/* Quota row stacks on mobile */
.quota-row {
flex-direction: column;
gap: 10px;
padding: 10px 12px;
}
/* Dropdown menus right-aligned on mobile */
@@ -788,8 +1102,7 @@ code {
/* Badge readability */
.badge {
font-size: 55%;
padding: 3px 6px;
font-size: 70%;
}
/* Search & filter toolbar */
@@ -810,9 +1123,22 @@ code {
display: none !important;
}
/* Metrics cards on home */
.col-md-4 h3 {
font-size: 1.1rem;
/* Metrics cards on mobile */
.metrics-grid {
grid-template-columns: 1fr;
gap: 10px;
}
.metric-value {
font-size: 1.5rem;
}
.hero-title {
font-size: 1.8rem;
}
.hero-subtitle {
font-size: 0.95rem;
}
/* ---- Anonymize page mobile ---- */
+220 -347
View File
@@ -1,382 +1,217 @@
<div class="container-fluid h-100 anonymize-page">
<div class="row h-100 flex-column flex-md-row">
<div
class="col-md sidePanel shadow overflow-auto anonymize-form-col"
>
<div
class="p-0 py-2 m-auto"
ng-class="{'card': !repoUrl,'container': repoUrl}"
>
<form
class="form needs-validation"
ng-class="{'card-body': !repoUrl}"
name="anonymize"
novalidate
>
<h3 class="card-title mb-3">Anonymize your repository</h3>
<!-- repoUrl -->
<div class="form-group mb-0">
<div class="col-md sidePanel shadow overflow-auto anonymize-form-col">
<div class="p-0 py-2 m-auto" ng-class="{'card': !sourceUrl, 'container': sourceUrl}">
<form class="form needs-validation" ng-class="{'card-body': !sourceUrl}" name="anonymize" novalidate>
<h3 class="card-title mb-3">Anonymize</h3>
<!-- Source URL (auto-detects repo vs PR) -->
<div class="form-group mb-2">
<input
type="text"
class="form-control"
name="repoUrl"
id="repoUrl"
ng-class="{'is-invalid': anonymize.repoUrl.$invalid}"
ng-model="repoUrl"
placeholder="URL of your GitHub repository"
name="sourceUrl"
id="sourceUrl"
ng-class="{'is-invalid': anonymize.sourceUrl.$invalid}"
ng-model="sourceUrl"
placeholder="Paste a GitHub repo or pull request URL"
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
ng-change="repoSelected()"
ng-change="urlSelected()"
/>
<div
class="invalid-feedback"
ng-show="anonymize.repoUrl.$error.github"
>
Please provide a valid Github url, e.g.,
https://github.com/owner/repo.
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.github">
Please provide a valid GitHub URL, e.g., https://github.com/owner/repo or https://github.com/owner/repo/pull/123
</div>
<div
class="invalid-feedback"
ng-show="anonymize.repoUrl.$error.access"
>
{{repoUrl}} is not accessible. Some organizations are restricting
the access to the repositories.
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.access">
Not accessible. The organization may restrict access.
</div>
<div
class="invalid-feedback"
ng-show="anonymize.repoUrl.$error.missing"
>
{{repoUrl}} does not exist or is not accessible
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.missing">
Does not exist or is not accessible.
</div>
<div
class="invalid-feedback"
ng-show="anonymize.repoUrl.$error.used"
>
{{repoUrl}} is already anonymized
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.used">
Already anonymized.
</div>
<small class="form-text text-muted" ng-hide="sourceUrl">
Paste a repository URL to anonymize a repo, or a pull request URL to anonymize a PR.
</small>
</div>
<div ng-show="repoUrl">
<!-- Branch -->
<div class="form-group">
<label for="branch">Branch</label>
<div class="input-group mb-1">
<select
class="form-control"
id="branch"
name="branch"
ng-model="source.branch"
>
<option
ng-repeat="b in branches"
ng-bind="b.name"
value="{{b.name}}"
></option>
</select>
<div class="input-group-append">
<button
class="btn btn-outline-secondary"
ng-click="getBranches(true)"
title="Refresh!"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="fa fa-undo"></i>
</button>
<!-- Type indicator -->
<div class="mb-3" ng-show="detectedType && sourceUrl">
<span class="type-badge" ng-class="{'type-repo': detectedType === 'repo', 'type-pr': detectedType === 'pr'}">
<i ng-class="{'fas fa-code-branch': detectedType === 'repo', 'fas fa-code-merge': detectedType === 'pr'}"></i>
{{detectedType === 'repo' ? 'Repository' : 'Pull Request'}}
</span>
</div>
<div ng-show="sourceUrl">
<!-- ==== REPO-SPECIFIC FIELDS ==== -->
<div ng-show="detectedType === 'repo'">
<div class="form-group">
<label for="branch">Branch</label>
<div class="input-group mb-1">
<select class="form-control" id="branch" name="branch" ng-model="source.branch">
<option ng-repeat="b in branches" ng-bind="b.name" value="{{b.name}}"></option>
</select>
<div class="input-group-append">
<button class="btn btn-outline-secondary" ng-click="getBranches(true)" title="Refresh" data-toggle="tooltip" data-placement="bottom">
<i class="fa fa-undo"></i>
</button>
</div>
</div>
<small class="form-text text-muted">The branch to anonymize</small>
</div>
<small class="form-text text-muted">
The branch to anonymize
</small>
</div>
<!-- Auto update (both types) -->
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="update"
name="update"
ng-model="options.update"
/>
<input class="form-check-input" type="checkbox" id="update" name="update" ng-model="options.update" />
<label class="form-check-label" for="update">Auto update</label>
<small id="updateHelp" class="form-text text-muted"
>Automatically update the anonymized repository with the
latest commit of the repository. The repository is updated
once per hour maximum.</small
>
<small class="form-text text-muted">Automatically update with the latest changes.</small>
</div>
</div>
<!-- Commit -->
<div class="form-group">
<!-- Commit (repo only) -->
<div class="form-group" ng-show="detectedType === 'repo'">
<label for="commit">Commit</label>
<input
class="form-control"
id="commit"
name="commit"
pattern="[a-fA-Z0-9]{6,}"
ng-model="source.commit"
required
ng-class="{'is-invalid': anonymize.commit.$invalid}"
/>
<small class="form-text text-muted"
>The SHA of the commit to anonymize.</small
>
<div
class="invalid-feedback"
ng-show="anonymize.commit.$error.pattern || anonymize.commit.$error.required"
>
The commit SHA is not valid. It should respect this pattern
[a-fA-Z0-9]{6,}.
<input class="form-control" id="commit" name="commit" pattern="[a-fA-Z0-9]{6,}" ng-model="source.commit" required ng-class="{'is-invalid': anonymize.commit.$invalid}" />
<small class="form-text text-muted">The SHA of the commit to anonymize.</small>
<div class="invalid-feedback" ng-show="anonymize.commit.$error.pattern || anonymize.commit.$error.required">
The commit SHA is not valid.
</div>
</div>
<h5 class="anonymize-section-title">
<i class="fas fa-chalkboard-teacher"></i> Conference ID
</h5>
<!-- Conference -->
<div class="form-group">
<label for="conference"
>Conference ID <span class="text-muted">(Optional)</span></label
>
<input
class="form-control"
id="conference"
name="conference"
ng-model="conference"
ng-class="{'is-invalid': anonymize.conference.$invalid}"
/>
<small class="form-text text-muted" ng-show="conference_data"
><a ng-href="{{conference_data.url}}" target="_target"
>{{conference_data.name}}</a
>
will expire on {{conference_data.endDate | date}}.</small
>
<div
class="invalid-feedback"
ng-show="anonymize.conference.$error.activated"
>
The conference is not activated.
</div>
<small class="form-text text-muted" ng-show="!conference_data">
Use the Conference ID that your conference provided you. This
will update automatically the anonymization options based on the
conference preferences.
<label for="conference">Conference ID <span class="text-muted">(Optional)</span></label>
<input class="form-control" id="conference" name="conference" ng-model="conference" ng-class="{'is-invalid': anonymize.conference.$invalid}" />
<small class="form-text text-muted" ng-show="conference_data">
<a ng-href="{{conference_data.url}}" target="_blank">{{conference_data.name}}</a> will expire on {{conference_data.endDate | date}}.
</small>
<div class="invalid-feedback" ng-show="anonymize.conference.$error.activated">The conference is not activated.</div>
<small class="form-text text-muted" ng-show="!conference_data">Conference ID updates anonymization options automatically.</small>
</div>
<h5 class="anonymize-section-title">
<i class="fas fa-shield-alt"></i> Anonymization Options
</h5>
<!-- Repo ID -->
<div class="form-group">
<label for="repoId">Anonymized repository id</label>
<input
type="text"
class="form-control"
name="repoId"
id="repoId"
ng-class="{'is-invalid': anonymize.repoId.$invalid}"
ng-model="repoId"
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
/>
<small id="idHelp" class="form-text text-muted"
>Id used in the url:
https://anonymous.4open.science/r/{{repoId}}</small
>
<div
class="invalid-feedback"
ng-show="anonymize.repoId.$error.format"
>
Repository id can only contain letters and numbers
</div>
<div
class="invalid-feedback"
ng-show="anonymize.repoId.$error.used"
>
{{repoId}} is already used
</div>
<!-- Anonymized ID: repo -->
<div class="form-group" ng-show="detectedType === 'repo'">
<label for="repoId">Anonymized repository ID</label>
<input type="text" class="form-control" name="repoId" id="repoId" ng-class="{'is-invalid': anonymize.repoId.$invalid}" ng-model="repoId" ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }" />
<small class="form-text text-muted">URL: https://anonymous.4open.science/r/{{repoId}}</small>
<div class="invalid-feedback" ng-show="anonymize.repoId.$error.format">ID can only contain letters and numbers.</div>
<div class="invalid-feedback" ng-show="anonymize.repoId.$error.used">{{repoId}} is already used.</div>
</div>
<!-- Anonymized ID: PR -->
<div class="form-group" ng-show="detectedType === 'pr'">
<label for="pullRequestId">Anonymized pull request ID</label>
<input type="text" class="form-control" name="pullRequestId" id="pullRequestId" ng-class="{'is-invalid': anonymize.pullRequestId.$invalid}" ng-model="pullRequestId" ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }" />
<small class="form-text text-muted">URL: https://anonymous.4open.science/pr/{{pullRequestId}}</small>
<div class="invalid-feedback" ng-show="anonymize.pullRequestId.$error.format">ID can only contain letters and numbers.</div>
<div class="invalid-feedback" ng-show="anonymize.pullRequestId.$error.used">{{pullRequestId}} is already used.</div>
</div>
<!-- Terms -->
<div class="form-group">
<label for="terms">Terms to anonymize</label>
<textarea
class="form-control"
id="terms"
name="terms"
rows="3"
ng-model="terms"
ng-model-options="{ debounce: 250 }"
ng-class="{'is-invalid': anonymize.terms.$invalid}"
></textarea>
<small id="termsHelp" class="form-text text-muted">
One term per line. A term is a RegEx! Each term will be replaced
by {{site_options.ANONYMIZATION_MASK}}-[Line Number].
</small>
<div
class="warning-feedback"
ng-show="anonymize.terms.$error.regex"
>
We identify that you are using some regex characters, if it was
not on purpose, please escape them.
</div>
<div
class="invalid-feedback"
ng-show="anonymize.terms.$error.format"
>
Terms are in an invalid format.
</div>
<textarea class="form-control" id="terms" name="terms" rows="3" ng-model="terms" ng-model-options="{ debounce: 250 }" ng-class="{'is-invalid': anonymize.terms.$invalid}"></textarea>
<small class="form-text text-muted">One term per line (regex). Each term will be replaced by {{site_options.ANONYMIZATION_MASK}}-[N].</small>
<div class="warning-feedback" ng-show="anonymize.terms.$error.regex">Regex characters detected. Escape them if unintentional.</div>
<div class="invalid-feedback" ng-show="anonymize.terms.$error.format">Terms are in an invalid format.</div>
</div>
<div class="form-group">
<label for="expiration">Expiration strategy</label>
<select
class="form-control"
id="expiration"
name="expiration"
ng-model="options.expirationMode"
>
<select class="form-control" id="expiration" name="expiration" ng-model="options.expirationMode">
<option value="never" selected>Never expire</option>
<option value="redirect">
Redirect to GitHub when expired
</option>
<option value="redirect">Redirect to GitHub when expired</option>
<option value="remove">Remove when expired</option>
</select>
<small class="form-text text-muted"
>Define the expiration strategy for the anonymized
repository.</small
>
</div>
<div
class="form-group"
id="expiration-date-form"
ng-hide="options.expirationMode=='never'"
>
<label for="expirationDate"
>Expiration date of the anonymized repository</label
>
<input
class="form-control"
type="date"
name="expirationDate"
id="expirationDate"
ng-model="options.expirationDate"
/>
<small
class="form-text text-muted"
ng-show="options.expirationMode=='remove'"
>After {{options.expirationDate | date}}, the repository will be
removed and the visitor will not be able to see the content of
the repository.</small
>
<small
class="form-text text-muted"
ng-show="options.expirationMode=='redirect'"
>After {{options.expirationDate | date}}, the visitors of the
anonymized repository will be redirected to {{repoUrl}}.</small
>
<div class="form-group" ng-hide="options.expirationMode=='never'">
<label for="expirationDate">Expiration date</label>
<input class="form-control" type="date" name="expirationDate" id="expirationDate" ng-model="options.expirationDate" />
<small class="form-text text-muted" ng-show="options.expirationMode=='remove'">After {{options.expirationDate | date}}, the content will be removed.</small>
<small class="form-text text-muted" ng-show="options.expirationMode=='redirect'">After {{options.expirationDate | date}}, visitors will be redirected to GitHub.</small>
</div>
<div class="accordion mb-3" id="options">
<!-- Advanced options -->
<div class="accordion mb-3" id="advancedOptions">
<div class="card">
<div class="card-header" id="headingOne">
<div class="card-header" id="headingAdvanced">
<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"
>
<button class="btn btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseAdvanced" aria-expanded="true" aria-controls="collapseAdvanced">
<i class="fas fa-cog mr-1"></i> Advanced options
</button>
</h2>
</div>
<div
id="collapseOne"
class="collapse show"
aria-labelledby="headingOne"
data-parent="#options"
>
<div id="collapseAdvanced" class="collapse show" aria-labelledby="headingAdvanced" data-parent="#advancedOptions">
<div class="card-body">
<div class="form-group">
<!-- Shared options -->
<div class="form-group mb-0">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="link"
name="link"
ng-model="options.link"
/>
<label class="form-check-label" for="link"
>Keep links</label
>
<small id="linkHelp" class="form-text text-muted"
>Keep or remove all the links.</small
>
<input class="form-check-input" type="checkbox" id="link" name="link" ng-model="options.link" />
<label class="form-check-label" for="link">Keep links</label>
<small class="form-text text-muted">Keep or remove all the links.</small>
</div>
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="image"
name="image"
ng-model="options.image"
/>
<label class="form-check-label" for="image"
>Display images</label
>
<small id="imageHelp" class="form-text text-muted"
>Images are not anonymized</small
>
<input class="form-check-input" type="checkbox" id="image" name="image" ng-model="options.image" />
<label class="form-check-label" for="image">Display images</label>
<small class="form-text text-muted">Images are not anonymized.</small>
</div>
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="pdf"
name="pdf"
ng-model="options.pdf"
/>
<label class="form-check-label" for="pdf"
>Display PDFs</label
>
<small id="pdfHelp" class="form-text text-muted"
>PDF are not anonymized</small
>
<!-- Repo-specific options -->
<div ng-show="detectedType === 'repo'">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="pdf" name="pdf" ng-model="options.pdf" />
<label class="form-check-label" for="pdf">Display PDFs</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" ng-model="options.notebook" />
<label class="form-check-label" for="notebook">Display Notebooks</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="page" name="page" ng-model="options.page" ng-disabled="!details.hasPage" />
<label class="form-check-label" for="page">GitHub Pages</label>
<small class="form-text text-muted">Enable anonymized GitHub Pages at https://anonymous.4open.science/w/{{repoId}}</small>
</div>
</div>
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="notebook"
name="notebook"
ng-model="options.notebook"
/>
<label class="form-check-label" for="notebook"
>Display Notebooks</label
>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="page"
name="page"
ng-model="options.page"
ng-disabled="!details.hasPage"
/>
<label class="form-check-label" for="page"
>Github page</label
>
<small id="pageHelp" class="form-text text-muted"
>Enable anonymized Github pages. It currently only
supported for Github pages that are defined in the
same branch. It will be available at
https://anonymous.4open.science/w/{{repoId}}</small
>
<!-- PR-specific options -->
<div ng-show="detectedType === 'pr'">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="date" name="date" ng-model="options.date" />
<label class="form-check-label" for="date">Display dates</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="username" name="username" ng-model="options.username" />
<label class="form-check-label" for="username">Display username</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="comments" name="comments" ng-model="options.comments" />
<label class="form-check-label" for="comments">Display comments</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="diff" name="diff" ng-model="options.diff" />
<label class="form-check-label" for="diff">Display diff</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="origin" name="origin" ng-model="options.origin" />
<label class="form-check-label" for="origin">Display project name</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="title" name="title" ng-model="options.title" />
<label class="form-check-label" for="title">Display PR title</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="body" name="body" ng-model="options.body" />
<label class="form-check-label" for="body">Display PR body</label>
</div>
</div>
</div>
</div>
@@ -384,39 +219,77 @@
</div>
</div>
</div>
<div
class="alert alert-danger"
role="alert"
ng-if="error"
ng-bind="error"
></div>
<div class="anonymize-submit-bar" ng-show="repoUrl">
<button
id="submit"
type="submit"
class="btn btn-primary btn-block"
ng-click="anonymizeRepo($event)"
ng-if="!isUpdate"
>
<i class="fas fa-user-secret mr-1"></i> Anonymize
<div class="alert alert-danger" role="alert" ng-if="error" ng-bind="error"></div>
<div class="anonymize-submit-bar" ng-show="sourceUrl && detectedType">
<!-- Repo submit -->
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && !isUpdate">
<i class="fas fa-user-secret mr-1"></i> Anonymize Repository
</button>
<button
id="submit"
type="submit"
class="btn btn-primary btn-block"
ng-click="updateRepo($event)"
ng-if="isUpdate"
>
<i class="fas fa-save mr-1"></i> Update
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && isUpdate">
<i class="fas fa-save mr-1"></i> Update Repository
</button>
<!-- PR submit -->
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && !isUpdate">
<i class="fas fa-user-secret mr-1"></i> Anonymize Pull Request
</button>
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && isUpdate">
<i class="fas fa-save mr-1"></i> Update Pull Request
</button>
</div>
</form>
</div>
</div>
<div
class="col-md-8 p-2 overflow-auto markdown-body body anonymize-preview-col"
ng-bind-html="html_readme"
ng-if="html_readme"
></div>
<!-- Preview: repo README -->
<div class="col-md-8 p-2 overflow-auto markdown-body body anonymize-preview-col" ng-bind-html="html_readme" ng-if="detectedType === 'repo' && html_readme"></div>
<!-- Preview: PR content -->
<div class="col-md-8 p-2 overflow-auto anonymize-preview-col" ng-if="detectedType === 'pr' && details">
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
<h2 class="pr-title mb-1">
<span ng-if="options.title">{{anonymizePrContent(details.pullRequest.title)}}</span>
<span class="badge" ng-class="{'badge-success':details.pullRequest.merged, 'badge-warning':details.pullRequest.state=='open', 'badge-danger':details.pullRequest.state=='closed' && !details.pullRequest.merged}">
{{details.pullRequest.merged ? "merged" : details.pullRequest.state | title}}
</span>
</h2>
<small ng-bind="details.pullRequest.updatedDate | date" ng-if="options.date"></small>
</div>
<small ng-if="options.origin">Pull Request on {{details.pullRequest.baseRepositoryFullName}}</small>
<div class="pr-body shadow-sm p-3 mb-4 rounded" style="background: var(--sidebar-bg-color)" ng-if="options.body">
<markdown content="anonymizePrContent(details.pullRequest.body)" options="options" terms="terms"></markdown>
</div>
<ul class="nav nav-tabs" id="prTabs" role="tablist">
<li class="nav-item" role="presentation" ng-if="options.diff">
<button class="nav-link active" id="pills-diff-tab" data-toggle="pill" data-target="#pills-diff" type="button" role="tab">Diff</button>
</li>
<li class="nav-item" role="presentation" ng-if="options.comments">
<button class="nav-link" ng-class="{'active':!options.diff}" id="pills-comments-tab" data-toggle="pill" data-target="#pills-comments" type="button" role="tab">
<ng-pluralize count="details.pullRequest.comments.length" when="{'0': 'No comment', 'one': 'One Comment', 'other': '{} Comments'}"></ng-pluralize>
</button>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane show active" id="pills-diff" role="tabpanel" ng-if="options.diff">
<div class="pr-diff shadow-sm p-3 mb-4 rounded" style="background: var(--sidebar-bg-color)">
<pre style="overflow-x: auto"><code ng-bind-html="anonymizePrContent(details.pullRequest.diff) | diff"></code></pre>
</div>
</div>
<div class="tab-pane" ng-class="{'show active':!options.diff}" id="pills-comments" role="tabpanel" ng-if="options.comments">
<ul class="pr-comments list-group">
<li class="pr-comment list-group-item" ng-repeat="comment in details.pullRequest.comments">
<div class="d-flex w-100 justify-content-between flex-wrap">
<h5 class="mb-1" ng-if="options.username">@{{anonymizePrContent(comment.author)}}</h5>
<small ng-bind="comment.updatedDate | date" ng-if="options.date"></small>
</div>
<p class="mb-1">
<markdown class="pr-comment-body" ng-if="options.body" content="anonymizePrContent(comment.body)" options="options" terms="terms"></markdown>
</p>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
+219 -352
View File
@@ -1,413 +1,280 @@
<div class="container page">
<div class="container page dashboard-page">
<div class="row">
<span class="border-bottom color-border-secondary py-3 w-100">
<div class="d-flex align-items-center w-100">
<form class="w-100" aria-label="Repositories" accept-charset="UTF-8">
<div class="">
<div class="w-100 mb-2">
<input
type="search"
id="search"
class="form-control"
aria-label="Find a repository…"
placeholder="Find a repository…"
autocomplete="off"
ng-model="search"
/>
<div class="w-100 py-3">
<!-- Header row: title + action button -->
<div class="d-flex align-items-center justify-content-between mb-3">
<h2 class="dashboard-title mb-0">Dashboard</h2>
<a href="/anonymize" class="btn btn-primary btn-sm">
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize
</a>
</div>
<!-- Quota bars -->
<div class="quota-row" ng-show="quota">
<div class="quota-item">
<div class="quota-header">
<span class="quota-label">Repositories</span>
<span class="quota-value" ng-show="quota">{{quota.repository.used | number}}/{{quota.repository.total}}</span>
</div>
<div class="progress quota-progress">
<div
class="progress-bar"
ng-class="{'bg-success': quota.repository.percent < 25 || quota.repository.total == 0, 'bg-danger': quota.repository.percent > 95 && quota.repository.total > 0, 'bg-warning': quota.repository.percent > 75 && quota.repository.total > 0 }"
role="progressbar"
style="width: {{quota.repository.percent}}%;"
aria-valuenow="{{quota.repository.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.repository.total}}"
></div>
</div>
</div>
<div class="quota-item">
<div class="quota-header">
<span class="quota-label">Storage</span>
<span class="quota-value" ng-show="quota">{{quota.storage.used | humanFileSize}}/{{quota.storage.total | humanFileSize}}</span>
</div>
<div class="progress quota-progress">
<div
class="progress-bar"
ng-class="{'bg-success': quota.storage.percent < 25 || quota.storage.total == 0, 'bg-danger': quota.storage.percent > 95 && quota.storage.total > 0, 'bg-warning': quota.storage.percent > 75 && quota.storage.total > 0 }"
role="progressbar"
style="width: {{quota.storage.percent}}%;"
aria-valuenow="{{quota.storage.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.storage.total}}"
></div>
</div>
</div>
<div class="quota-item">
<div class="quota-header">
<span class="quota-label">Files</span>
<span class="quota-value" ng-show="quota">{{quota.file.used | number}}/{{quota.file.total || "&#8734;"}}</span>
</div>
<div class="progress quota-progress">
<div
class="progress-bar"
ng-class="{'bg-success': quota.file.percent < 25 || quota.file.total == 0, 'bg-danger': quota.file.percent > 95 && quota.file.total > 0, 'bg-warning': quota.file.percent > 75 && quota.file.total > 0 }"
role="progressbar"
style="width: {{quota.file.percent}}%;"
aria-valuenow="{{quota.file.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.file.total}}"
></div>
</div>
</div>
</div>
<!-- Search + filters row -->
<form class="w-100" aria-label="Dashboard" accept-charset="UTF-8">
<div class="d-flex flex-column flex-md-row align-items-md-center" style="gap: 8px">
<div class="flex-grow-1">
<input
type="search"
id="search"
class="form-control"
aria-label="Search..."
placeholder="Search..."
autocomplete="off"
ng-model="search"
/>
</div>
<div class="d-flex flex-wrap" style="gap: 6px">
<!-- Type filter -->
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn" ng-class="{'btn-primary': typeFilter === 'all'}" ng-click="typeFilter = 'all'">All</button>
<button type="button" class="btn" ng-class="{'btn-primary': typeFilter === 'repo'}" ng-click="typeFilter = 'repo'">Repos</button>
<button type="button" class="btn" ng-class="{'btn-primary': typeFilter === 'pr'}" ng-click="typeFilter = 'pr'">PRs</button>
</div>
<div class="d-flex flex-wrap">
<div class="dropdown mt-1 mt-lg-0 mr-1">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownSort"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Sort
</button>
<div class="dropdown-menu" aria-labelledby="dropdownSort">
<h6 class="dropdown-header">Select order</h6>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortFullName"
value="fullName"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortFullName">
Repository
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortAnonymizeDate"
value="-anonymizeDate"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortAnonymizeDate">
Anonymize Date
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortStatus"
value="-status"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortStatus">
Status
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortLastView"
value="-lastView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortLastView">
Last View
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortPageView"
value="-pageView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortPageView">
Page View
</label>
</div>
<div class="dropdown">
<button
class="btn btn-sm dropdown-toggle"
type="button"
id="dropdownSort"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Sort
</button>
<div class="dropdown-menu" aria-labelledby="dropdownSort">
<h6 class="dropdown-header">Select order</h6>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortFullName" value="_name" ng-model="orderBy" />
<label class="form-check-label" for="sortFullName">Name</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortAnonymizeDate" value="-anonymizeDate" ng-model="orderBy" />
<label class="form-check-label" for="sortAnonymizeDate">Anonymize Date</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortStatus" value="-status" ng-model="orderBy" />
<label class="form-check-label" for="sortStatus">Status</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortLastView" value="-lastView" ng-model="orderBy" />
<label class="form-check-label" for="sortLastView">Last View</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortPageView" value="-pageView" ng-model="orderBy" />
<label class="form-check-label" for="sortPageView">Page View</label>
</div>
</div>
</div>
<div class="dropdown mt-1 mt-lg-0 mr-1">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownStatus"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Status
</button>
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
<h6 class="dropdown-header">Select status</h6>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusReady"
value="ready"
ng-model="filters.status.ready"
/>
<label class="form-check-label" for="statusReady">
Ready
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusExpired"
value="expired"
ng-model="filters.status.expired"
/>
<label class="form-check-label" for="statusExpired">
Expired
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusRemoved"
value="removed"
ng-model="filters.status.removed"
/>
<label class="form-check-label" for="statusRemoved">
Removed
</label>
</div>
<div class="dropdown">
<button
class="btn btn-sm dropdown-toggle"
type="button"
id="dropdownStatus"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Status
</button>
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
<h6 class="dropdown-header">Select status</h6>
<div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" id="statusReady" value="ready" ng-model="filters.status.ready" />
<label class="form-check-label" for="statusReady">Ready</label>
</div>
</div>
<div class="mt-1 mr-1" style="margin-top: -0.25rem !important">
<strong>Repository</strong>
<div
class="progress position-relative"
style="min-width: 150px"
>
<div
class="progress-bar"
ng-class="{'progress-bar-striped progress-bar-animated w-100 bg-dark': !quota, 'bg-success': quota.repository.percent < 25 || quota.repository.total == 0, 'bg-danger': quota.repository.percent > 95 && quota.repository.total > 0, 'bg-warning': quota.repository.percent > 75 && quota.repository.total > 0 }"
role="progressbar"
style="width: {{quota.repository.percent}}%;"
aria-valuenow="{{quota.repository.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.repository.total}}"
></div>
<span
class="justify-content-center d-flex position-absolute w-100"
ng-show="quota"
>{{quota.repository.used |
number}}/{{quota.repository.total}}</span
>
<div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" id="statusExpired" value="expired" ng-model="filters.status.expired" />
<label class="form-check-label" for="statusExpired">Expired</label>
</div>
</div>
<div class="mt-1 mr-1" style="margin-top: -0.25rem !important">
<strong>Storage</strong>
<div
class="progress position-relative"
style="min-width: 150px"
>
<div
class="progress-bar"
ng-class="{'progress-bar-striped progress-bar-animated w-100 bg-dark': !quota, 'bg-success': quota.storage.percent < 25 || quota.storage.total == 0, 'bg-danger': quota.storage.percent > 95 && quota.storage.total > 0, 'bg-warning': quota.storage.percent > 75 && quota.storage.total > 0 }"
role="progressbar"
style="width: {{quota.storage.percent}}%;"
aria-valuenow="{{quota.storage.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.storage.total}}"
></div>
<span
ng-show="quota"
class="justify-content-center d-flex position-absolute w-100"
>{{quota.storage.used |
humanFileSize}}/{{quota.storage.total| humanFileSize}}</span
>
</div>
</div>
<div class="mt-1 mr-1" style="margin-top: -0.25rem !important">
<strong>File</strong>
<div
class="progress position-relative"
style="min-width: 150px"
>
<div
class="progress-bar"
ng-class="{'progress-bar-striped progress-bar-animated w-100 bg-dark': !quota, 'bg-success': quota.file.percent < 25 || quota.file.total == 0, 'bg-danger': quota.file.percent > 95 && quota.file.total > 0, 'bg-warning': quota.file.percent > 75 && quota.file.total > 0 }"
role="progressbar"
style="width: {{quota.file.percent}}%;"
aria-valuenow="{{quota.file.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.file.total}}"
></div>
<span
class="justify-content-center d-flex position-absolute w-100"
ng-show="quota"
>{{quota.file.used | number}}/{{quota.file.total ||
"∞"}}</span
>
<div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" id="statusRemoved" value="removed" ng-model="filters.status.removed" />
<label class="form-check-label" for="statusRemoved">Removed</label>
</div>
</div>
</div>
</div>
</form>
<div class="d-flex flex-wrap mt-2 mt-md-0" style="gap: 6px">
<a href="/anonymize" class="text-center btn btn-primary btn-sm">
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize Repo
</a>
<a
href="/pull-request-anonymize"
class="text-center btn btn-primary btn-sm"
>
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize PR
</a>
</div>
</div>
<div class="d-flex d-inline-flex mt-2">
<div class="alert alert-info alert-dismissible my-0 ml-1" ng-show="!v" role="alert" ng-repeat="(f, v) in filters.status">
<strong>{{f | title}}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close" ng-click="filters.status[f] = true;">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
</form>
<!-- Active filter chips -->
<div class="d-flex flex-wrap mt-2" style="gap: 6px" ng-show="filters.status.ready === false || filters.status.expired === false || filters.status.removed === false">
<span class="filter-chip" ng-show="!v" ng-repeat="(f, v) in filters.status">
{{f | title}}
<button type="button" class="filter-chip-close" aria-label="Remove filter" ng-click="filters.status[f] = true;">
&times;
</button>
</span>
</div>
</div>
<ul class="p-0 m-0 w-100">
<!-- Unified item list -->
<ul class="repo-list w-100">
<li
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
ng-class="{'expired': repo.status == 'expired','removed': repo.status == 'removed','error': repo.status == 'error' }"
ng-repeat="repo in repositories| filter:repoFiler| orderBy:orderBy as filteredRepositories"
class="repo-list-item"
ng-class="{'repo-inactive': item.status == 'expired' || item.status == 'removed' || item.status == 'error'}"
ng-repeat="item in items | filter:itemFilter | orderBy:orderBy as filteredItems"
>
<div class="w-100">
<div class="">
<h3>
<a ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
<div class="repo-list-item-content">
<div class="repo-list-item-main">
<div class="repo-list-item-header">
<span class="type-badge" ng-class="{'type-repo': item._type === 'repo', 'type-pr': item._type === 'pr'}">
<i ng-class="{'fas fa-code-branch': item._type === 'repo', 'fas fa-code-merge': item._type === 'pr'}"></i>
{{item._type === 'repo' ? 'Repo' : 'PR'}}
</span>
<a ng-href="{{item._viewUrl}}" class="repo-name" ng-bind="item._name"></a>
<span
class="badge"
ng-class="{'badge-warning': repo.status == 'removed' || repo.status == 'expired' || repo.status == 'removing' || repo.status == 'expiring', 'badge-info': repo.status == 'preparing' || repo.status == 'download', 'badge-success': repo.status == 'ready', 'badge-danger': repo.status == 'error'}"
><span ng-bind="repo.status | title"></span>
<span
ng-if="repo.status == 'error'"
ng-bind="': ' + repo.statusMessage"
></span
></span>
</h3>
<span class="color-text-secondary mb-1">
<span class="repository">
class="status-badge"
ng-class="{'status-removed': item.status == 'removed' || item.status == 'expired' || item.status == 'removing' || item.status == 'expiring', 'status-preparing': item.status == 'preparing' || item.status == 'download', 'status-ready': item.status == 'ready', 'status-error': item.status == 'error'}"
><span ng-bind="item.status | title"></span><span
ng-if="item.status == 'error' && item.statusMessage"
ng-bind="': ' + item.statusMessage"
></span></span>
</div>
<div class="repo-source">
<span>
<i class="fab fa-github" aria-hidden="true"></i>
<a
href="https://github.com/{{repo.source.fullName}}/"
class="fullName"
ng-bind="repo.source.fullName"
ng-if="item._type === 'repo'"
href="https://github.com/{{item.source.fullName}}/"
ng-bind="item.source.fullName"
></a>
<a
ng-if="item._type === 'pr'"
href="https://github.com/{{item.source.repositoryFullName}}/pull/{{item.source.pullRequestId}}"
ng-bind="item._source"
></a>
</span>
<span class="branch" ng-if="repo.options.update">
<span ng-if="item._type === 'repo' && item.options.update">
<i class="fas fa-code-branch" aria-hidden="true"></i>
<a
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}"
class="branch"
ng-bind="repo.source.branch"
href="https://github.com/{{item.source.fullName}}/tree/{{item.source.branch}}"
ng-bind="item.source.branch"
></a>
</span>
<span class="commit" ng-if="!repo.options.update">
<span ng-if="item._type === 'repo' && !item.options.update">
@<a
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}"
class="commit"
ng-bind="repo.source.commit.substring(0, 8)"
href="https://github.com/{{item.source.fullName}}/tree/{{item.source.commit}}"
ng-bind="item.source.commit.substring(0, 8)"
></a>
</span>
anonymized {{repo.anonymizeDate | humanTime}}
</span>
<span class="repo-date">anonymized {{item.anonymizeDate | humanTime}}</span>
</div>
</div>
<div class="color-text-secondary mt-2">
<span class="ml-0 mr-3" ng-if="::repo.conference">
<i class="fas fa-chalkboard-teacher"></i>
{{repo.conference}}
<div class="repo-meta">
<span ng-if="item.conference" title="Conference">
<i class="fas fa-chalkboard-teacher"></i> {{item.conference}}
</span>
<span
class="ml-0 mr-3"
class="terms"
title="Terms: {{::repo.options.terms.join(', ')}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="fas fa-shield-alt"></i>
{{::repo.options.terms.length | number}}
<span title="Terms" data-toggle="tooltip" data-placement="bottom">
<i class="fas fa-shield-alt"></i> {{item.options.terms.length | number}}
</span>
<span
class="ml-0 mr-3"
title="Size: {{::repo.size.storage | humanFileSize}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="fas fa-database"></i> {{::repo.size.storage |
humanFileSize}}</span
>
<span
class="ml-0 mr-3"
title="View: {{::repo.pageView | number}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="far fa-eye" aria-hidden="true"></i>
{{::repo.pageView | number}}
<span ng-if="item._type === 'repo' && item.size" title="Size" data-toggle="tooltip" data-placement="bottom">
<i class="fas fa-database"></i> {{item.size.storage | humanFileSize}}
</span>
<span title="Views" data-toggle="tooltip" data-placement="bottom">
<i class="far fa-eye"></i> {{item.pageView | number}}
</span>
<span title="Last view" data-toggle="tooltip" data-placement="bottom">
<i class="far fa-calendar-alt"></i> {{item.lastView | humanTime}}
</span>
<span ng-if="item.options.expirationMode !== 'never' && item.status == 'ready'">
<i class="far fa-clock"></i> Expire: {{item.options.expirationDate | humanTime}}
</span>
<span
class="ml-0 mr-3"
title="Last view: {{::repo.lastView | date}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="far fa-calendar-alt" aria-hidden="true"></i>
Last view: {{::repo.lastView | humanTime}}</span
>
<span
class="ml-0 mr-3"
ng-if="repo.options.expirationMode!='never' && repo.status == 'ready'"
>
<i class="far fa-clock" aria-hidden="true"></i>
Expire: {{repo.options.expirationDate | humanTime}}</span
>
</div>
</div>
<div class="d-flex">
<div class="repo-list-item-actions">
<div class="dropdown">
<button
class="btn black_border dropdown-toggle btn-sm"
class="btn btn-sm dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Actions
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}">
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" ng-href="{{item._editUrl}}">
<i class="far fa-edit" aria-hidden="true"></i> Edit
</a>
<a
class="dropdown-item"
href="#"
ng-show="repo.status == 'ready' || repo.status == 'error'"
ng-click="updateRepository(repo)"
>
<a class="dropdown-item" href="#" ng-show="item.status == 'ready' || item.status == 'error'" ng-click="refreshItem(item)">
<i class="fas fa-sync"></i> Force update
</a>
<a
class="dropdown-item"
href="#"
ng-show="repo.status == 'removed'"
ng-click="updateRepository(repo)"
>
<i class="fas fa-check-circle"></i>
Enable
<a class="dropdown-item" href="#" ng-show="item.status == 'removed'" ng-click="refreshItem(item)">
<i class="fas fa-check-circle"></i> Enable
</a>
<a
class="dropdown-item"
href="#"
ng-show="repo.status == 'ready'"
ng-click="removeRepository(repo)"
>
<a class="dropdown-item" href="#" ng-show="item.status == 'ready'" ng-click="removeItem(item)">
<i class="fas fa-trash-alt"></i> Remove
</a>
<a class="dropdown-item" href="/r/{{repo.repoId}}/">
<i class="fa fa-eye" aria-hidden="true"></i> View Repo
<a class="dropdown-item" ng-href="{{item._viewUrl}}">
<i class="fa fa-eye" aria-hidden="true"></i> View
</a>
<a
class="dropdown-item"
href="/w/{{repo.repoId}}/"
target="_self"
ng-if="repo.options.page && repo.status == 'ready'"
>
<a class="dropdown-item" href="/w/{{item.repoId}}/" target="_self" ng-if="item._type === 'repo' && item.options.page && item.status == 'ready'">
<i class="fas fa-globe"></i> View Page
</a>
</div>
</div>
</div>
</li>
<li
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
ng-if="filteredRepositories.length == 0"
>
There is no repository to display.
<li class="repo-list-empty" ng-if="filteredItems.length == 0">
<i class="fas fa-inbox"></i>
<span>Nothing to display.</span>
</li>
</ul>
</div>
+6 -26
View File
@@ -28,18 +28,18 @@
ng-class="{'active': path == '/dashboard'}"
href="/dashboard"
>
<i class="fas fa-code-branch d-lg-none mr-1"></i>
Repositories
<i class="fas fa-th-large d-lg-none mr-1"></i>
Dashboard
</a>
</li>
<li class="nav-item" ng-if="user">
<a
class="nav-link"
ng-class="{'active': path == '/pr-dashboard'}"
href="/pr-dashboard"
ng-class="{'active':path == '/anonymize' || path == '/pull-request-anonymize'}"
href="/anonymize"
>
<i class="fas fa-code-branch d-lg-none mr-1"></i>
Pull Requests
<i class="fas fa-user-secret d-lg-none mr-1"></i>
Anonymize
</a>
</li>
<li class="nav-item" ng-if="user">
@@ -52,26 +52,6 @@
Conferences
</a>
</li>
<li class="nav-item" ng-if="user">
<a
class="nav-link"
ng-class="{'active':path == '/anonymize'}"
href="/anonymize"
>
<i class="fas fa-user-secret d-lg-none mr-1"></i>
Anonymize
</a>
</li>
<li class="nav-item" ng-if="user">
<a
class="nav-link"
ng-class="{'active':path == '/pull-request-anonymize'}"
href="/pull-request-anonymize"
>
<i class="fas fa-user-secret d-lg-none mr-1"></i>
Anonymize PR
</a>
</li>
<li class="nav-item" ng-if="user && user.isAdmin">
<a
class="nav-link"
+67 -97
View File
@@ -3,77 +3,68 @@
id="home"
class="row view rgba-gradient d-flex align-self-stretch justify-content-center align-items-center"
>
<!-- Content -->
<div class="container px-md-3 px-sm-0">
<!--Grid row-->
<form action="" method="post">
<div class="row fadeIn main-options">
<!--Grid column-->
<div class="col-md-12 mb-4 white-text text-center fadeIn">
<h3 class="display-4 font-weight-bold white-text mb-0 pt-5">
Anonymous GitHub
</h3>
<hr class="hr-light my-4 w-75" />
<h4 class="subtext-header mt-2 mb-4">
Anonymize your repository in 5 min as {{stat.nbUsers | number}}
users already did.
</h4>
<span ng-if="!user">
<a
href="/github/login"
target="_self"
class="btn p-2 white_border"
>Login with GitHub to anonymize</a
>
</span>
</div>
<!--Grid column-->
<div class="row fadeIn main-options">
<div class="col-md-12 mb-4 white-text text-center fadeIn">
<h1 class="hero-title pt-5">
Anonymous GitHub
</h1>
<p class="hero-subtitle mt-3 mb-4">
Anonymize your repository in 5 minutes &mdash; join {{stat.nbUsers | number}} users.
</p>
<span ng-if="!user">
<a
href="/github/login"
target="_self"
class="btn btn-hero"
><i class="fab fa-github mr-2"></i>Login with GitHub</a
>
</span>
<span ng-if="user">
<a
href="/anonymize"
class="btn btn-hero"
><i class="fa fa-plus-circle mr-2"></i>Anonymize a Repository</a
>
</span>
</div>
</form>
<!--Grid row-->
</div>
</div>
<!-- Content -->
</div>
<main>
<div class="container">
<!--Grid row-->
<div class="container home-content">
<div class="row pt-5">
<div class="col-md-12">
<h2 id="usage">Usage</h2>
<div class="card-text mb-auto">
<ol>
<li>
<a href="/github/login" target="_self">Login</a> with Github
access your dashboard and anonymize your repositories.
</li>
<li>Complete the list of terms that will be anonymized.</li>
<li>Anonymize and share your link in your double-anonymized paper.</li>
</ol>
Example of an anonymized repository:
<h2 id="usage">How it works</h2>
<ol class="usage-steps">
<li>
<a href="/github/login" target="_self">Login</a> with GitHub to access your dashboard and anonymize your repositories.
</li>
<li>Configure which terms, links, and images to anonymize.</li>
<li>Share the anonymous link in your double-blind paper submission.</li>
</ol>
<p class="usage-example">
Example:
<a
target="_self"
href="https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/"
>https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/</a
>.
</div>
>
</p>
</div>
</div>
<hr class="featurette-divider" />
<div class="row featurette">
<div class="row featurette align-items-center">
<div class="col-md-7 order-md-2">
<h2 class="featurette-heading">
Double-anonymous
<span class="text-muted">Anonymize your repositories.</span>
</h2>
<p class="lead">
Anonymous Github allows you to simply anonymize your Github
repository. Several anonymization options are available to ensure
that you do not break the double-anonymize such as removing links,
images or specific terms. You still keep control of your repository,
define an expiration date to make your repository unavailable after
the review.
<p class="featurette-lead">
Anonymize your GitHub repository with options to remove links,
images, or specific terms. Keep full control &mdash; set an
expiration date to make your repository unavailable after review.
</p>
</div>
<div class="col-md-5 order-md-1">
@@ -87,17 +78,16 @@
<hr class="featurette-divider" />
<div class="row featurette">
<div class="row featurette align-items-center">
<div class="col-md-7">
<h2 class="featurette-heading">
Explorer <span class="text-muted">Navigate the content.</span>
Explorer
</h2>
<p class="lead">
The reviewers can explore your repository with ease, the source code
is highlighted, PDFs, images, Notebook are rendered. The goal is to
make is as easy as possible for the reviewer to explore and review
the repository. <a href="https://pages.github.com">GitHub Pages</a>
is also supported, but not static site generators, such as Jekyll.
<p class="featurette-lead">
Reviewers can browse your repository with highlighted source code,
rendered PDFs, images, and notebooks.
<a href="https://pages.github.com">GitHub Pages</a>
is also supported.
</p>
</div>
<div class="col-md-5">
@@ -108,19 +98,17 @@
/>
</div>
</div>
<hr class="featurette-divider" />
<div class="row featurette">
<div class="row featurette align-items-center">
<div class="col-md-7 order-md-2">
<h2 class="featurette-heading">
Manage
<span class="text-muted">Keep an eyes on your repositories.</span>
</h2>
<p class="lead">
Keep a view in your anonymized repositories. Edit your anonymization
configuration, remove your repository and update the content of your
repository. You can also monitor the access and the number of views
of your repository.
<p class="featurette-lead">
Monitor views, edit configuration, remove or update your repository &mdash;
all from a clean dashboard.
</p>
</div>
<div class="col-md-5 order-md-1">
@@ -131,42 +119,24 @@
/>
</div>
</div>
<hr class="featurette-divider" />
<h2 id="about">Metrics</h2>
<div class="row pb-5">
<!--Grid column-->
<div class="col-md-4">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">{{stat.nbUsers|number}} Users</h3>
</div>
</div>
<h2 id="about" class="mb-4">Metrics</h2>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-value">{{stat.nbUsers | number}}</div>
<div class="metric-label">Users</div>
</div>
<div class="col-md-4">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">
{{stat.nbRepositories|number}} Repositories
</h3>
</div>
</div>
<div class="metric-card">
<div class="metric-value">{{stat.nbRepositories | number}}</div>
<div class="metric-label">Repositories</div>
</div>
<div class="col-md-4">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">{{stat.nbPageViews|number}} Page views</h3>
</div>
</div>
<div class="metric-card">
<div class="metric-value">{{stat.nbPageViews | number}}</div>
<div class="metric-label">Page Views</div>
</div>
<!--Grid column-->
</div>
<!--Grid row-->
</div>
</main>
</div>
+162 -276
View File
@@ -1,168 +1,14 @@
<div class="container page">
<div class="container page dashboard-page">
<div class="row">
<div class="border-bottom color-border-secondary py-3 w-100">
<div class="d-flex align-items-center w-100">
<form class="w-100" aria-label="Pull Requests" accept-charset="UTF-8">
<div class="d-flex flex-column flex-lg-row flex-auto">
<div class="mb-1 mb-md-0 mr-md-3">
<input
type="search"
id="search"
class="form-control"
aria-label="Find a pull request…"
placeholder="Find a pull request…"
autocomplete="off"
ng-model="search"
/>
</div>
<div class="d-flex flex-wrap">
<div class="dropdown mt-1 mt-lg-0 mr-1">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownSort"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Sort
</button>
<div class="dropdown-menu" aria-labelledby="dropdownSort">
<h6 class="dropdown-header">Select order</h6>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortFullName"
value="fullName"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortFullName">
Pull Request
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortAnonymizeDate"
value="-anonymizeDate"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortAnonymizeDate">
Anonymize Date
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortStatus"
value="-status"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortStatus">
Status
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortLastView"
value="-lastView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortLastView">
Last View
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortPageView"
value="-pageView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortPageView">
Page View
</label>
</div>
</div>
</div>
<div class="dropdown mt-1 mt-lg-0 mr-1">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownStatus"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Status
</button>
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
<h6 class="dropdown-header">Select status</h6>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusReady"
value="ready"
ng-model="filters.status.ready"
/>
<label class="form-check-label" for="statusReady">
Ready
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusExpired"
value="expired"
ng-model="filters.status.expired"
/>
<label class="form-check-label" for="statusExpired">
Expired
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusRemoved"
value="removed"
ng-model="filters.status.removed"
/>
<label class="form-check-label" for="statusRemoved">
Removed
</label>
</div>
</div>
</div>
</div>
</div>
</form>
<div class="d-flex flex-wrap mt-2 mt-md-0" style="gap: 6px">
<a href="/anonymize" class="text-center btn btn-primary btn-sm">
<div class="w-100 py-3">
<!-- Header row: title + action buttons -->
<div class="d-flex align-items-center justify-content-between mb-3">
<h2 class="dashboard-title mb-0">Pull Requests</h2>
<div class="d-flex flex-wrap" style="gap: 6px">
<a href="/anonymize" class="btn btn-primary btn-sm">
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize Repo
</a>
<a
href="/pull-request-anonymize"
class="text-center btn btn-primary btn-sm"
>
<a href="/pull-request-anonymize" class="btn btn-primary btn-sm">
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize PR
</a>
<a
@@ -170,138 +16,180 @@
data-toggle="tooltip"
data-placement="bottom"
href="/claim"
class="text-center btn btn-secondary btn-sm"
class="btn btn-sm"
>
Claim
</a>
</div>
</div>
</div>
<ul class="p-0 m-0 w-100">
<li
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
ng-class="{'expired': pr.status == 'expired','removed': pr.status == 'removed','error': pr.status == 'error' }"
ng-repeat="pr in pullRequests| filter:pullRequestFilter| orderBy:orderBy as filteredPullRequests"
>
<div class="w-100">
<div class="">
<h3>
<a
ng-href="/pr/{{pr.pullRequestId}}"
ng-bind="pr.pullRequestId"
></a>
<span
class="badge"
ng-class="{'badge-warning': pr.status == 'removed' || pr.status == 'expired' || pr.status == 'removing' || pr.status == 'expiring', 'badge-info': pr.status == 'preparing' || pr.status == 'download', 'badge-success': pr.status == 'ready', 'badge-danger': pr.status == 'error'}"
><span ng-bind="pr.status | title"></span>
<span
ng-if="pr.status == 'error'"
ng-bind="': ' + pr.statusMessage"
></span
></span>
</h3>
<!-- Search + filters row -->
<form class="w-100" aria-label="Pull Requests" accept-charset="UTF-8">
<div class="d-flex flex-column flex-md-row align-items-md-center" style="gap: 8px">
<div class="flex-grow-1">
<input
type="search"
id="search"
class="form-control"
aria-label="Find a pull request..."
placeholder="Find a pull request..."
autocomplete="off"
ng-model="search"
/>
</div>
<div class="color-text-secondary mt-t">
<span class="pull-request">
<i class="fab fa-github" aria-hidden="true"></i>
<a
href="https://github.com/{{pr.source.repositoryFullName}}/pull/{{pr.source.pullRequestId}}"
class="fullName"
>{{pr.source.repositoryFullName}}@{{pr.source.pullRequestId}}</a
<div class="d-flex flex-wrap" style="gap: 6px">
<div class="dropdown">
<button
class="btn btn-sm dropdown-toggle"
type="button"
id="dropdownSort"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
</span>
<span
class="badge"
ng-class="{'badge-success':pr.merged, 'badge-warning':pr.state=='open', 'badge-danger':pr.state=='closed' &&!pr.merged}"
>
{{pr.merged?"merged":pr.state | title}}
</span>
anonymized {{pr.anonymizeDate | humanTime}}
</div>
<div class="color-text-secondary mt-2">
<span class="ml-0 mr-3" ng-if="::pr.conference">
<i class="fas fa-chalkboard-teacher"></i>
{{pr.conference}}
</span>
<span
class="ml-0 mr-3"
class="terms"
title="Terms: {{::pr.options.terms.join(', ')}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="fas fa-shield-alt"></i>
{{::pr.options.terms.length | number}}
</span>
<span
class="ml-0 mr-3"
title="View: {{::pr.pageView | number}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="far fa-eye" aria-hidden="true"></i>
{{::pr.pageView | number}}
</span>
<span
class="ml-0 mr-3"
title="Last view: {{::pr.lastView | date}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="far fa-calendar-alt" aria-hidden="true"></i>
Last view: {{::pr.lastView | humanTime}}</span
>
<span
class="ml-0 mr-3"
ng-if="pr.options.expirationMode!='never' && pr.status == 'ready'"
>
<i class="far fa-clock" aria-hidden="true"></i>
Expire: {{pr.options.expirationDate | humanTime}}</span
>
Sort
</button>
<div class="dropdown-menu" aria-labelledby="dropdownSort">
<h6 class="dropdown-header">Select order</h6>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortFullName" value="fullName" ng-model="orderBy" />
<label class="form-check-label" for="sortFullName">Pull Request</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortAnonymizeDate" value="-anonymizeDate" ng-model="orderBy" />
<label class="form-check-label" for="sortAnonymizeDate">Anonymize Date</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortStatus" value="-status" ng-model="orderBy" />
<label class="form-check-label" for="sortStatus">Status</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortLastView" value="-lastView" ng-model="orderBy" />
<label class="form-check-label" for="sortLastView">Last View</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="radio" name="sort" id="sortPageView" value="-pageView" ng-model="orderBy" />
<label class="form-check-label" for="sortPageView">Page View</label>
</div>
</div>
</div>
<div class="dropdown">
<button
class="btn btn-sm dropdown-toggle"
type="button"
id="dropdownStatus"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Status
</button>
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
<h6 class="dropdown-header">Select status</h6>
<div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" name="sort" id="statusReady" value="ready" ng-model="filters.status.ready" />
<label class="form-check-label" for="statusReady">Ready</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" name="sort" id="statusExpired" value="expired" ng-model="filters.status.expired" />
<label class="form-check-label" for="statusExpired">Expired</label>
</div>
<div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" name="sort" id="statusRemoved" value="removed" ng-model="filters.status.removed" />
<label class="form-check-label" for="statusRemoved">Removed</label>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex">
</form>
<!-- Active filter chips -->
<div class="d-flex flex-wrap mt-2" style="gap: 6px" ng-show="filters.status.ready === false || filters.status.expired === false || filters.status.removed === false">
<span class="filter-chip" ng-show="!v" ng-repeat="(f, v) in filters.status">
{{f | title}}
<button type="button" class="filter-chip-close" aria-label="Remove filter" ng-click="filters.status[f] = true;">
&times;
</button>
</span>
</div>
</div>
<!-- Pull request list -->
<ul class="repo-list w-100">
<li
class="repo-list-item"
ng-class="{'repo-inactive': pr.status == 'expired' || pr.status == 'removed' || pr.status == 'error'}"
ng-repeat="pr in pullRequests| filter:pullRequestFilter| orderBy:orderBy as filteredPullRequests"
>
<div class="repo-list-item-content">
<div class="repo-list-item-main">
<div class="repo-list-item-header">
<a ng-href="/pr/{{pr.pullRequestId}}" class="repo-name" ng-bind="pr.pullRequestId"></a>
<span
class="status-badge"
ng-class="{'status-removed': pr.status == 'removed' || pr.status == 'expired' || pr.status == 'removing' || pr.status == 'expiring', 'status-preparing': pr.status == 'preparing' || pr.status == 'download', 'status-ready': pr.status == 'ready', 'status-error': pr.status == 'error'}"
><span ng-bind="pr.status | title"></span><span
ng-if="pr.status == 'error'"
ng-bind="': ' + pr.statusMessage"
></span></span>
</div>
<div class="repo-source">
<span>
<i class="fab fa-github" aria-hidden="true"></i>
<a href="https://github.com/{{pr.source.repositoryFullName}}/pull/{{pr.source.pullRequestId}}">{{pr.source.repositoryFullName}}#{{pr.source.pullRequestId}}</a>
</span>
<span
class="status-badge"
ng-class="{'status-ready': pr.merged, 'status-removed': pr.state=='open', 'status-error': pr.state=='closed' && !pr.merged}"
style="font-size: 10px;"
>
{{pr.merged ? "merged" : pr.state | title}}
</span>
<span class="repo-date">anonymized {{pr.anonymizeDate | humanTime}}</span>
</div>
</div>
<div class="repo-meta">
<span ng-if="::pr.conference" title="Conference">
<i class="fas fa-chalkboard-teacher"></i> {{pr.conference}}
</span>
<span title="Terms: {{::pr.options.terms.join(', ')}}" data-toggle="tooltip" data-placement="bottom">
<i class="fas fa-shield-alt"></i> {{::pr.options.terms.length | number}}
</span>
<span title="Views: {{::pr.pageView | number}}" data-toggle="tooltip" data-placement="bottom">
<i class="far fa-eye"></i> {{::pr.pageView | number}}
</span>
<span title="Last view: {{::pr.lastView | date}}" data-toggle="tooltip" data-placement="bottom">
<i class="far fa-calendar-alt"></i> {{::pr.lastView | humanTime}}
</span>
<span ng-if="pr.options.expirationMode!='never' && pr.status == 'ready'">
<i class="far fa-clock"></i> Expire: {{pr.options.expirationDate | humanTime}}
</span>
</div>
</div>
<div class="repo-list-item-actions">
<div class="dropdown">
<button
class="btn black_border dropdown-toggle btn-sm"
class="btn btn-sm dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Actions
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a
class="dropdown-item"
href="/pull-request-anonymize/{{pr.pullRequestId}}"
>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/pull-request-anonymize/{{pr.pullRequestId}}">
<i class="far fa-edit" aria-hidden="true"></i> Edit
</a>
<a
class="dropdown-item"
href="#"
ng-show="pr.status == 'ready' || pr.status == 'error'"
ng-click="updatePullRequest(pr)"
>
<a class="dropdown-item" href="#" ng-show="pr.status == 'ready' || pr.status == 'error'" ng-click="updatePullRequest(pr)">
<i class="fas fa-sync"></i> Force update
</a>
<a
class="dropdown-item"
href="#"
ng-show="pr.status == 'removed'"
ng-click="updatePullRequest(pr)"
>
<i class="fas fa-check-circle"></i>
Enable
<a class="dropdown-item" href="#" ng-show="pr.status == 'removed'" ng-click="updatePullRequest(pr)">
<i class="fas fa-check-circle"></i> Enable
</a>
<a
class="dropdown-item"
href="#"
ng-show="pr.status == 'ready'"
ng-click="removePullRequest(pr)"
>
<a class="dropdown-item" href="#" ng-show="pr.status == 'ready'" ng-click="removePullRequest(pr)">
<i class="fas fa-trash-alt"></i> Remove
</a>
<a class="dropdown-item" href="/pr/{{pr.pullRequestId}}/">
@@ -311,11 +199,9 @@
</div>
</div>
</li>
<li
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
ng-if="filteredPullRequests.length == 0"
>
There is no pull request to display.
<li class="repo-list-empty" ng-if="filteredPullRequests.length == 0">
<i class="fas fa-inbox"></i>
<span>No pull requests to display.</span>
</li>
</ul>
</div>
+344 -793
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
File diff suppressed because one or more lines are too long