mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
ui: add complex descriptors selection in problem search (#636)
Co-authored-by: undefined <i@undefined.moe> Co-authored-by: panda <panda_dtdyy@outlook.com>
This commit is contained in:
9 files changed
+312
-87
No files matched your search
@@ -518,8 +518,8 @@ Numeric PID: 数字题号
|
||||
Oh, the user doesn't have any contributions!: 啊哦,这个用户还没贡献过题目~
|
||||
Oh, the user hasn't created any discussions yet!: 这个用户还没有发布过讨论
|
||||
Oh, the user hasn't submitted yet!: 这个用户还没有交过题 _(:зゝ∠)_
|
||||
Oh, there is no task in the queue!: 喔,队列中目前没有任务。
|
||||
Oh, there are no tasks that match the filter!: 喔,目前没有符合过滤条件的任务。
|
||||
Oh, there is no task in the queue!: 喔,队列中目前没有任务。
|
||||
Ok: 确定
|
||||
Only A-Z, a-z, 0-9 and _ are accepted: 只接受 A-Z, a-z, 0-9 和 _
|
||||
Oops, there are no results.: 呀,没有结果。
|
||||
@@ -582,6 +582,7 @@ Preview: 预览
|
||||
Privacy: 隐私
|
||||
Problem {1} not found.: 题目 {1} 不存在。
|
||||
Problem Categories: 题目标签
|
||||
Problem Category: 标签
|
||||
Problem Data: 题目数据
|
||||
Problem Description: 题目描述
|
||||
Problem ID List: 题目 ID 列表
|
||||
@@ -684,7 +685,9 @@ Section: 章节
|
||||
Security: 安全
|
||||
Select a node to create discussion.: 选择一个节点来发表讨论。
|
||||
Select a role: 选择一个角色
|
||||
Select Category: 选择标签
|
||||
Select User: 选择用户
|
||||
Selected categories: 已选标签
|
||||
Selected roles have been deleted.: 所选角色已删除。
|
||||
Selected users have been removed from the domain.: 所选用户已从此域中移除。
|
||||
Send Code after acceptance: 通过题目后发送源代码
|
||||
|
||||
@@ -646,6 +646,7 @@ Pretest?: 自测?
|
||||
Privacy: 隐私
|
||||
Privilege: 权限
|
||||
Problem {1} not found.: 题目 {1} 不存在。
|
||||
Problem Category: 标签
|
||||
Problem Data: 题目数据
|
||||
Problem ID cannot be a pure number. Leave blank if you want to use numberic id.: 题目ID不能为纯数字。 若要自动分配数字ID请将此处留空。
|
||||
Problem ID List: 题目 ID 列表
|
||||
@@ -767,7 +768,9 @@ Section: 章节
|
||||
Security: 安全
|
||||
Select a node to create discussion.: 选择一个节点来发表讨论。
|
||||
Select a role: 选择一个角色
|
||||
Select Category: 选择标签
|
||||
Select User: 选择用户
|
||||
Selected categories: 已选标签
|
||||
Selected files have been deleted.: 所选文件已被删除。
|
||||
Selected problems have been deleted.: 所选题目已被删除。
|
||||
Selected roles have been deleted.: 所选角色已删除。
|
||||
|
||||
@@ -125,6 +125,9 @@ html
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
.flex-wrap
|
||||
flex-wrap: wrap
|
||||
|
||||
.flex-fill
|
||||
flex: 1
|
||||
min-width: 0
|
||||
|
||||
@@ -38,19 +38,6 @@
|
||||
&__category
|
||||
margin-bottom: rem(6px)
|
||||
|
||||
&-tag
|
||||
display: inline-block
|
||||
padding: rem(5px 6px)
|
||||
|
||||
&:hover
|
||||
color: #FFF
|
||||
background: $primary-color
|
||||
text-decoration: none
|
||||
|
||||
&.selected
|
||||
color: #FFF
|
||||
background: $secondary-color
|
||||
|
||||
&__drop
|
||||
position: relative
|
||||
font-size: rem($font-size-small)
|
||||
@@ -80,19 +67,19 @@
|
||||
&__subcategory
|
||||
margin: rem(2px)
|
||||
display: inline-block
|
||||
|
||||
&__tag
|
||||
display: inline-block
|
||||
padding: rem(5px 6px)
|
||||
|
||||
&-tag
|
||||
display: inline-block
|
||||
padding: rem(5px 6px)
|
||||
&:hover
|
||||
color: #FFF
|
||||
background: $primary-color
|
||||
text-decoration: none
|
||||
|
||||
&:hover
|
||||
color: #FFF
|
||||
background: $primary-color
|
||||
text-decoration: none
|
||||
|
||||
&.selected
|
||||
color: #FFF
|
||||
background: $secondary-color
|
||||
&.selected
|
||||
color: #FFF
|
||||
background: $secondary-color
|
||||
|
||||
.drop-target-attached-right .widget--category-filter__drop
|
||||
margin-right: 0
|
||||
@@ -126,6 +113,82 @@
|
||||
.data-table > thead > tr
|
||||
height: 54.9px
|
||||
|
||||
.search-container
|
||||
padding-bottom: 0
|
||||
|
||||
.title
|
||||
margin-bottom: rem(5px)
|
||||
|
||||
.dialog-button
|
||||
font-size: rem($font-size-secondary)
|
||||
display: inline-block
|
||||
padding: rem(5px 6px)
|
||||
margin-bottom: rem(5px)
|
||||
|
||||
.search-tag__item
|
||||
display: inline-block
|
||||
padding: rem(5px 6px)
|
||||
font-size: rem($font-size-secondary)
|
||||
margin-bottom: rem(5px)
|
||||
|
||||
&:hover
|
||||
color: #555;
|
||||
background: #f4f4f4;
|
||||
text-decoration: none
|
||||
|
||||
&.selected
|
||||
color: #FFF
|
||||
background: $primary-color !important
|
||||
|
||||
&.hide
|
||||
display: none
|
||||
opacity: 0
|
||||
|
||||
.category-filter__header
|
||||
[data-category-container].list
|
||||
overflow-x: auto
|
||||
|
||||
.list
|
||||
.list__item
|
||||
padding: 12px 12px
|
||||
cursor: pointer
|
||||
margin: 0px 10px
|
||||
background: #eeeff3
|
||||
min-width: 90px
|
||||
text-align: center
|
||||
position: relative
|
||||
|
||||
&.selected
|
||||
background: $primary-color
|
||||
color: #FFF
|
||||
font-weight: bold
|
||||
|
||||
.list__item::first
|
||||
margin-left: 0px
|
||||
|
||||
.category-filter__body
|
||||
margin: 12px 10px 0 10px
|
||||
background: #f7f7f7
|
||||
max-height: rem(400px)
|
||||
overflow-y: auto
|
||||
|
||||
.subcategory-container
|
||||
&.hide
|
||||
display: none
|
||||
opacity: 0
|
||||
|
||||
& h2
|
||||
font-size: rem($font-size)
|
||||
font-weight: bold
|
||||
margin-bottom: 16px
|
||||
|
||||
.divider
|
||||
border-bottom: 1px solid #e5e5e5
|
||||
margin: rem(16px) 0
|
||||
|
||||
.search-tag__item
|
||||
background: #eaeaea;
|
||||
|
||||
.search
|
||||
width: 100%
|
||||
position: relative
|
||||
|
||||
@@ -2,7 +2,7 @@ import parser, { SearchParserResult } from '@hydrooj/utils/lib/search';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import DomainSelectAutoComplete from 'vj/components/autocomplete/DomainSelectAutoComplete';
|
||||
import { ActionDialog, ConfirmDialog } from 'vj/components/dialog';
|
||||
import { ActionDialog, ConfirmDialog, Dialog } from 'vj/components/dialog';
|
||||
import Dropdown from 'vj/components/dropdown/Dropdown';
|
||||
import createHint from 'vj/components/hint';
|
||||
import Notification from 'vj/components/notification';
|
||||
@@ -12,13 +12,19 @@ import {
|
||||
delay, i18n, pjax, request, tpl,
|
||||
} from 'vj/utils';
|
||||
|
||||
const categories = {};
|
||||
let selections: string[] = [];
|
||||
const list = [];
|
||||
const pinned: Record<string, string[]> = { category: [], difficulty: [] };
|
||||
const selections = { category: {}, difficulty: {} };
|
||||
const selectedTags: Record<string, string[]> = { category: [], difficulty: [] };
|
||||
|
||||
function setDomSelected($dom, selected) {
|
||||
if (selected) $dom.addClass('selected');
|
||||
else $dom.removeClass('selected');
|
||||
function setDomSelected($dom, selected, icon?) {
|
||||
if (selected) {
|
||||
$dom.addClass('selected');
|
||||
if (icon) $dom.append(icon);
|
||||
} else {
|
||||
$dom.removeClass('selected');
|
||||
if (icon) $dom.find('span').remove();
|
||||
}
|
||||
}
|
||||
|
||||
const parserOptions = {
|
||||
@@ -33,26 +39,40 @@ function writeSelectionToInput() {
|
||||
const parsedCurrentValue = parser.parse(currentValue, parserOptions) as SearchParserResult;
|
||||
const q = parser.stringify({
|
||||
...parsedCurrentValue,
|
||||
category: selections,
|
||||
category: selectedTags.category,
|
||||
difficulty: selectedTags.difficulty,
|
||||
text: parsedCurrentValue.text,
|
||||
}, parserOptions);
|
||||
$('[name="q"]').val(q);
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
selections = _.uniq(selections);
|
||||
for (const category in categories) {
|
||||
const item = categories[category];
|
||||
let childSelected = false;
|
||||
for (const subcategory in item.children) {
|
||||
const shouldSelect = selections.includes(subcategory);
|
||||
const isSelected = item.children[subcategory].$tag.hasClass('selected');
|
||||
childSelected ||= shouldSelect;
|
||||
if (isSelected !== shouldSelect) setDomSelected(item.children[subcategory].$tag, shouldSelect);
|
||||
selectedTags.category = _.uniq(selectedTags.category);
|
||||
for (const type in selections) {
|
||||
for (const selection in selections[type]) {
|
||||
const item = selections[type][selection];
|
||||
const shouldSelect = selectedTags[type].includes(selection);
|
||||
const isSelected = (item.$tag || item.$legacy).hasClass('selected');
|
||||
let childSelected = false;
|
||||
for (const subcategory in item.children) {
|
||||
const childShouldSelect = selectedTags[type].includes(subcategory);
|
||||
const childIsSelected = item.children[subcategory].$tag.hasClass('selected');
|
||||
childSelected ||= childShouldSelect;
|
||||
if (childIsSelected !== childShouldSelect) setDomSelected(item.children[subcategory].$tag, childShouldSelect);
|
||||
}
|
||||
if (item.$legacy) setDomSelected(item.$legacy, (shouldSelect || childSelected));
|
||||
if (isSelected !== shouldSelect) {
|
||||
if (pinned[type].includes(selection)) {
|
||||
setDomSelected(item.$tag, shouldSelect, '<span class="icon icon-check"></span>');
|
||||
} else {
|
||||
if (item.$tag) setDomSelected(item.$tag, shouldSelect, '<span class="icon icon-close"></span>');
|
||||
for (const $element of item.$phantom) {
|
||||
if (shouldSelect) $($element).removeClass('hide');
|
||||
else $($element).addClass('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const shouldSelect = selections.includes(category) || childSelected;
|
||||
const isSelected = item.$tag.hasClass('selected');
|
||||
if (isSelected !== shouldSelect) setDomSelected(item.$tag, shouldSelect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +85,22 @@ function loadQuery() {
|
||||
pjax.request({ url: url.toString() });
|
||||
}
|
||||
|
||||
function buildCategoryFilter() {
|
||||
function handleTagSelected(ev) {
|
||||
if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return;
|
||||
let [type, selection] = ['category', $(ev.currentTarget).text()];
|
||||
if ($(ev.currentTarget).attr('data-selection')) [type, selection] = $(ev.currentTarget).attr('data-selection').split(':');
|
||||
const category = $(ev.currentTarget).attr('data-category');
|
||||
const treeItem = category ? selections[type][category].children[selection] : selections[type][selection];
|
||||
const shouldSelect = !(treeItem.$tag || treeItem.$legacy).hasClass('selected');
|
||||
if (shouldSelect) selectedTags[type].push(selection);
|
||||
else selectedTags[type] = _.without(selectedTags[type], selection, ...(category ? [] : Object.keys(treeItem.children)));
|
||||
updateSelection();
|
||||
writeSelectionToInput();
|
||||
loadQuery();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function buildLegacyCategoryFilter() {
|
||||
const $container = $('[data-widget-cf-container]');
|
||||
if (!$container) return;
|
||||
$container.attr('class', 'widget--category-filter row small-up-3 medium-up-2');
|
||||
@@ -75,17 +110,22 @@ function buildCategoryFilter() {
|
||||
const $categoryTag = $category
|
||||
.find('.section__title a')
|
||||
.remove()
|
||||
.attr('class', 'widget--category-filter__category-tag');
|
||||
.attr('class', 'widget--category-filter__tag');
|
||||
const categoryText = $categoryTag.text();
|
||||
const $drop = $category
|
||||
.children('.chip-list')
|
||||
.remove()
|
||||
.attr('class', 'widget--category-filter__drop');
|
||||
const treeItem = {
|
||||
$tag: $categoryTag,
|
||||
children: {},
|
||||
};
|
||||
categories[categoryText] = treeItem;
|
||||
if (selections.category[categoryText]) {
|
||||
selections.category[categoryText].$legacy = $categoryTag;
|
||||
} else {
|
||||
selections.category[categoryText] = {
|
||||
$legacy: $categoryTag,
|
||||
$tag: null,
|
||||
children: {},
|
||||
$phantom: [],
|
||||
};
|
||||
}
|
||||
$category.empty().append($categoryTag);
|
||||
if ($drop.length > 0) {
|
||||
$categoryTag.text(`${$categoryTag.text()}`);
|
||||
@@ -93,11 +133,11 @@ function buildCategoryFilter() {
|
||||
.children('li')
|
||||
.attr('class', 'widget--category-filter__subcategory')
|
||||
.find('a')
|
||||
.attr('class', 'widget--category-filter__subcategory-tag')
|
||||
.attr('class', 'widget--category-filter__tag')
|
||||
.attr('data-category', categoryText);
|
||||
$subCategoryTags.get().forEach((subCategoryTag) => {
|
||||
const $tag = $(subCategoryTag);
|
||||
treeItem.children[$tag.text()] = {
|
||||
selections.category[categoryText].children[$tag.text()] = {
|
||||
$tag,
|
||||
};
|
||||
});
|
||||
@@ -107,40 +147,15 @@ function buildCategoryFilter() {
|
||||
});
|
||||
}
|
||||
});
|
||||
list.push(...Object.keys(categories));
|
||||
list.push(..._.flatMap(Object.values(categories), (c: any) => Object.keys(c.children)));
|
||||
$(document).on('click', '.widget--category-filter__category-tag', (ev) => {
|
||||
if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return;
|
||||
const category = $(ev.currentTarget).text();
|
||||
const treeItem = categories[category];
|
||||
const shouldSelect = !treeItem.$tag.hasClass('selected');
|
||||
if (shouldSelect) selections.push(category);
|
||||
else selections = _.without(selections, category, ...Object.keys(treeItem.children));
|
||||
updateSelection();
|
||||
writeSelectionToInput();
|
||||
loadQuery();
|
||||
ev.preventDefault();
|
||||
});
|
||||
$(document).on('click', '.widget--category-filter__subcategory-tag', (ev) => {
|
||||
if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return;
|
||||
const subcategory = $(ev.currentTarget).text();
|
||||
const category = $(ev.currentTarget).attr('data-category');
|
||||
const treeItem = categories[category].children[subcategory];
|
||||
const shouldSelect = !treeItem.$tag.hasClass('selected');
|
||||
if (shouldSelect) selections.push(subcategory);
|
||||
else selections = _.without(selections, subcategory);
|
||||
// TODO auto de-select parent
|
||||
updateSelection();
|
||||
writeSelectionToInput();
|
||||
loadQuery();
|
||||
ev.preventDefault();
|
||||
});
|
||||
list.push(...Object.keys(selections.category));
|
||||
list.push(..._.flatMap(Object.values(selections.category), (c: any) => Object.keys(c.children)));
|
||||
$(document).on('click', '.widget--category-filter__tag', (ev) => handleTagSelected(ev));
|
||||
}
|
||||
|
||||
function parseCategorySelection() {
|
||||
const parsed = parser.parse($('[name="q"]').val() as string || '', parserOptions) as SearchParserResult;
|
||||
selections = _.uniq(parsed.category || []);
|
||||
updateSelection();
|
||||
selectedTags.category = _.uniq(parsed.category || []);
|
||||
selectedTags.difficulty = _.uniq(parsed.difficulty || []);
|
||||
}
|
||||
|
||||
function ensureAndGetSelectedPids() {
|
||||
@@ -214,6 +229,54 @@ function hideTags(target) {
|
||||
.forEach((i) => $(i).addClass('notag--hide'));
|
||||
}
|
||||
|
||||
const categoryDialog: any = new Dialog({
|
||||
$body: $('.dialog--category-filter'),
|
||||
cancelByClickingBack: true,
|
||||
cancelByEsc: true,
|
||||
});
|
||||
|
||||
categoryDialog.clear = function () {
|
||||
const $dataCategoryContainer = this.$dom.find('[data-category-container]');
|
||||
$dataCategoryContainer.children().addClass('hide');
|
||||
const first = $dataCategoryContainer.children().first();
|
||||
setDomSelected(first, true);
|
||||
this.$dom.find('[data-subcategory-container]').addClass('hide');
|
||||
this.$dom.find(`[data-subcategory-container="${first.attr('data-category')}"]`).removeClass('hide');
|
||||
return this;
|
||||
};
|
||||
|
||||
function buildSearchContainer() {
|
||||
$('[data-pinned-container] [data-selection]').each((_index, _element) => {
|
||||
const [type, selection] = $(_element).attr('data-selection').split(':');
|
||||
pinned[type].push(selection);
|
||||
selections[type][selection] = {
|
||||
$tag: $(_element),
|
||||
children: {},
|
||||
};
|
||||
});
|
||||
|
||||
categoryDialog.$dom.find('.subcategory__all .search-tag__item').each((_index, _element) => {
|
||||
const [,subcategory] = $(_element).attr('data-selection').split(':');
|
||||
selections.category[subcategory] = {
|
||||
$tag: $(_element),
|
||||
children: {},
|
||||
$phantom: [
|
||||
...categoryDialog.$dom.find(`.subcategory__selected .search-tag__item[data-selection="category:${subcategory}"]`).get(),
|
||||
...$(`.subcategory-container__selected .search-tag__item[data-selection="category:${subcategory}"]`).get(),
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
$(document).on('click', '[data-category-container] [data-category]', (ev) => {
|
||||
$('[data-category-container] [data-category]').removeClass('selected');
|
||||
$(ev.currentTarget).addClass('selected');
|
||||
$('[data-subcategory-container]').addClass('hide');
|
||||
$(`[data-subcategory-container="${$(ev.currentTarget).attr('data-category')}"]`).removeClass('hide');
|
||||
});
|
||||
|
||||
$(document).on('click', '.search-tag__item', (ev) => handleTagSelected(ev));
|
||||
}
|
||||
|
||||
async function handleDownload(ev) {
|
||||
let name = 'Export';
|
||||
// eslint-disable-next-line no-alert
|
||||
@@ -231,7 +294,9 @@ const page = new NamedPage(['problem_main'], () => {
|
||||
const $body = $('body');
|
||||
$body.addClass('display-mode');
|
||||
$('.section.display-mode').removeClass('display-mode');
|
||||
buildCategoryFilter();
|
||||
|
||||
buildSearchContainer();
|
||||
buildLegacyCategoryFilter();
|
||||
parseCategorySelection();
|
||||
updateSelection();
|
||||
$(document).on('click', '[name="leave-edit-mode"]', () => {
|
||||
@@ -246,7 +311,7 @@ const page = new NamedPage(['problem_main'], () => {
|
||||
$(document).on('click', '[name="download_selected_problems"]', handleDownload);
|
||||
|
||||
$(document).on('click', '.toggle-tag', () => {
|
||||
$('.section__table-container').toggleClass('hide-problem-tag');
|
||||
$('.section__table-container').children().toggleClass('hide-problem-tag');
|
||||
});
|
||||
function inputChanged() {
|
||||
parseCategorySelection();
|
||||
@@ -259,6 +324,10 @@ const page = new NamedPage(['problem_main'], () => {
|
||||
});
|
||||
$('#searchForm').on('submit', inputChanged);
|
||||
$('#searchForm').find('input').on('input', _.debounce(inputChanged, 500));
|
||||
$('.dialog-button').on('click', (ev) => {
|
||||
categoryDialog.clear().open();
|
||||
ev.preventDefault();
|
||||
});
|
||||
$(document).on('click', 'a.pager__item', (ev) => {
|
||||
ev.preventDefault();
|
||||
pjax.request(ev.currentTarget.getAttribute('href')).then(() => window.scrollTo(0, 0));
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<ul class="group-list" data-widget-cf-container>
|
||||
{%- for category, sub_categories in model.system.get('problem.categories')|parseYaml -%}
|
||||
{% set _categories = (handler.domain.problemCategories or model.system.get('problem.categories'))|parseYaml %}
|
||||
{%- for category, sub_categories in _categories -%}
|
||||
<li class="group-list__item">
|
||||
<h2 class="section__title">
|
||||
<a href="{{ url('problem_main', query={q:'category:'+category}) }}" data-category="{{ category }}">{{ category }}</a>
|
||||
<a href="{{ url('problem_main', query={q:'category:'+category}) }}">{{ category }}</a>
|
||||
</h2>
|
||||
{% if sub_categories | length > 0 %}
|
||||
<ol class="chip-list">
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{% set _categories = (handler.domain.problemCategories or model.system.get('problem.categories'))|parseYaml %}
|
||||
<div class="dialog--category-filter">
|
||||
<div class="category-filter__header">
|
||||
<div class="list flex-row flex-cross-center" data-category-container>
|
||||
{%- for category, sub_categories in _categories -%}
|
||||
<div class="list__item" data-category="{{category}}">{{ category }}</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="category-filter__body">
|
||||
{%- for category, sub_categories in _categories -%}
|
||||
<div class="section__body subcategory-container hide" data-subcategory-container="{{ category }}">
|
||||
<div class="subcategory__selected">
|
||||
<h2>{{ _('Selected categories') }}: </h2>
|
||||
<div class="list flex-row flex-wrap">
|
||||
<div class="chip-list__item search-tag__item hide selected" data-selection="category:{{ category }}">
|
||||
{{ category }}
|
||||
<span class="icon icon-close"></span>
|
||||
</div>
|
||||
{%- for sub_category in sub_categories -%}
|
||||
<div class="chip-list__item search-tag__item hide selected" data-selection="category:{{ sub_category }}">
|
||||
{{ sub_category }}
|
||||
<span class="icon icon-close"></span>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="subcategory__all">
|
||||
<h2>{{ category }}: </h2>
|
||||
<div class="list flex-row flex-wrap">
|
||||
<div class="chip-list__item search-tag__item" data-selection="category:{{ category }}">{{ category }}</div>
|
||||
{%- for sub_category in sub_categories -%}
|
||||
<div class="chip-list__item search-tag__item" data-selection="category:{{ sub_category }}">{{ sub_category }}</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
<div class="section__body search-container">
|
||||
{% set pinnedFilter = handler.domain.pinnedFilter|parseYaml %}
|
||||
{% set categories = (handler.domain.problemCategories or model.system.get('problem.categories'))|parseYaml %}
|
||||
{% for k, v in pinnedFilter %}
|
||||
<div class="flex-row flex-cross-center">
|
||||
<b class="title">{{ k }}: </b>
|
||||
<div class="chip-list flex-wrap flex-row" data-pinned-container>
|
||||
{%- for name, t in v -%}
|
||||
<a class="chip-list__item search-tag__item" data-selection="{{ t }}">{{ name }}</a>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="flex-row flex-cross-center">
|
||||
<b class="title">{{ _('Problem Category') }}: </b>
|
||||
<div class="chip-list flex-wrap flex-row">
|
||||
<a class="chip-list__item typo-a dialog-button">{{ _('Select Category') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row flex-cross-center">
|
||||
<div class="subcategory-container__selected flex-row flex-wrap chip-list">
|
||||
{%- for category, sub_categories in categories -%}
|
||||
<div class="chip-list__item search-tag__item search-category__item hide selected" data-selection="category:{{ category }}">
|
||||
{{ category }}
|
||||
<span class="icon icon-close"></span>
|
||||
</div>
|
||||
{%- for sub_category in sub_categories -%}
|
||||
<div class="chip-list__item search-tag__item search-category__item hide selected" data-selection="category:{{ sub_category }}">
|
||||
{{ sub_category }}
|
||||
<span class="icon icon-close"></span>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "partials/problem_category_dialog.html" %}
|
||||
@@ -15,6 +15,11 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% if handler.domain.pinnedFilter %}
|
||||
{% include "partials/problem_search.html" %}
|
||||
{% endif %}
|
||||
<div class="section__body no-padding">
|
||||
{% include "partials/problem_list.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user