diff --git a/apps/web/app/components/RiskIndicators.tsx b/apps/web/app/components/RiskIndicators.tsx index 59e1568a..28cecba3 100644 --- a/apps/web/app/components/RiskIndicators.tsx +++ b/apps/web/app/components/RiskIndicators.tsx @@ -1,7 +1,13 @@ import type { ContractDetail } from '@sigma/api-contract'; +import { Link } from 'react-router'; import { pct } from '@sigma/shared'; import { evaluateRiskIndicators } from '../lib/riskLogic'; +// The per-contract risk signals. Copy is deliberately non-accusatory (#219): each item states the +// structural fact and frames it as a signal that warrants a look, never as proven wrongdoing. The +// note below repeats the disclaimer next to the flags and links to the full methodology, so the +// caveat travels with the flags wherever they appear (issue #219 co-requisite of the risk numbers). +// Labels mirror methodology §10 and the homepage FLAG_LABELS for a consistent vocabulary. export function RiskIndicators({ contract }: { contract: ContractDetail }) { const flags = evaluateRiskIndicators(contract); @@ -27,22 +33,23 @@ export function RiskIndicators({ contract }: { contract: ContractDetail }) { - Индикатори за риск + Сигнали за риск +

+ Сигналите са ориентири за преглед, не присъда — отбелязват договор, който заслужава + внимание, а не доказано нарушение.{' '} + Как четем сигналите → + {' · '} + Смятате сигнал за грешен? → +

); } diff --git a/apps/web/app/routes/contract.tsx b/apps/web/app/routes/contract.tsx index f3e393c5..8eb1175b 100644 --- a/apps/web/app/routes/contract.tsx +++ b/apps/web/app/routes/contract.tsx @@ -1,5 +1,14 @@ import { Link } from 'react-router'; -import { count, longDate, money, moneyBare, plural, signedMoney, signedPct } from '@sigma/shared'; +import { + count, + isNaturalPersonProfileName, + longDate, + money, + moneyBare, + plural, + signedMoney, + signedPct, +} from '@sigma/shared'; import { contractIdFromSlug, contractSlug, getContract, getDb } from '@sigma/db'; import type { CohortBand, ContractDetail } from '@sigma/api-contract'; import type { Route } from './+types/contract'; @@ -71,7 +80,7 @@ function AnnexDescription({ text }: { text: string | null }) { export function meta({ data, params, matches }: Route.MetaArgs) { const c = data?.contract; - return seoMeta({ + const tags = seoMeta({ matches, path: `/contracts/${contractSlug(contractIdFromSlug(params.id))}`, title: `${c?.subject ?? 'Договор'} — СИГМА`, @@ -79,6 +88,15 @@ export function meta({ data, params, matches }: Route.MetaArgs) { ? `Договор по УНП ${c.unp} между ${c.authority.name} и ${c.bidder.displayName}.` : '', }); + // GDPR/ЗЗЛД (#219 review): when the bidder is a sole-trader (ЕТ) / natural person, keep this contract + // page — which carries the „Сигнали за риск" box — out of search indexes, mirroring the noindex on + // sole-trader company profiles (company.tsx) and their exclusion from the sitemap. The contract stays + // fully public on the site; only search-engine amplification of a named individual + risk label is + // avoided. + if (c && isNaturalPersonProfileName(c.bidder.displayName)) { + tags.push({ name: 'robots', content: 'noindex' }); + } + return tags; } export function headers() { diff --git a/apps/web/app/routes/methodology.tsx b/apps/web/app/routes/methodology.tsx index 2b4aa857..7d5cd791 100644 --- a/apps/web/app/routes/methodology.tsx +++ b/apps/web/app/routes/methodology.tsx @@ -37,6 +37,7 @@ const TOC = [ ['identity', 'Имена, ЕИК, УНП'], ['gaps', 'Известни празнини в полетата'], ['export', 'Сваляне и достъп до данните'], + ['flagged', 'Сигнали за риск'], ['contact', 'Поправки и обратна връзка'], ]; @@ -143,8 +144,10 @@ export default function Methodology({ loaderData }: Route.ComponentProps) {

- СИГМА е изцяло само за четене: не въвежда нови данни, не оценява процедурите и не - маркира фирми като рискови. + СИГМА е изцяло само за четене: не въвежда нови данни и не оценява процедурите. Тя + отбелязва договори със структурни сигнали за преглед (виж{' '} + раздел 10), но не обявява фирми или възложители за нарушители + — сигналът е ориентир, не присъда.

@@ -530,8 +533,62 @@ export default function Methodology({ loaderData }: Route.ComponentProps) {

Засега не предлагаме публично API в реално време.

+
+

10. Сигнали за риск

+

+ На страницата на всеки договор СИГМА отбелязва{' '} + структурни сигнали за риск, когато поне един признак заслужава + преглед. Сигналите са ориентири, не присъда: те не твърдят нарушение, а + насочват към договори, които си струва да се погледнат по-внимателно. +

+

Отбелязваме четири признака:

+
+
Липса на конкуренция
+
+

+ Допусната е една-единствена оферта (приети оферти = 1), без финансиране от ЕС. +

+ → приети оферти = 1 +
+
Липса на конкуренция (със средства от ЕС)
+
+

+ Същият признак, но при финансиране от ЕС, където правилата за конкуренция са + по-строги — затова го отделяме. +

+ → приети оферти = 1 · финансиране от ЕС +
+
Ръст на стойността чрез анекси
+
+

+ Текущата стойност е нараснала с над 20% спрямо стойността при подписване — + индикатор за оскъпяване след сключването. +

+ → текуща стойност > 1.2 × стойност при подписване +
+
Стойностна или времева аномалия
+
+

+ Стойност с непотвърдена достоверност (виж речника) или договор, подписан повече + от два дни след датата си на публикуване. Двудневният допуск поема обичайното + забавяне при въвеждане; отвъд него редът на датите остава необяснен от + източника. +

+ → value_flag / date_flag +
+
+

+ Ограничения. Сигналите стъпват на публични данни и структурни + признаци, не на разследване. Възможни са грешки или празнини в източника (виж{' '} + раздел 8), затова един сигнал може да е фалшив — например + законна процедура с една оферта по обективни причини. Сигнал не е констатация за + нарушение и не оценява поведението на конкретно лице. Ако смятате конкретен сигнал + за неточен, пишете ни (раздел 11). +

+
+
-

10. Поправки и обратна връзка

+

11. Поправки и обратна връзка

Грешките поправяме ръчно при сигнал — двойни записи за институция/компания (изпратете двата ЕИК/линка) или сума, която не отговаря на оригиналния документ diff --git a/apps/web/app/styles/pages.css b/apps/web/app/styles/pages.css index fd6341d3..b317a413 100644 --- a/apps/web/app/styles/pages.css +++ b/apps/web/app/styles/pages.css @@ -102,6 +102,15 @@ .risk-list li:last-child { margin-bottom: 0; } +/* Non-accusatory disclaimer that travels with the per-contract flags (#219). */ +.risk-note { + margin: var(--s-4) 0 0; + padding-top: var(--s-3); + border-top: 1px solid var(--warning); +} +.risk-note a { + color: var(--warning-strong); +} /* Value-history strip + current lot row */ .value-history {