2026-05-14 14:13:52 +03:00
< script setup lang = "ts" >
2026-05-14 14:41:28 +03:00
import { ref , reactive , computed , onMounted , onBeforeUnmount , watch } from 'vue' ;
import axios from 'axios' ;
2026-05-14 14:13:52 +03:00
import type { Project } from '../../stores/projectsStore' ;
2026-05-14 14:48:24 +03:00
import { useProjectsStore } from '../../stores/projectsStore' ;
2026-05-15 05:54:05 +03:00
import { REGIONS , FEDERAL _DISTRICT _NAMES } from '../../constants/regions' ;
2026-06-25 19:40:04 +03:00
import { formatLeadDate , firstLeadDate } from '../../utils/leadDate' ;
2026-05-14 14:13:52 +03:00
2026-05-14 14:21:07 +03:00
const props = defineProps < { project : Project | null } > ( ) ;
2026-05-28 09:52:42 +03:00
const emit = defineEmits < { close : [ ] ; saved : [ appliesFrom : string | null ] } > ( ) ;
2026-05-14 14:21:07 +03:00
interface FormState {
name : string ;
daily _limit _target : number ;
2026-05-15 05:54:05 +03:00
regions : number [ ] ;
2026-05-14 14:21:07 +03:00
delivery _days _mask : number ;
sms _senders : string [ ] ;
sms _keyword : string ;
2026-05-18 15:43:28 +03:00
signal _identifier : string ;
2026-05-14 14:21:07 +03:00
}
const form = reactive < FormState > ( {
name : '' ,
daily _limit _target : 50 ,
2026-05-15 05:54:05 +03:00
regions : [ ] ,
2026-05-14 14:21:07 +03:00
delivery _days _mask : 127 ,
sms _senders : [ ] ,
sms _keyword : '' ,
2026-05-18 15:43:28 +03:00
signal _identifier : '' ,
2026-05-14 14:21:07 +03:00
} ) ;
2026-05-14 17:51:56 +03:00
const selectableRegions = REGIONS . filter ( ( r ) => r . code !== 0 ) ;
2026-05-14 14:21:07 +03:00
function reseedFromProject ( p : Project | null ) : void {
if ( ! p ) return ;
form . name = p . name ;
form . daily _limit _target = p . daily _limit _target ;
2026-05-15 05:54:05 +03:00
form . regions = Array . isArray ( p . regions ) ? [ ... p . regions ] : [ ] ;
2026-05-14 14:21:07 +03:00
form . delivery _days _mask = p . delivery _days _mask ? ? 127 ;
form . sms _senders = p . sms _senders ? ? [ ] ;
form . sms _keyword = p . sms _keyword ? ? '' ;
2026-05-18 15:43:28 +03:00
form . signal _identifier = p . signal _identifier ? ? '' ;
2026-05-14 14:21:07 +03:00
}
reseedFromProject ( props . project ) ;
2026-05-15 05:54:05 +03:00
watch (
( ) => props . project ? . id ,
( ) => {
reseedFromProject ( props . project ) ;
} ,
) ;
2026-05-14 17:51:56 +03:00
2026-05-14 14:41:28 +03:00
const saving = ref ( false ) ;
const errors = reactive < Record < string , string [ ] > > ( { } ) ;
2026-05-14 14:48:24 +03:00
const store = useProjectsStore ( ) ;
2026-06-25 17:48:34 +03:00
// Эпик 3 (редизайн 2026-06-25): вместо жёсткого замка — редактируемые поля
// + объявление о вступлении + подтверждение смены источника. Раздача доводит
// хвост по старому источнику через слепок, поэтому смена безопасна.
2026-06-22 20:59:02 +03:00
const sourceLocked = computed ( ( ) => props . project ? . source _locked === true ) ;
2026-06-25 17:48:34 +03:00
// Информационный баннер: пока летит сбор на завтра, правки количества/региона/дней
// вступят в силу не сразу. Показываем, когда проект залочен И эти поля изменены.
const slepokFieldsDirty = computed ( ( ) => {
const p = props . project ;
if ( ! p ) return false ;
const regionsChanged = JSON . stringify ( [ ... form . regions ] . sort ( ) ) !== JSON . stringify ( [ ... ( p . regions ? ? [ ] ) ] . sort ( ) ) ;
return (
form . daily _limit _target !== p . daily _limit _target ||
form . delivery _days _mask !== ( p . delivery _days _mask ? ? 127 ) ||
regionsChanged
) ;
} ) ;
const showAppliesFromBanner = computed ( ( ) => sourceLocked . value && slepokFieldsDirty . value ) ;
2026-06-25 19:40:04 +03:00
// Time-aware дата вступления правок по правилу слепка 18:00 МСК (до 18:00 → завтра,
// после → послезавтра). Объявление всегда показывает АКТУАЛЬНУЮ дату.
const appliesFromDate = computed ( ( ) => firstLeadDate ( ) ) ;
2026-06-25 17:48:34 +03:00
// Изменён ли источник относительно исходного состояния проекта.
const sourceDirty = computed ( ( ) => {
const p = props . project ;
if ( ! p ) return false ;
if ( p . signal _type === 'sms' ) {
const sendersChanged = JSON . stringify ( form . sms _senders ) !== JSON . stringify ( p . sms _senders ? ? [ ] ) ;
return sendersChanged || form . sms _keyword !== ( p . sms _keyword ? ? '' ) ;
2026-06-22 20:59:02 +03:00
}
2026-06-25 17:48:34 +03:00
return form . signal _identifier !== ( p . signal _identifier ? ? '' ) ;
} ) ;
// Диалог подтверждения смены источника на залоченном проекте.
const sourceConfirmOpen = ref ( false ) ;
const sourceConfirmText = computed ( ( ) => {
2026-06-25 19:08:47 +03:00
// Эпик 6.3: единый текст правила из API (ProjectRuleMessages). Fallback — локальный.
const fromApi = props . project ? . source _change _message ;
if ( fromApi ) {
return ` Мы уже ведём сбор на завтра. ${ fromApi } Подтвердите смену источника. ` ;
}
2026-06-25 17:48:34 +03:00
const d = formatLeadDate ( props . project ? . source _unlock _at ) ;
return (
'Мы уже ведём сбор на завтра. Лиды по старому источнику придут завтра в любом случае. ' +
( d ? ` Послезавтра (после ${ d } ) — уже по новому. ` : '' ) +
'Подтвердите смену источника.'
) ;
2026-06-22 20:59:02 +03:00
} ) ;
2026-05-14 14:48:24 +03:00
async function onPause ( ) : Promise < void > {
if ( ! props . project ) return ;
await store . toggleActive ( props . project ) ;
2026-05-22 18:50:04 +03:00
// #4: после паузы/возобновления панель и галочка должны исчезнуть (как у Save и Delete).
emit ( 'close' ) ;
2026-05-14 14:48:24 +03:00
}
2026-05-14 14:54:55 +03:00
async function onDelete ( ) : Promise < void > {
if ( ! props . project ) return ;
2026-05-15 05:54:05 +03:00
const ok = window . confirm (
2026-05-26 12:33:18 +03:00
'Удалить проект? Действие необратимо. Если по проекту есть сделки или поставщик уже заказал лиды — удаление будет заблокировано.' ,
2026-05-15 05:54:05 +03:00
) ;
2026-05-14 14:54:55 +03:00
if ( ! ok ) return ;
2026-05-26 12:33:18 +03:00
Object . keys ( errors ) . forEach ( ( k ) => delete errors [ k ] ) ;
try {
await store . del ( props . project . id ) ;
emit ( 'close' ) ;
} catch ( e : unknown ) {
const err = e as { response ? : { status ? : number ; data ? : { errors ? : Record < string , string [ ] > } } } ;
if ( err . response ? . status === 422 && err . response . data ? . errors ) {
Object . assign ( errors , err . response . data . errors ) ;
}
// НЕ закрываем drawer — клиент видит ошибку и может поставить проект на паузу.
}
2026-05-14 14:54:55 +03:00
}
2026-05-14 14:41:28 +03:00
async function onSave ( ) : Promise < void > {
2026-06-25 17:48:34 +03:00
if ( ! props . project ) return ;
// Эпик 3.2: смена источника на залоченном проекте — через подтверждение.
if ( sourceLocked . value && sourceDirty . value ) {
sourceConfirmOpen . value = true ;
return ;
}
await doSave ( ) ;
}
function cancelSourceChange ( ) : void {
sourceConfirmOpen . value = false ;
}
async function confirmSourceChange ( ) : Promise < void > {
sourceConfirmOpen . value = false ;
await doSave ( ) ;
}
async function doSave ( ) : Promise < void > {
2026-05-14 14:41:28 +03:00
if ( ! props . project ) return ;
saving . value = true ;
Object . keys ( errors ) . forEach ( ( k ) => delete errors [ k ] ) ;
try {
const payload : Record < string , unknown > = {
name : form . name ,
daily _limit _target : form . daily _limit _target ,
2026-05-15 05:54:05 +03:00
regions : form . regions ,
2026-05-14 14:41:28 +03:00
delivery _days _mask : form . delivery _days _mask ,
} ;
2026-05-18 15:43:28 +03:00
// 18.05.2026 ux: редактирование источника проекта.
if ( props . project . signal _type === 'site' || props . project . signal _type === 'call' ) {
payload . signal _identifier = form . signal _identifier ;
}
2026-05-14 14:41:28 +03:00
if ( props . project . signal _type === 'sms' ) {
payload . sms _senders = form . sms _senders ;
payload . sms _keyword = form . sms _keyword ;
}
2026-05-28 09:52:42 +03:00
const { data } = await axios . patch ( ` /api/projects/ ${ props . project . id } ` , payload ) ;
// Backend кладёт applies_from когда правка задела slepok-чувствительные поля
// (см. ProjectService::updateAndExposeAppliesFrom / Task 2.8).
const appliesFrom : string | null = data ? . data ? . applies _from ? ? null ;
emit ( 'saved' , appliesFrom ) ;
2026-05-14 14:41:28 +03:00
} catch ( e : unknown ) {
2026-06-23 11:44:06 +03:00
const err = e as {
response ? : {
status ? : number ;
data ? : {
errors ? : Record < string , string [ ] > ;
error ? : string ;
current _capacity _leads ? : number ;
would _be _required _leads ? : number ;
} ;
} ;
} ;
2026-05-14 14:41:28 +03:00
if ( err . response ? . status === 422 && err . response . data ? . errors ) {
Object . assign ( errors , err . response . data . errors ) ;
2026-06-23 11:44:06 +03:00
} else if ( err . response ? . status === 409 && err . response . data ? . error === 'balance_insufficient' ) {
// O (балансовый блок): раньше панель молчала при 409 — клиент не понимал,
// почему правка лимита не сохранилась. Показываем причину под полем лимита.
const d = err . response . data ;
errors . daily _limit _target = [
` Лимит превышает баланс: хватает на ${ d . current _capacity _leads ? ? 0 } лид(ов), запрошено ${ d . would _be _required _leads ? ? form . daily _limit _target } . Пополните баланс, чтобы увеличить лимит. ` ,
] ;
2026-05-14 14:41:28 +03:00
}
} finally {
saving . value = false ;
}
}
2026-05-14 14:28:49 +03:00
function onKey ( e : KeyboardEvent ) : void {
if ( e . key === 'Escape' && props . project ) emit ( 'close' ) ;
}
onMounted ( ( ) => document . addEventListener ( 'keydown' , onKey ) ) ;
onBeforeUnmount ( ( ) => document . removeEventListener ( 'keydown' , onKey ) ) ;
2026-05-14 14:21:07 +03:00
const activeDays = computed < boolean [ ] > ( ( ) => {
const mask = form . delivery _days _mask ;
return Array . from ( { length : 7 } , ( _ , i ) => ( mask & ( 1 << i ) ) !== 0 ) ;
} ) ;
function toggleDay ( i : number ) : void {
2026-05-15 05:54:05 +03:00
form . delivery _days _mask ^= 1 << i ;
2026-05-14 14:21:07 +03:00
}
const dayLabels = [ 'Пн' , 'Вт' , 'Ср' , 'Чт' , 'Пт' , 'Сб' , 'Вс' ] ;
2026-05-14 14:13:52 +03:00
< / script >
< template >
< aside class = "project-details-drawer" : class = "{ open: project !== null }" >
< div v-if = "project" class="pdd-content" >
2026-05-14 14:21:07 +03:00
< header class = "pdd-head" >
< div class = "pdd-title" > { { project . name } } < / div >
2026-06-21 12:52:58 +03:00
< button class = "pdd-close" data -testid = " pdd -close " aria -label = " Закрыть " @click ="$emit('close')" >
< v -icon size = "20" icon = "mdi-close" / >
< / button >
2026-05-14 14:21:07 +03:00
< / header >
< div class = "pdd-body" >
2026-05-26 12:33:18 +03:00
<!-- Общая ошибка уровня проекта ( например , supplier - snapshot guard или has - deals на delete ) . -- >
< div v-if = "errors.project" class="pdd-error pdd-error-banner" data-testid="pdd-error-project" >
{{ errors.project [ 0 ] }}
< / div >
2026-05-14 14:21:07 +03:00
< label class = "pdd-field" >
< span class = "pdd-label" > Название < / span >
< input v-model = "form.name" data-testid="pdd-name" class="pdd-input" / >
2026-05-14 14:41:28 +03:00
< div v-if = "errors.name" class="pdd-error" data-testid="pdd-error-name" > {{ errors.name [ 0 ] }} < / div >
2026-05-14 14:21:07 +03:00
< / label >
2026-05-18 15:43:28 +03:00
< ! - - 18.05.2026 ux : редактирование источника проекта ( site / call / sms ) - - >
< label v-if = "project?.signal_type === 'site'" class="pdd-field" >
2026-06-25 05:16:11 +03:00
< span class = "pdd-label" > Сайт , с которого нужны заявки < / span >
2026-05-18 15:43:28 +03:00
< input
v-model = "form.signal_identifier"
data -testid = " pdd -signal -identifier "
class = "pdd-input"
2026-06-25 05:16:11 +03:00
placeholder = "okna-moskva.ru"
2026-05-18 15:43:28 +03:00
/ >
< div v-if = "errors.signal_identifier" class="pdd-error" data-testid="pdd-error-signal" >
{{ errors.signal_identifier [ 0 ] }}
< / div >
< / label >
< label v-else-if = "project?.signal_type === 'call'" class="pdd-field" >
2026-06-25 05:16:11 +03:00
< span class = "pdd-label" > Телефон , по которому идут клиенты < / span >
2026-05-18 15:43:28 +03:00
< input
v-model = "form.signal_identifier"
data -testid = " pdd -signal -identifier "
class = "pdd-input"
placeholder = "79161234567"
/ >
2026-06-24 13:04:58 +03:00
< div class = "pdd-hint" > Можно с + 7 , 8 , скобками и пробелами — приведём к виду 79161234567 < / div >
2026-05-18 15:43:28 +03:00
< div v-if = "errors.signal_identifier" class="pdd-error" data-testid="pdd-error-signal" >
{{ errors.signal_identifier [ 0 ] }}
< / div >
< / label >
< div v-else-if = "project?.signal_type === 'sms'" class="pdd-field" >
< span class = "pdd-label" > Источник — отправители SMS < / span >
< v-combobox
v-model = "form.sms_senders"
data -testid = " pdd -sms -senders "
multiple
chips
clearable
density = "comfortable"
hide -details
placeholder = "MTS, BEELINE …"
/ >
< div v-if = "errors.sms_senders" class="pdd-error" > {{ errors.sms_senders [ 0 ] }} < / div >
< span class = "pdd-label mt-2" > Ключевое слово ( опционально ) < / span >
< input
v-model = "form.sms_keyword"
data -testid = " pdd -sms -keyword "
class = "pdd-input"
placeholder = "КРЕДИТ"
/ >
< div v-if = "errors.sms_keyword" class="pdd-error" > {{ errors.sms_keyword [ 0 ] }} < / div >
< / div >
2026-06-25 17:48:34 +03:00
< ! - - Эпик 3.1 : объявление о вступлении правок количества / региона / дней ( без блокировки ) . - - >
< div
v-if = "showAppliesFromBanner"
class = "pdd-lock-hint"
data -testid = " pdd -applies -from -banner "
>
2026-06-25 19:40:04 +03:00
⏳ Ближайший сбор уже идёт по текущим настройкам . Новые количество , регионы
и дни приёма вступят в силу с { { appliesFromDate } } .
2026-06-22 20:59:02 +03:00
< / div >
2026-05-14 14:21:07 +03:00
< label class = "pdd-field" >
< span class = "pdd-label" > Лимит лидов в день < / span >
< input
v -model .number = " form.daily_limit_target "
type = "number"
min = "1"
max = "10000"
data -testid = " pdd -limit "
class = "pdd-input"
/ >
2026-05-14 14:41:28 +03:00
< div v-if = "errors.daily_limit_target" class="pdd-error" > {{ errors.daily_limit_target [ 0 ] }} < / div >
2026-05-14 14:21:07 +03:00
< / label >
2026-05-14 17:51:56 +03:00
< div class = "pdd-field" >
< span class = "pdd-label" > Регионы ( пусто = вся РФ ) < / span >
< v-autocomplete
2026-05-15 05:54:05 +03:00
v-model = "form.regions"
2026-05-14 17:51:56 +03:00
:items = "selectableRegions"
item -title = " name "
item -value = " code "
multiple
chips
2026-05-23 10:21:10 +03:00
closable -chips
2026-05-14 17:51:56 +03:00
clearable
density = "comfortable"
hide -details
data -testid = " pdd -regions "
2026-05-15 05:54:05 +03:00
>
< template # item = "{ props: itemProps, item }" >
< v-list-item v-bind = "itemProps" >
< template # subtitle >
{ { FEDERAL _DISTRICT _NAMES [ item . raw . federalDistrict ] || '' } }
< / template >
< / v-list-item >
< / template >
< / v-autocomplete >
2026-05-14 17:51:56 +03:00
< / div >
2026-05-14 14:21:07 +03:00
< div class = "pdd-field" >
< span class = "pdd-label" > Дни недели приёма < / span >
< div class = "pdd-days" >
< button
v-for = "(label, i) in dayLabels"
:key = "i"
type = "button"
:data-testid = "`pdd-day-${i}`"
: class = "['pdd-day', { active: activeDays[i] }]"
@click ="toggleDay(i)"
>
{ { label } }
< / button >
< / div >
< / div >
< / div >
< footer class = "pdd-foot" >
2026-05-14 14:48:24 +03:00
< div class = "pdd-foot-left" >
< button class = "pdd-btn pdd-btn-warning" data -testid = " pdd -pause " @click ="onPause" >
2026-06-21 12:52:58 +03:00
< v -icon size = "18" : icon = "project.is_active ? 'mdi-pause' : 'mdi-play'" / >
{ { project . is _active ? 'Приостановить' : 'Возобновить' } }
< / button >
< button class = "pdd-btn pdd-btn-error" data -testid = " pdd -delete " @click ="onDelete" >
< v -icon size = "18" icon = "mdi-delete" / > Удалить
2026-05-14 14:48:24 +03:00
< / button >
< / div >
2026-05-14 14:21:07 +03:00
< div class = "pdd-foot-right" >
2026-05-15 05:54:05 +03:00
< button class = "pdd-btn pdd-btn-text" data -testid = " pdd -cancel " @click ="$emit('close')" >
Отмена
< / button >
< button class = "pdd-btn pdd-btn-primary" data -testid = " pdd -save " :disabled = "saving" @click ="onSave" >
Сохранить
< / button >
2026-05-14 14:21:07 +03:00
< / div >
< / footer >
2026-06-25 17:48:34 +03:00
< ! - - Эпик 3.2 : подтверждение смены источника на залоченном проекте. - - >
< div
v-if = "sourceConfirmOpen"
class = "pdd-confirm-overlay"
data -testid = " pdd -source -change -confirm "
>
< div class = "pdd-confirm-box" >
< div class = "pdd-confirm-title" > Сменить источник ? < / div >
< div class = "pdd-confirm-text" > { { sourceConfirmText } } < / div >
< div class = "pdd-confirm-actions" >
< button
class = "pdd-btn pdd-btn-text"
data -testid = " pdd -source -confirm -no "
@click ="cancelSourceChange"
>
Отмена
< / button >
< button
class = "pdd-btn pdd-btn-primary"
data -testid = " pdd -source -confirm -yes "
@click ="confirmSourceChange"
>
Сменить источник
< / button >
< / div >
< / div >
< / div >
2026-05-14 14:13:52 +03:00
< / div >
< / aside >
< / template >
< style scoped >
. project - details - drawer {
2026-05-15 05:54:05 +03:00
position : fixed ;
top : 0 ;
right : 0 ;
bottom : 0 ;
2026-05-14 14:13:52 +03:00
width : 480 px ;
background : var ( -- liderra - surface , # ffffff ) ;
border - left : 1 px solid var ( -- liderra - line , # e6e2d6 ) ;
box - shadow : - 4 px 0 16 px rgba ( 0 , 0 , 0 , 0.06 ) ;
transform : translateX ( 100 % ) ;
transition : transform 240 ms cubic - bezier ( 0.16 , 1 , 0.3 , 1 ) ;
2026-05-15 05:54:05 +03:00
display : flex ;
flex - direction : column ;
2026-05-14 14:13:52 +03:00
z - index : 5 ;
}
2026-05-15 05:54:05 +03:00
. project - details - drawer . open {
transform : translateX ( 0 ) ;
}
. pdd - content {
display : flex ;
flex - direction : column ;
height : 100 % ;
}
. pdd - head {
display : flex ;
justify - content : space - between ;
align - items : center ;
padding : 16 px 20 px ;
border - bottom : 1 px solid var ( -- liderra - line , # e6e2d6 ) ;
}
. pdd - title {
font - weight : 600 ;
font - size : 16 px ;
}
. pdd - close {
background : none ;
border : 0 ;
cursor : pointer ;
font - size : 18 px ;
padding : 4 px ;
}
. pdd - body {
padding : 16 px 20 px ;
display : flex ;
flex - direction : column ;
gap : 14 px ;
flex : 1 ;
overflow - y : auto ;
}
. pdd - field {
display : flex ;
flex - direction : column ;
gap : 4 px ;
}
. pdd - label {
font - size : 12 px ;
color : # 6 b6f72 ;
}
. pdd - input {
padding : 8 px 10 px ;
border : 1 px solid var ( -- liderra - line , # e6e2d6 ) ;
border - radius : 6 px ;
font : inherit ;
}
. pdd - days {
display : flex ;
gap : 4 px ;
}
. pdd - day {
padding : 6 px 10 px ;
border : 1 px solid var ( -- liderra - line , # e6e2d6 ) ;
background : # ffffff ;
border - radius : 4 px ;
cursor : pointer ;
font : inherit ;
}
. pdd - day . active {
background : # 0 f6e56 ;
color : # ffffff ;
border - color : # 0 f6e56 ;
}
. pdd - foot {
display : flex ;
justify - content : space - between ;
padding : 12 px 20 px ;
border - top : 1 px solid var ( -- liderra - line , # e6e2d6 ) ;
}
. pdd - foot - left ,
. pdd - foot - right {
display : flex ;
gap : 8 px ;
}
. pdd - btn {
padding : 6 px 14 px ;
border : 0 ;
border - radius : 6 px ;
cursor : pointer ;
font : inherit ;
}
. pdd - btn - text {
background : transparent ;
color : # 081319 ;
}
. pdd - btn - primary {
background : # 0 f6e56 ;
color : # ffffff ;
}
. pdd - btn - warning {
background : # f59e0b ;
color : # ffffff ;
}
. pdd - btn - error {
background : # dc2626 ;
color : # ffffff ;
}
. pdd - error {
color : # dc2626 ;
font - size : 12 px ;
margin - top : 4 px ;
}
2026-06-24 13:04:58 +03:00
. pdd - hint {
color : # 6 b6f72 ;
font - size : 12 px ;
margin - top : 4 px ;
}
2026-06-22 20:59:02 +03:00
. pdd - lock - hint {
margin - top : 6 px ;
font - size : 12.5 px ;
line - height : 1.45 ;
color : # 23433 a ;
background : # f0f6f3 ;
border : 1 px solid # cfe4db ;
border - left : 3 px solid # 0 f6e56 ;
border - radius : 6 px ;
padding : 9 px 11 px ;
}
2026-06-25 17:48:34 +03:00
. pdd - confirm - overlay {
position : absolute ;
inset : 0 ;
background : rgba ( 1 , 32 , 25 , 0.45 ) ;
display : flex ;
align - items : center ;
justify - content : center ;
padding : 24 px ;
z - index : 10 ;
}
. pdd - confirm - box {
background : # ffffff ;
border - radius : 10 px ;
padding : 20 px ;
max - width : 380 px ;
box - shadow : 0 8 px 28 px rgba ( 0 , 0 , 0 , 0.18 ) ;
}
. pdd - confirm - title {
font - weight : 600 ;
font - size : 15 px ;
margin - bottom : 8 px ;
}
. pdd - confirm - text {
font - size : 13 px ;
line - height : 1.5 ;
color : # 23433 a ;
margin - bottom : 16 px ;
}
. pdd - confirm - actions {
display : flex ;
justify - content : flex - end ;
gap : 8 px ;
}
2026-05-14 14:13:52 +03:00
< / style >