Files
portal/app/resources/js/components/projects/RegionsBulkDialog.vue
T
Дмитрий 54451d2ea6 feat(projects): RegionsBulkDialog — subject-level regions (89 RF subjects) #1426
Bulk regions dialog reworked from federal-district bitmask to subject/region
selection, consistent with ProjectDetailsDrawer/NewProjectDialog. Full-stack:
add_regions/remove_regions on projects.regions INT[], BulkProjectActionRequest
split validation, ProjectService model-instance update. federal-districts.ts
removed (zero consumers). +menuRepositionFix util for v-autocomplete menu.
phpstan-baseline: bump actingAs ignore count 14->15 (new validation test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 03:41:46 +03:00

115 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<v-dialog v-model="open" max-width="560">
<v-card>
<v-card-title>Регионы для {{ count }} проектов</v-card-title>
<v-card-text>
<p class="text-caption text-medium-emphasis mb-4">
Изменения применяются к каждому из {{ count }} выбранных проектов: выбранные субъекты
добавляются к их регионам или убираются из них.
</p>
<div class="mb-2">
<div class="text-caption text-success font-weight-medium mb-2"> Добавить регионы</div>
<v-autocomplete
v-model="addRegions"
:items="selectableRegions"
item-title="name"
item-value="code"
label="Субъекты РФ"
multiple
chips
clearable
density="comfortable"
data-testid="region-add-select"
@update:menu="repositionMenuAfterOpen"
>
<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>
</div>
<div>
<div class="text-caption text-error font-weight-medium mb-2"> Убрать регионы</div>
<v-autocomplete
v-model="removeRegions"
:items="selectableRegions"
item-title="name"
item-value="code"
label="Субъекты РФ"
multiple
chips
clearable
density="comfortable"
data-testid="region-remove-select"
@update:menu="repositionMenuAfterOpen"
>
<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>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn data-testid="cancel" @click="open = false">Отмена</v-btn>
<v-btn
color="primary"
data-testid="apply"
:disabled="addRegions.length === 0 && removeRegions.length === 0"
@click="apply"
>Применить к {{ count }}</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { REGIONS, FEDERAL_DISTRICT_NAMES } from '../../constants/regions';
import { repositionMenuAfterOpen } from '../../utils/menuRepositionFix';
const props = defineProps<{ modelValue: boolean; count: number }>();
const emit = defineEmits<{
'update:modelValue': [value: boolean];
apply: [payload: { add_regions: number[]; remove_regions: number[] }];
}>();
// code:0 — sentinel «Вся РФ»; в bulk add/remove субъектов не выбирается.
const selectableRegions = REGIONS.filter((r) => r.code !== 0);
const open = ref(props.modelValue);
const addRegions = ref<number[]>([]);
const removeRegions = ref<number[]>([]);
watch(
() => props.modelValue,
(val) => {
open.value = val;
if (val) {
addRegions.value = [];
removeRegions.value = [];
}
},
);
watch(open, (val) => emit('update:modelValue', val));
function apply() {
emit('apply', { add_regions: [...addRegions.value], remove_regions: [...removeRegions.value] });
addRegions.value = [];
removeRegions.value = [];
open.value = false;
}
defineExpose({ addRegions, removeRegions, apply });
</script>