core&ui: add all enroll users status in training (#456)
Co-authored-by: undefined <i@undefined.moe>pull/468/head
parent
d02934f766
commit
c2f5541e83
@ -1,25 +1,82 @@
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import pjax from 'vj/utils/pjax';
|
||||
import { slideDown, slideUp } from 'vj/utils/slide';
|
||||
|
||||
async function handleSection(ev, type: string) {
|
||||
const $section = $(ev.currentTarget).closest('.training__section');
|
||||
if ($section.is(`.${type}d, .animating`)) return;
|
||||
type SectionAction = 'expand' | 'collapse';
|
||||
type SectionState = 'expanded' | 'collapsed';
|
||||
|
||||
function action2state(action: SectionAction): SectionState {
|
||||
return action === 'expand' ? 'expanded' : 'collapsed';
|
||||
}
|
||||
|
||||
async function setSectionState($section: JQuery<HTMLElement>, state: SectionState) {
|
||||
if ($section.is(`.${state}, .animating`)) return;
|
||||
$section.addClass('animating');
|
||||
const $detail = $section.find('.training__section__detail');
|
||||
if (type === 'expand') {
|
||||
if (state === 'expanded') {
|
||||
await slideDown($detail, 300, { opacity: 0 }, { opacity: 1 });
|
||||
} else {
|
||||
await slideUp($detail, 300, { opacity: 1 }, { opacity: 0 });
|
||||
}
|
||||
$section.addClass(type === 'expand' ? 'expanded' : 'collapsed');
|
||||
$section.removeClass(type === 'expand' ? 'collapsed' : 'expanded');
|
||||
$section.addClass(state);
|
||||
$section.removeClass(state === 'expanded' ? 'collapsed' : 'expanded');
|
||||
$section.removeClass('animating');
|
||||
}
|
||||
|
||||
async function handleSection(ev: JQuery.ClickEvent<Document>, type: SectionAction) {
|
||||
const $section = $(ev.currentTarget).closest('.training__section');
|
||||
await setSectionState($section, action2state(type));
|
||||
}
|
||||
|
||||
function searchUser() {
|
||||
const val = $('input[name=uid]').val().toString().toLowerCase();
|
||||
$('.enroll_user_menu_item').each((i, e) => {
|
||||
const $item = $(e);
|
||||
const $username = $item.data('uname').toString().toLowerCase();
|
||||
const $uid = $item.data('uid').toString();
|
||||
$item.toggle($username.includes(val) || $uid === val);
|
||||
});
|
||||
}
|
||||
|
||||
function selectUser(ev) {
|
||||
ev.preventDefault();
|
||||
if ($('.enroll_user_menu_item:visible').length === 1) {
|
||||
$('.enroll_user_menu_item:visible').first().find('a')[0].click();
|
||||
}
|
||||
}
|
||||
|
||||
function handleChooseUser(ev) {
|
||||
ev.preventDefault();
|
||||
$('.enroll_user_menu_item .active').removeClass('active');
|
||||
$(ev.currentTarget).addClass('active');
|
||||
pjax.request({ url: ev.currentTarget.href });
|
||||
}
|
||||
|
||||
async function handleSidebarClick(ev: JQuery.ClickEvent<Document>) {
|
||||
const id = $(ev.currentTarget).attr('href');
|
||||
const $section = $(id).closest('.training__section');
|
||||
await setSectionState($section, 'expanded');
|
||||
}
|
||||
|
||||
async function handleHashChange() {
|
||||
const id = window.location.hash;
|
||||
if (id.startsWith('#node-')) {
|
||||
const $section = $(id).closest('.training__section');
|
||||
await setSectionState($section, 'expanded');
|
||||
}
|
||||
}
|
||||
|
||||
const page = new NamedPage('training_detail', () => {
|
||||
$('.search__input').on('input', _.debounce(searchUser, 500));
|
||||
$('#searchForm').on('submit', selectUser);
|
||||
$(document).on('click', '[name="training__section__expand"]', (ev) => handleSection(ev, 'expand'));
|
||||
$(document).on('click', '[name="training__section__collapse"]', (ev) => handleSection(ev, 'collapse'));
|
||||
$(document).on('click', '.enroll_user_menu_item > a', (ev) => handleChooseUser(ev));
|
||||
$(document).on('click', '#menu-item-training_detail > ul > li > a', (ev) => handleSidebarClick(ev));
|
||||
window.addEventListener('hashchange', handleHashChange);
|
||||
$(handleHashChange);
|
||||
});
|
||||
|
||||
export default page;
|
||||
|
@ -0,0 +1,164 @@
|
||||
{% import "components/record.html" as record with context %}
|
||||
{% import "components/problem.html" as problem with context %}
|
||||
{% set expanded = 0 %}
|
||||
<div data-fragment-id="training_detail">
|
||||
{% if handler.request.query.uid and handler.request.query.uid != handler.user._id %}
|
||||
<blockquote class="typo note">
|
||||
<p>{{ _('page.training_detail.see_other_user_detail').format(udict[handler.request.query.uid].uname) }}</p>
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
{%- for node in tdoc['dag'] -%}
|
||||
<div data-heading-extract-to="#menu-item-training_detail" class="training__section {% if expanded < 5 and (nsdict[node['_id']]['isProgress'] or nsdict[node['_id']]['isOpen']) %}expanded{% set expanded = expanded + 1 %}{% else %}collapsed{% endif %}">
|
||||
<div class="section__header clearfix">
|
||||
<div class="float-left">
|
||||
<h1 data-heading id="node-{{ node._id }}" class="section__title">{{ _('Section') }} {{ node['_id'] }}. {{ node['title'].split('\n')[0] }}</h1>
|
||||
{% if node['title'].split('\n')[1] %}<h4>{{ node['title'].split('\n')[1] }}</h4>{% endif %}
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<h1 class="section__title training-section-status--text {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['isProgress'] %}progress{% elif nsdict[node['_id']]['isOpen'] %}open{% else %}invalid{% endif %}">
|
||||
<span class="icon training-section-status--icon {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['isProgress'] %}progress{% elif nsdict[node['_id']]['isOpen'] %}open{% else %}invalid{% endif %}"></span>
|
||||
{% if nsdict[node['_id']]['isDone'] %}
|
||||
{{ _('Completed') }}
|
||||
{% elif nsdict[node['_id']]['isProgress'] %}
|
||||
{{ _('In Progress') }}
|
||||
{% elif nsdict[node['_id']]['isOpen'] %}
|
||||
{{ _('Open') }}
|
||||
{% else %}
|
||||
{{ _('Invalid') }}
|
||||
{% endif %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<ul class="supplementary dot list">
|
||||
<li>
|
||||
<a href="javascript:;" name="training__section__expand" class="expanded--hidden"><span class="icon icon-expand_more"></span> {{ _('expand') }}</a>
|
||||
<a href="javascript:;" name="training__section__collapse" class="collapsed--hidden"><span class="icon icon-expand_less"></span> {{ _('collapse') }}</a>
|
||||
</li>
|
||||
<!--
|
||||
TODO(twd2): twd2 todo
|
||||
<li>
|
||||
递交了 ? 次完成该章节 (TOP ?%)
|
||||
</li>
|
||||
<li>
|
||||
第 ? 个完成该章节 (总计 ? 用户完成)
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="training__section__detail">
|
||||
{% if nsdict[node['_id']]['isInvalid'] %}
|
||||
<div class="section__body">
|
||||
<blockquote class="typo note">
|
||||
<p>{{ _('This section cannot be challenged at present, so please complete the following sections first') }}:</p>
|
||||
<ul>
|
||||
{%- for nid in node['requireNids'] -%}
|
||||
<li>{{ _('Section') }} {{ _(nid) }}. {{ ndict[nid]['title'] }} ({{ _('Completed') }} {{ nsdict[nid]['progress'] }}%)</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
</blockquote>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if node['content'] %}
|
||||
<div class="section__body typo">
|
||||
{{ node['content']|markdown|safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="section__body no-padding training__problems">
|
||||
{% set should_compare = (handler.request.query.uid or handler.user._id) != handler.user._id and tsdoc["enroll"] %}
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if should_compare %}
|
||||
<col class="col--status col--status--sm">
|
||||
<col class="col--status col--status--sm">
|
||||
{% else %}
|
||||
<col class="col--status">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<col class="col--name">
|
||||
<col class="col--tried">
|
||||
<col class="col--ac">
|
||||
<col class="col--difficulty">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if should_compare %}
|
||||
<th class="col--status col--status--sm record-status--border">{{ _('Status') }}</th>
|
||||
<th class="col--status col--status--sm">{{ _('My status') }}</th>
|
||||
{% else %}
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<th class="col--name">{{ _('Problem') }}</th>
|
||||
<th class="col--tried">{{ _('Tried') }}</th>
|
||||
<th class="col--ac">{{ _('AC') }}</th>
|
||||
<th class="col--difficulty">{{ _('Difficulty') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for pid in node['pids'] -%}
|
||||
{% if pid in pdict %}
|
||||
{% set pdoc=pdict[pid] %}
|
||||
{% set psdoc=psdict[pid] %}
|
||||
{% set self_psdoc=selfPsdict[pid] %}
|
||||
<tr>
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if psdoc['rid'] %}
|
||||
{{ record.render_status_td(psdoc, rid_key='rid', short=should_compare) }}
|
||||
{% else %}
|
||||
<td class="col--status record-status--border"></td>
|
||||
{% endif %}
|
||||
{% if should_compare %}
|
||||
{% if self_psdoc['rid'] %}
|
||||
{{ record.render_status_td(self_psdoc, rid_key='rid', short=true) }}
|
||||
{% else %}
|
||||
<td class="col--status col--status--sm"></td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<td class="col--name col--problem-name">
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<form class="form--inline" action="{{ url('problem_main') }}" method="post">
|
||||
<input type="hidden" name="pid" value="{{ pdoc.docId }}">
|
||||
<input type="hidden" name="operation" value="{% if not psdoc['star'] %}star{% else %}unstar{% endif %}">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
<button class="star{% if psdoc['star'] %} activated{% endif %}" type="submit">
|
||||
<span class="starred--hide"><span class="icon icon-star--outline" data-tooltip="{{ _('Star') }}"></span></span>
|
||||
<span class="starred--show"><span class="icon icon-star" data-tooltip="{{ _('Unstar') }}"></span></span>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{{ problem.render_problem_title(
|
||||
pdoc,
|
||||
invalid=not tsdoc['enroll'],
|
||||
show_tags=false
|
||||
) }}
|
||||
</td>
|
||||
<td class="col--tried">{{ pdoc.nSubmit }}</td>
|
||||
<td class="col--ac">{{ pdoc.nAccept }}</td>
|
||||
<td class="col--difficulty">{{ pdoc['difficulty'] or lib.difficulty(pdoc.nSubmit, pdoc.nAccept) or _('(None)') }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
{% set pdoc = {'_id': pid, 'hidden': true, 'title': '*'} %}
|
||||
<tr>
|
||||
<td class="col--status record-status--border">
|
||||
</td>
|
||||
<td class="col--name col--problem-name">
|
||||
{{ problem.render_problem_title(pdoc, invalid=true) }}
|
||||
</td>
|
||||
<td class="col--tried">*</td>
|
||||
<td class="col--ac">*</td>
|
||||
<td class="col--difficulty">*</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
<div class="section__body"> <!-- space placeholder --></div>
|
||||
</div>
|
Loading…
Reference in New Issue