diff --git a/CHANGELOG.md b/CHANGELOG.md index b4d169a..7549aaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## Не выпущено +- Frontend `0.2.6` / Backend `0.13.1`: двухпанельная компоновка поиска + сохраняется до ширины 1175 px, чтобы три панели не обрезались при + промежуточных ширинах окна. +- Frontend `0.2.5` / Backend `0.13.1`: устранены промежуточные переполнения + навигации, старый текст больше не остаётся при ошибке загрузки другого + документа; очищаются дополнительные URL-атрибуты юридической разметки, + включая `srcset` и `ping`. Добавлены fixture-проверки ошибок и гонок загрузки, + изоляции ссылок, reflow и безопасных форм управления пользователями. +- Frontend `0.2.4` / Backend `0.13.1`: исправлены состояния и адаптивный поиск, + чтение документа и лаборатории, доступные диалоги; управление пользователями + вынесено на отдельный защищённый экран. Документная разметка очищается от + активного HTML и небезопасных ссылок. Добавлены проверки маршрутов и reflow; + правовой текст и API управления пользователями не менялись. - Frontend `0.2.3`: добавлен публичный поиск по правовой базе с фильтрами, адаптивной выдачей, просмотром редакций и поиском по тексту документа; внутренняя лаборатория сохранена, её подсказки стали компактнее и привязаны diff --git a/DESIGN.md b/DESIGN.md index 54ac9e7..49d4edc 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -92,7 +92,7 @@ spacing: до реализации; утверждённые токены не заменяются цветами, взятыми пипеткой из растрового изображения. Изменения постоянных правил вносятся сюда явно. Этапы работ, исходные измерения и критерии визуальной приёмки находятся в -[плане точного воспроизведения поискового интерфейса](docs/product/frontend-pixel-perfect-plan.md). +[едином плане корректировки UI/UX](docs/product/interface-correction-plan.md). План не является второй дизайн-системой и не означает завершение реализации. **Key Characteristics:** diff --git a/backend/search/api.py b/backend/search/api.py index 46ee5a6..bea9111 100644 --- a/backend/search/api.py +++ b/backend/search/api.py @@ -395,7 +395,7 @@ def handler(api: Api): session = cookies.get("akyldash_session") token = session.value if session else None user = api.auth_store.user_for_token(token) - if method == "GET" and route in {"/", "/review"}: + if method == "GET" and route in {"/", "/review", "/users"}: body = api.review_page().encode() self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") diff --git a/backend/search/minjust_opensearch.py b/backend/search/minjust_opensearch.py index 10b7049..807ef09 100644 --- a/backend/search/minjust_opensearch.py +++ b/backend/search/minjust_opensearch.py @@ -17,7 +17,7 @@ from typing import Iterator from search.catalog import authority_codes, legal_force_code, source_code -APP_VERSION = "0.13.0" +APP_VERSION = "0.13.1" LANGUAGES = {"ru", "ky"} DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json") @@ -465,7 +465,7 @@ def main() -> int: else: documents, fragments = export_bulk(arguments.input, arguments.output, arguments.index, arguments.limit) destination = arguments.output - print(f"documents={documents} fragments={fragments} destination={destination}\nAkyldash · Backend v{APP_VERSION} · Frontend v0.1.2") + print(f"documents={documents} fragments={fragments} destination={destination}\nAkyldash · Backend v{APP_VERSION} · Frontend v0.2.6") return 0 diff --git a/backend/search/review.html b/backend/search/review.html index ba7de2d..6a95198 100644 --- a/backend/search/review.html +++ b/backend/search/review.html @@ -148,6 +148,130 @@ @media (max-width: 480px) { .review__search { grid-template-columns: 1fr; } .review__search .review__field:first-child, .review__search button { grid-column: auto; } .review__rating { grid-template-columns: repeat(2, minmax(0, 1fr)); } .search__query-row { grid-template-columns: 1fr 1fr; } .search__query-row > input { grid-column: 1 / -1; } .review__nav { align-items: start; flex-wrap: wrap; } .review__nav-links { order: 3; width: 100%; flex-wrap: wrap; } .review__nav-account { margin-inline-start: auto; } } @media (hover: hover) { button:hover { background: var(--surface-muted); border-color: var(--muted); } .review__button--primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); } .review__result:hover { background: var(--subtle); } .review__result-title:hover, .search__result-title:hover { background: none; color: var(--brand); } .review__rating label:hover { border-color: var(--brand); } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; } } + /* Route-specific layout keeps reading and review controls in view without nested page shells. */ + body:has(#review:not([hidden])) { min-height: 100dvh; display: flex; flex-direction: column; } + body:has(#review:not([hidden])) > .review__header { width: 100%; } + body:has(#review:not([hidden])) > #review { width: min(100%, 1600px); flex: 1; } + body:has(#review:not([hidden])) > .search__footer { display: none; } + body:has(#review:not([hidden])) > .review__actions { width: min(100%, 1600px); margin: auto auto 0; } + body:has(#review:not([hidden])) > footer.review__header { width: 100%; margin-block-start: 1rem; } + .review__header-content, .review__actions-inner { max-width: 1600px; } + .review__header { padding-block-end: .5rem; } + .review__header-content h1 { margin-block: .6rem .25rem; } + .review__workspace { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); } + .review__document { position: static; max-height: min(68dvh, 860px); overflow: auto; overscroll-behavior: contain; } + .review__document-body { max-width: 72ch; margin-inline: auto; } + .review__document-body table { display: block; max-width: 100%; overflow-x: auto; } + .review__document-body p:empty { display: none; } + .review__document-body a { color: var(--brand-dark); text-underline-offset: .18em; } + .search__layout[data-filters-hidden="true"] { grid-template-columns: minmax(320px, .8fr) minmax(0, 1.5fr); } + .search__layout[data-filters-hidden="true"] .search__filters { display: none; } + .search__layout.is-expanded { position: fixed; z-index: 15; inset: 0; display: block; overflow: auto; background: var(--surface); } + .search__layout.is-expanded .search__filters, .search__layout.is-expanded .search__results { display: none; } + .search__layout.is-expanded .search__document { min-height: 100%; padding: 24px clamp(16px, 6vw, 96px); } + .search__layout.is-expanded .search__document-body { max-width: 78ch; margin-inline: auto; } + .search__toolbar-actions { display: flex; align-items: center; gap: .5rem; } + .search__layout:not([data-has-editions="true"]) .search__document-toolbar { display: none; } + .search__layout:not([data-has-document="true"]) .search__find, + .search__layout:not([data-has-document="true"]) #document-expand, + .search__layout:not([data-has-document="true"]) #document-print, + .search__layout:not([data-has-document="true"]) .search__toc, + .search__layout:not([data-has-document="true"]) #document-source { display: none; } + .search__layout:not([data-has-document="true"]) .search__document-body { display: none; } + .search__mobile-filters { display: inline-flex; } + .search__account button { display: inline-flex; width: auto; min-height: 44px; gap: 8px; align-items: center; padding: 8px 12px; } + .search__account-label { display: inline; } + .search__document-body table, .review__document-body table { display: block; max-width: 100%; overflow-x: auto; } + .search__table-scroll { max-width: 100%; margin-block: 1rem; overflow-x: auto; overscroll-behavior-inline: contain; } + .search__table-scroll table { width: max-content; min-width: 100%; border-collapse: collapse; } + .search__filters .review__panel-heading { align-items: center; flex-wrap: wrap; } + .search__filters .review__panel-heading h2 { flex: 1 1 auto; } + .search__filters .review__panel-heading button { flex: 0 0 auto; } + .search__filter label { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; align-items: center; gap: 8px; min-height: 40px; } + .search__filter label span { min-width: 0; overflow-wrap: anywhere; } + .search__filter-count { color: var(--muted); font-size: .875rem; font-variant-numeric: tabular-nums; white-space: nowrap; } + .search__filter-more { margin-block-start: .4rem; } + .search__filter-more summary { min-height: 40px; display: flex; align-items: center; color: var(--brand-dark); cursor: pointer; } + .search__language-chip select { width: auto; min-width: 126px; max-width: none; } + .search__document-toolbar select { width: auto; min-width: 160px; max-width: min(280px, 100%); } + .search__document-toolbar .search__edition-label select { width: auto; min-width: 220px; max-width: min(360px, 100%); } + .search__source-link { max-width: none; white-space: normal; } + .review__rating { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .review__rating label { justify-items: start; text-align: start; padding-inline: .6rem; } + .review__comment-disclosure { margin-block: .75rem; } + .review__comment-disclosure summary { color: var(--brand-dark); cursor: pointer; } + .review__actions-inner { align-items: end; } + #save:disabled { cursor: not-allowed; } + dialog { width: min(640px, calc(100vw - 32px)); max-height: min(88dvh, 900px); overflow: auto; padding: 0; } + dialog > .review__page, dialog > section { max-width: none; padding: 24px; } + dialog h2 { margin: 0; padding-inline-end: 52px; } + .review__dialog-heading { position: sticky; z-index: 1; inset-block-start: -24px; display: flex; justify-content: space-between; align-items: start; gap: 1rem; margin: -24px -24px 16px; padding: 18px 24px; background: var(--surface); border-block-end: 1px solid var(--line); } + .review__dialog-heading h2 { margin: 0; font: 600 1.5rem/1.3 var(--serif); } + .review__dialog-heading button { flex: 0 0 44px; width: 44px; min-height: 44px; padding: 0; } + .review__dialog-heading button svg { width: 20px; height: 20px; } + body.is-users-route { min-height: 100dvh; display: flex; flex-direction: column; } + body.is-users-route > .review__header { display: block; width: 100%; padding-block-end: 0; } + body.is-users-route > .review__header > .review__header-content { display: none; } + body.is-users-route > footer.review__header { display: block; margin-block-start: auto; } + .review__users-page { width: min(1200px, 100%); flex: 1; margin-inline: auto; padding: 28px clamp(16px, 4vw, 48px); } + .review__users-page[hidden] { display: none !important; } + .review__users-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-block-end: 24px; } + .review__user-table-head, .review__user-row { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr) minmax(0, 1.2fr) minmax(0, 1.2fr) minmax(180px, 1.5fr); align-items: start; gap: 12px; } + .review__user-table-head { padding: 12px; color: var(--muted); font-size: .875rem; border-block-end: 1px solid var(--line); } + .review__user-list { list-style: none; margin: 0; padding: 0; } + .review__user-row { padding: 16px 12px; border-block-end: 1px solid var(--line); } + .review__user-row > * { min-width: 0; overflow-wrap: anywhere; } + .review__user-row details { grid-column: 1 / -1; } + .review__user-row details summary { min-height: 40px; display: flex; align-items: center; color: var(--brand-dark); cursor: pointer; } + .review__user-actions { display: flex; flex-wrap: wrap; gap: .5rem; } + .review__account-tabs { display: flex; gap: .5rem; margin-block: 1rem; border-block-end: 1px solid var(--line); } + .review__account-tabs [aria-selected="true"] { color: var(--brand-dark); border-color: var(--brand); background: var(--subtle); } + @media (max-width: 1100px) { + .search__layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); } + .search__layout:not([data-filters-hidden="true"]) .search__filters { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; border-inline-end: 0; border-block-end: 1px solid var(--line); } + .search__layout:not([data-filters-hidden="true"]) .search__filters > .review__panel-heading, + .search__layout:not([data-filters-hidden="true"]) .search__filters > .search__dates, + .search__layout:not([data-filters-hidden="true"]) .search__filters > .search__date-label, + .search__layout:not([data-filters-hidden="true"]) .search__filters > #filter-fields { grid-column: 1 / -1; } + .search__filters .search__ornament { display: none; } + } + @media (max-width: 900px) { + body:has(.search:not([hidden])) { height: auto; min-height: 100dvh; overflow: visible; } + .review__workspace { grid-template-columns: minmax(0, 1fr); } + .review__document { max-height: 62dvh; } + .search__layout:not([data-filters-hidden="true"]) .search__filters { display: none; } + .search__layout:not([data-filters-hidden="true"]) .search__filters.is-open { display: block; } + } + @media (max-width: 600px) { + .review__users-heading { align-items: stretch; flex-direction: column; } + .review__user-table-head { display: none; } + .review__user-row { grid-template-columns: minmax(0, 1fr); gap: 8px; } + .review__user-row > [data-label]::before { content: attr(data-label) ": "; color: var(--muted); font-weight: 600; } + .review__user-row .review__user-actions { grid-column: 1; } + .review__nav { flex-wrap: wrap; } + .review__nav-links { flex-wrap: wrap; } + .review__nav-links a, .review__nav-links button { min-height: 44px; } + .search__topbar { grid-template-columns: minmax(0, 1fr) auto; } + .search__account { grid-column: auto; grid-row: 1; } + .search__account button { width: 48px; min-height: 48px; } + .search__account-label { display: none; } + .search__subnav { flex-wrap: wrap; } + .search__language-chip select { min-width: 126px; } + .search__layout { display: block; } + .search__filters { overflow: visible; } + .search__filter label { grid-template-columns: 24px minmax(0, 1fr) auto; } + .search__dates { grid-template-columns: minmax(0, 1fr); } + .search__document-toolbar { align-items: stretch; } + .search__document-toolbar > *, .search__document-toolbar label { max-width: 100%; } + .search__document-toolbar .search__edition-label select { min-width: min(220px, calc(100vw - 48px)); } + .search__find { grid-template-columns: minmax(0, 1fr) auto auto; } + .search__find input { grid-column: 1 / -1; } + .search__find label { grid-column: 1 / -1; } + .review__actions-inner { display: grid; grid-template-columns: minmax(0, 1fr); } + dialog { max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px); } + dialog > .review__page, dialog > section { padding: 16px; } + .review__dialog-heading { inset-block-start: -16px; margin: -16px -16px 12px; padding: 12px 16px; } + } :root { --color-bg: #F7F2E8; --color-surface: #fff; --color-surface-muted: #FBF8F4; --color-text: #38262A; --color-text-secondary: #786469; --color-border: #DED3CE; --color-primary: #9D2633; --color-primary-hover: #84202B; --color-accent: #A37B32; --color-highlight: #FFF0C7; --color-focus: #245C78; --color-success: #246843; --search-header-height: 76px; --search-nav-height: 50px; } body:has(.search:not([hidden])) { width: 100%; height: 100dvh; min-height: 0; overflow: hidden; display: flex; flex-direction: column; } body:has(.search:not([hidden])) > .review__header { display: none; } @@ -207,6 +331,7 @@ .search__filter input[type="radio"]:checked::after { content: ''; position: absolute; width: 6px; height: 11px; inset: 3px 7px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); } .search__filter--authority { display: grid; gap: 5px; margin-block: 0 20px; } .search__filter--authority label { display: grid; min-height: auto; gap: 5px; font-weight: 600; } + .search__filter--authority label { grid-template-columns: minmax(0, 1fr); } .search__filter--authority select { min-height: 42px; padding-block: 7px; font-weight: 400; } .search__date-label { display: block; margin-block: 0 7px; font-size: 16px; font-weight: 600; } .search__dates { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } @@ -230,6 +355,7 @@ .search__result-snippet { margin: 6px 0 0; color: var(--ink); font-size: 15px; line-height: 1.45; } .search__result-snippet mark, .search__document-body mark { color: inherit; background: var(--color-highlight); padding: 0 2px; } .search__document-body mark.is-current { outline: 2px solid var(--color-accent); outline-offset: 1px; } + .search__document-body a { color: var(--brand-dark); text-underline-offset: .18em; } .search__result em { font-style: normal; } .search__pagination { padding-block-start: 12px; } .search__pagination button { min-height: 40px; padding: 6px 12px; } @@ -336,14 +462,38 @@ .search, .search__layout, .search__document { display: block; width: auto; min-height: 0; overflow: visible; border: 0; padding: 0; } .search__document-body { max-width: none; } } + /* Route visibility must win over broad legacy :has() layout selectors. */ + body.is-users-route { width: 100%; height: auto; min-height: 100dvh; overflow: visible; display: flex; flex-direction: column; } + body.is-users-route > header.review__header { display: block; width: 100%; padding-block-end: 0; } + body.is-users-route > header.review__header > .review__header-content { display: none; } + body.is-users-route > footer.review__header { display: block; width: 100%; margin-block-start: auto; } + .search__layout { grid-template-columns: clamp(256px, 19.45vw, 280px) clamp(360px, 30vw, 408px) minmax(560px, 1fr); } + .search__account button { width: auto; min-width: 44px; min-height: 44px; padding: 8px 12px; } + @media (min-width: 901px) and (max-width: 1175px) { + .search__layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); } + .search__layout:not([data-filters-hidden="true"]) .search__filters { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; } + .search__layout:not([data-filters-hidden="true"]) .search__filters > .review__panel-heading, + .search__layout:not([data-filters-hidden="true"]) .search__filters > .search__dates, + .search__layout:not([data-filters-hidden="true"]) .search__filters > .search__date-label, + .search__layout:not([data-filters-hidden="true"]) .search__filters > #filter-fields { grid-column: 1 / -1; } + } + @media (max-width: 600px) { .search__account button { width: 48px; padding: 8px; } } + @media (min-width: 601px) and (max-width: 700px) { + .search__subnav { flex-wrap: wrap; overflow-x: visible; white-space: normal; } + } + @media (min-width: 481px) and (max-width: 800px) { + .review__nav { flex-wrap: wrap; } + .review__nav-links { order: 3; width: 100%; flex-wrap: wrap; } + .review__nav-account { margin-inline-start: auto; } + }
Перейти к результатамСопоставляйте запрос с текстом документов и сохраняйте оценку выдачи.
Сопоставляйте запрос с текстом документов и сохраняйте оценку выдачи.
Черновик появится после поиска.
Права на действия определяются сервером.