fix(install): блокировать «Продолжить» до минимума совета, удаление без ограничений.

На prod требуется 3 человека (на dev — 1): кнопка и подсказка, состав редактируется свободно до перехода на следующий шаг.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alex Ant
2026-06-24 22:03:12 +05:00
parent eb16d85abc
commit 4d151befe0
4 changed files with 77 additions and 26 deletions
@@ -1 +1,5 @@
export { getMinSovietMembersCount } from './minSovietMembers';
export {
getMinSovietMembersCount,
getSovietMembersProgressHint,
getSovietContinueBlockedTooltip,
} from './minSovietMembers';
@@ -4,3 +4,26 @@ import { env } from 'src/shared/config';
export function getMinSovietMembersCount(): number {
return env.NODE_ENV === 'production' ? 3 : 1;
}
/** Краткая подсказка под формой: сколько указано и чего не хватает. */
export function getSovietMembersProgressHint(currentCount: number, min = getMinSovietMembersCount()): string | null {
if (min <= 1 || currentCount >= min) return null;
const missing = min - currentCount;
return `Указано ${currentCount} из ${min}. Добавьте ещё ${missing} ${pluralCouncilSlot(missing)}.`;
}
/** Текст для tooltip заблокированной кнопки «Продолжить». */
export function getSovietContinueBlockedTooltip(currentCount: number, min = getMinSovietMembersCount()): string {
if (currentCount >= min) return '';
if (min === 1) return 'Заполните данные председателя';
const missing = min - currentCount;
return `Для продолжения нужно ${min} человека в составе совета (ещё ${missing} ${pluralCouncilSlot(missing)})`;
}
function pluralCouncilSlot(count: number): string {
const mod10 = count % 10;
const mod100 = count % 100;
if (mod10 === 1 && mod100 !== 11) return 'человек';
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return 'человека';
return 'человек';
}
@@ -1,5 +1,8 @@
<template lang="pug">
.set-soviet(v-if='installStore')
p.set-soviet__requirement(v-if='minSovietMembers > 1')
| В составе совета минимум {{ minSovietMembers }}&nbsp;человека: председатель и члены совета.
.soviet-member(v-for='(member, index) in installStore.soviet', :key='member.id')
.soviet-member__head
span.soviet-member__role
@@ -29,6 +32,8 @@
:rules='[val => notEmpty(val), val => validEmail(val)]'
)
p.set-soviet__progress(v-if='progressHint') {{ progressHint }}
.set-soviet__actions
BaseButton(variant='ghost', @click='back')
q-icon(name='arrow_back', size='16px')
@@ -37,18 +42,29 @@
BaseButton(variant='secondary', @click='add')
q-icon(name='add', size='16px')
span.q-ml-sm Добавить члена совета
BaseButton(variant='primary', :loading='loading', @click='next')
span.q-mr-sm Продолжить
q-icon(name='arrow_forward', size='16px')
span.set-soviet__continue-wrap
q-tooltip(v-if='!canContinue') {{ continueBlockedTooltip }}
BaseButton(
variant='primary',
:loading='loading',
:disabled='!canContinue',
@click='next'
)
span.q-mr-sm Продолжить
q-icon(name='arrow_forward', size='16px')
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ref, computed } from 'vue';
import { useInstallCooperativeStore } from 'src/entities/Installer/model';
import { IndividualDataForm } from 'src/shared/ui/UserDataForm/IndividualDataForm';
import type { IIndividualData } from 'src/shared/lib/types/user/IUserData';
import { FailAlert } from 'src/shared/api';
import { getMinSovietMembersCount } from '../../lib';
import {
getMinSovietMembersCount,
getSovietMembersProgressHint,
getSovietContinueBlockedTooltip,
} from '../../lib';
import { notEmpty } from 'src/shared/lib/utils';
import { validEmail } from 'src/shared/lib/utils/validEmailRule';
import { BaseButton } from 'src/shared/ui/base/BaseButton';
@@ -58,6 +74,13 @@ const minSovietMembers = getMinSovietMembersCount();
installStore.is_finish = false;
const sovietCount = computed(() => installStore.soviet.length);
const canContinue = computed(() => sovietCount.value >= minSovietMembers);
const progressHint = computed(() => getSovietMembersProgressHint(sovietCount.value, minSovietMembers));
const continueBlockedTooltip = computed(() =>
getSovietContinueBlockedTooltip(sovietCount.value, minSovietMembers),
);
const add = () => {
let role: 'chairman' | 'member' = 'chairman';
@@ -76,24 +99,15 @@ const back = () => {
installStore.current_step = 'init';
};
const MIN_SOVIET_MEMBERS = minSovietMembers;
const next = async () => {
try {
if (installStore.soviet.length < MIN_SOVIET_MEMBERS) {
FailAlert(
MIN_SOVIET_MEMBERS === 1
? 'Необходимо добавить хотя бы одного члена совета'
: `Необходимо добавить не менее ${MIN_SOVIET_MEMBERS} членов совета (председатель и члены совета)`,
);
return;
}
if (!canContinue.value) return;
try {
loading.value = true;
installStore.current_step = 'vars';
loading.value = false;
} catch (e: any) {
FailAlert(e);
} finally {
loading.value = false;
}
};
@@ -112,6 +126,18 @@ if (installStore.soviet.length == 0) {
gap: var(--p-4, 16px);
}
.set-soviet__requirement,
.set-soviet__progress {
margin: 0;
font-size: var(--p-fs-body-sm, 13px);
line-height: var(--p-lh-body, 1.55);
color: var(--p-ink-2);
}
.set-soviet__progress {
color: var(--p-warn, var(--p-ink-2));
}
.soviet-member {
padding: var(--p-4, 16px);
background: var(--p-surface);
@@ -143,4 +169,7 @@ if (installStore.soviet.length == 0) {
gap: var(--p-2, 8px);
flex-wrap: wrap;
}
.set-soviet__continue-wrap {
display: inline-flex;
}
</style>
@@ -26,7 +26,9 @@
//- ---------- Шаг 3: члены совета ----------
.install-step(v-else-if='step.key === "soviet"')
p.install-step__intro {{ sovietStepIntro }}
p.install-step__intro
| Укажите председателя и членов совета. Каждому будет создан аккаунт пайщика
| и отправлено приглашение на электронную почту.
SetSovietForm
//- ---------- Шаг 4: переменные документов ----------
@@ -58,17 +60,10 @@ import { useSystemStore } from 'src/entities/System/model';
import { VerticalStepper } from 'src/shared/ui/domain/VerticalStepper';
import type { StepperStep } from 'src/shared/ui/domain/VerticalStepper';
import { BaseButton } from 'src/shared/ui/base/BaseButton';
import { getMinSovietMembersCount } from 'src/features/Installer/lib';
const router = useRouter();
const systemStore = useSystemStore();
const installStore = useInstallCooperativeStore();
const minSovietMembers = getMinSovietMembersCount();
const sovietStepIntro =
minSovietMembers === 1
? 'Введите данные председателя. Будет создан аккаунт пайщика и отправлено приглашение на электронную почту.'
: 'Введите данные председателя и не менее двух членов совета (всего минимум 3 человека). Каждому будет создан аккаунт пайщика и отправлено приглашение на электронную почту.';
const steps: StepperStep[] = [
{ key: 'key', label: 'Ключ установки', description: 'Ключ, выданный при регистрации пайщиком' },