mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-11-14 00:07:00 +01:00
The UI was uneven: the home page looked finished, but app pages used three different display typefaces, the anonymize flow changed frame between steps, and the dashboard, explorer and admin screens each had their own controls, status colours and edge-case handling. Make every screen follow one system: shared page header, grid, buttons, filters and status colours; a repository picker and step trail on anonymize; a "What reviewers will see" preview of the actual reviewer page; consistent file headers and viewers for every file type; truthful admin charts and tables. Fold the accumulated override blocks back into style.css (22% smaller, no visual change). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
236 lines
17 KiB
HTML
236 lines
17 KiB
HTML
<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" :class="{'card': !pullRequestUrl,'container': pullRequestUrl}">
|
|
<form class="form needs-validation" name="anonymizeForm" novalidate v-form="viewState" :class="{'card-body': !pullRequestUrl}">
|
|
<div class="paper-crumbs">Anonymize / <span class="here">Pull request</span></div>
|
|
<h1 class="paper-page-title">Anonymize a <em>pull request</em></h1>
|
|
<p class="paper-page-lede">
|
|
Fill in the details — it only takes a minute.
|
|
</p>
|
|
|
|
<div class="paper-section-eyebrow anonymize-section-title">
|
|
<i class="fab fa-github"></i> Source
|
|
</div>
|
|
<!-- pullRequestUrl -->
|
|
<div class="form-group">
|
|
<label for="pullRequestUrl">URL of your pull request</label>
|
|
<input type="text" class="form-control" name="pullRequestUrl" id="pullRequestUrl" placeholder="https://github.com/owner/repo/pull/123" v-field="{ state: viewState, set: value => { pullRequestUrl = value }, value: pullRequestUrl, form: "anonymizeForm", options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }, change: () => { pullRequestSelected() } }" :class="{'is-invalid': anonymize?.pullRequestUrl?.invalid}">
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestUrl?.errors?.access">
|
|
{{ pullRequestUrl }} is not accessible. Some organizations are
|
|
restricting the access to the repositories.
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestUrl?.errors?.missing">
|
|
{{ pullRequestUrl }} does not exist or is not accessible
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestUrl?.errors?.used">
|
|
{{ pullRequestUrl }} is already anonymized
|
|
</div>
|
|
</div>
|
|
<div v-show="pullRequestUrl">
|
|
<div class="form-group">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="update" name="update" v-field="{ state: viewState, set: value => { options.update = value }, value: options?.update, form: "anonymizeForm", options: {} }">
|
|
<label class="form-check-label" for="update">Auto update</label>
|
|
<small id="updateHelp" class="form-text text-muted">Automatically update the anonymized pull request with the
|
|
latest updates. The pull request is updated once per day
|
|
maximum.</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="paper-section-eyebrow anonymize-section-title">
|
|
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> Conference ID
|
|
</div>
|
|
<!-- 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" v-field="{ state: viewState, set: value => { conference = value }, value: conference, form: "anonymizeForm", options: { debounce: { default: 800, blur: 0 }, updateOn: 'default blur' } }" :class="{'is-invalid': anonymize?.conference?.invalid}">
|
|
<small class="form-text text-muted" v-show="conference_data"><a target="_target" :href="safeUrl((conference_data?.url))">{{ conference_data?.name }}</a>
|
|
will expire on {{ fmt?.date(conference_data?.endDate) }}.</small>
|
|
|
|
<div class="invalid-feedback" v-show="anonymize?.conference?.errors?.activated">
|
|
The conference is not activated.
|
|
</div>
|
|
<small class="form-text text-muted" v-show="!conference_data">
|
|
Use the Conference ID that your conference provided you. This
|
|
will update automatically the anonymization options based on the
|
|
conference preferences.
|
|
</small>
|
|
</div>
|
|
|
|
<div class="paper-section-eyebrow anonymize-section-title">
|
|
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-shield"></use></svg> Anonymization Options
|
|
</div>
|
|
<!-- Pull Request ID -->
|
|
<div class="form-group">
|
|
<label for="pullRequestId">Anonymized pull request id</label>
|
|
<input type="text" class="form-control" name="pullRequestId" id="pullRequestId" v-field="{ state: viewState, set: value => { pullRequestId = value }, value: pullRequestId, form: "anonymizeForm", options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' } }" :class="{'is-invalid': anonymize?.pullRequestId?.invalid}">
|
|
<small id="idHelp" class="form-text text-muted">Id used in the url:
|
|
https://anonymous.4open.science/r/{{ pullRequestId }}</small>
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestId?.errors?.format">
|
|
Repository id can only contain letters and numbers
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestId?.errors?.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" v-field="{ state: viewState, set: value => { terms = value }, value: terms, form: "anonymizeForm", options: { debounce: 250 } }" :class="{'is-invalid': anonymize?.terms?.invalid}"></textarea>
|
|
<small id="termsHelp" class="form-text text-muted">One term per line. Each term will be replaced by XXX.</small>
|
|
<div class="invalid-feedback" v-show="anonymize?.terms?.errors?.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" v-field="{ state: viewState, set: value => { options.expirationMode = value }, value: options?.expirationMode, form: "anonymizeForm", options: {} }">
|
|
<option value="never" selected>Never expire</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" v-show="!(options?.expirationMode=='never')">
|
|
<label for="expirationDate">Expiration date of the anonymized repository</label>
|
|
<input class="form-control" type="date" name="expirationDate" id="expirationDate" v-field="{ state: viewState, set: value => { options.expirationDate = value }, value: options?.expirationDate, form: "anonymizeForm", options: {} }">
|
|
<p class="date-echo" aria-live="polite" v-if="fmt?.dateEcho(options?.expirationDate)">Expires {{ fmt?.dateEcho(options?.expirationDate) }}</p>
|
|
<small class="form-text text-muted" v-show="options?.expirationMode=='remove'">After {{ fmt?.date(options?.expirationDate) }}, 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" v-show="options?.expirationMode=='redirect'">After {{ fmt?.date(options?.expirationDate) }}, the visitors of the
|
|
anonymized repository will be redirected to
|
|
{{ pullRequestUrl }}.</small>
|
|
</div>
|
|
|
|
<div class="accordion mb-3" id="options">
|
|
<div class="card">
|
|
<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">
|
|
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-filter"></use></svg> Advanced options
|
|
</button>
|
|
</h2>
|
|
</div>
|
|
|
|
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#options">
|
|
<div class="card-body">
|
|
<div class="form-group mb-0">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="link" name="link" v-field="{ state: viewState, set: value => { options.link = value }, value: options?.link, form: "anonymizeForm", options: {} }">
|
|
<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" v-field="{ state: viewState, set: value => { options.image = value }, value: options?.image, form: "anonymizeForm", options: {} }">
|
|
<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="date" name="date" v-field="{ state: viewState, set: value => { options.date = value }, value: options?.date, form: "anonymizeForm", options: {} }">
|
|
<label class="form-check-label" for="date">Display dates</label>
|
|
<small class="form-text text-muted">Display the date of the Pull Request and the date of
|
|
the comments.</small>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="username" name="username" v-field="{ state: viewState, set: value => { options.username = value }, value: options?.username, form: "anonymizeForm", options: {} }">
|
|
<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" v-field="{ state: viewState, set: value => { options.comments = value }, value: options?.comments, form: "anonymizeForm", options: {} }">
|
|
<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" v-field="{ state: viewState, set: value => { options.diff = value }, value: options?.diff, form: "anonymizeForm", options: {} }">
|
|
<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" v-field="{ state: viewState, set: value => { options.origin = value }, value: options?.origin, form: "anonymizeForm", options: {} }">
|
|
<label class="form-check-label" for="origin">Display the project name</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="title" name="title" v-field="{ state: viewState, set: value => { options.title = value }, value: options?.title, form: "anonymizeForm", options: {} }">
|
|
<label class="form-check-label" for="title">Display the PR title</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="body" name="body" v-field="{ state: viewState, set: value => { options.body = value }, value: options?.body, form: "anonymizeForm", options: {} }">
|
|
<label class="form-check-label" for="body">Display the PR body and comment bodies</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<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) })">
|
|
<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) })">
|
|
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-save"></use></svg> Update
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-8 p-2 overflow-auto anonymize-preview-col" v-if="details">
|
|
<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">{{ anonymize(details?.pullRequest?.title) }}</span>
|
|
<span class="badge" :class="{'badge-success':details?.pullRequest?.merged, 'badge-warning':details?.pullRequest?.state=='open', 'badge-danger':details?.pullRequest?.state=='closed' &&!details?.pullRequest?.merged}">
|
|
{{ fmt?.title(details?.pullRequest?.merged?"merged":details?.pullRequest?.state) }}
|
|
</span>
|
|
</h2>
|
|
<small v-if="options?.date" v-text="fmt?.date(details?.pullRequest?.updatedDate)"></small>
|
|
</div>
|
|
<small v-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)" v-if="options?.body">
|
|
<markdown :content="anonymize(details?.pullRequest?.body)" :options="options" :terms="terms"></markdown>
|
|
</div>
|
|
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
|
<li class="nav-item" role="presentation" v-if="options?.diff">
|
|
<button class="nav-link active" id="pills-diff-tab" data-toggle="pill" data-target="#pills-diff" type="button" role="tab" aria-controls="pills-diff" aria-selected="true">
|
|
Diff
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation" v-if="options?.comments">
|
|
<button class="nav-link" id="pills-comments-tab" data-toggle="pill" data-target="#pills-comments" type="button" role="tab" aria-controls="pills-comments" aria-selected="false" :class="{'active':!options?.diff}">
|
|
<span v-text="fmt.plural(details?.pullRequest?.comments?.length, {'0': 'No comment',
|
|
'one': 'One Comment',
|
|
'other': '{} Comments'})">
|
|
</span>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
<div class="tab-content" id="pills-tabContent">
|
|
<div class="tab-pane show active" id="pills-diff" role="tabpanel" aria-labelledby="pills-diff-tab">
|
|
<div class="pr-diff shadow-sm p-3 mb-4 rounded" style="background: var(--sidebar-bg-color)" v-if="options?.diff">
|
|
<pre style="overflow-x: auto"><code v-html="sanitize(fmt?.diff(anonymize(details?.pullRequest?.diff)))"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="pills-comments" role="tabpanel" aria-labelledby="pills-comments-tab" :class="{'show active':!options?.diff}">
|
|
<ul class="pr-comments list-group" v-if="options?.comments">
|
|
<li class="pr-comment list-group-item" v-for="(comment, index) in details?.pullRequest?.comments">
|
|
<div class="d-flex w-100 justify-content-between flex-wrap">
|
|
<h5 class="mb-1" v-if="options?.username">
|
|
@{{ anonymize(comment?.author) }}
|
|
</h5>
|
|
<small v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></small>
|
|
</div>
|
|
<p class="mb-1">
|
|
<markdown class="pr-comment-body" v-if="options?.body" :content="anonymize(comment?.body)" :options="options" :terms="terms"></markdown>
|
|
</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|