|
|
@@ -212,9 +212,17 @@
|
|
|
<template v-for="unit in filteredUnits" :key="unit.id">
|
|
|
<q-separator />
|
|
|
<div class="row items-center q-px-sm q-py-sm">
|
|
|
- <span class="col text-body2">{{
|
|
|
- unit.fantasy_name
|
|
|
- }}</span>
|
|
|
+ <span class="col text-body2 row items-center q-gutter-x-xs">
|
|
|
+ {{ unit.fantasy_name }}
|
|
|
+ <q-icon
|
|
|
+ v-if="lockedUnitIds.has(unit.id)"
|
|
|
+ name="mdi-lock"
|
|
|
+ color="amber-7"
|
|
|
+ size="xs"
|
|
|
+ >
|
|
|
+ <q-tooltip>Visibilidade definida por grupo</q-tooltip>
|
|
|
+ </q-icon>
|
|
|
+ </span>
|
|
|
<div class="col-auto row q-gutter-x-xs">
|
|
|
<q-btn
|
|
|
round
|
|
|
@@ -228,10 +236,11 @@
|
|
|
round
|
|
|
:outline="unit.visible"
|
|
|
:unelevated="!unit.visible"
|
|
|
- :color="unit.visible ? 'negative' : 'negative'"
|
|
|
+ color="negative"
|
|
|
icon="mdi-close"
|
|
|
size="xs"
|
|
|
- @click="unit.visible = false"
|
|
|
+ :disable="lockedUnitIds.has(unit.id)"
|
|
|
+ @click="!lockedUnitIds.has(unit.id) && (unit.visible = false)"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -302,49 +311,59 @@
|
|
|
overflow-y: auto;
|
|
|
"
|
|
|
>
|
|
|
- <div class="row items-center q-mb-xs q-px-sm">
|
|
|
- <q-icon
|
|
|
- name="mdi-account-group"
|
|
|
- color="secondary"
|
|
|
- size="sm"
|
|
|
- class="q-mr-xs"
|
|
|
- />
|
|
|
- <span class="text-body2">
|
|
|
- Grupos Ativos ({{ filteredGroups.length }})
|
|
|
- </span>
|
|
|
+ <div v-if="loadingGroups" class="flex flex-center q-py-md">
|
|
|
+ <q-spinner color="secondary" size="sm" />
|
|
|
</div>
|
|
|
|
|
|
- <q-separator class="q-mb-xs" />
|
|
|
+ <template v-else>
|
|
|
+ <div class="row items-center q-mb-xs q-px-sm">
|
|
|
+ <q-icon
|
|
|
+ name="mdi-account-group"
|
|
|
+ color="secondary"
|
|
|
+ size="sm"
|
|
|
+ class="q-mr-xs"
|
|
|
+ />
|
|
|
+ <span class="text-body2">
|
|
|
+ Grupos ({{ filteredGroups.length }})
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="row q-px-sm q-py-xs">
|
|
|
- <span class="col text-caption text-grey-6">Grupo</span>
|
|
|
- <span class="col-auto text-caption text-grey-6">Status</span>
|
|
|
- </div>
|
|
|
+ <q-separator class="q-mb-xs" />
|
|
|
|
|
|
- <template v-for="group in filteredGroups" :key="group.id">
|
|
|
- <q-separator />
|
|
|
- <div class="row items-center q-px-sm q-py-sm">
|
|
|
- <span class="col text-body2">{{ group.name }}</span>
|
|
|
- <div class="col-auto row q-gutter-x-xs">
|
|
|
- <q-btn
|
|
|
- round
|
|
|
- unelevated
|
|
|
- :color="group.visible ? 'positive' : 'grey-4'"
|
|
|
- icon="mdi-check"
|
|
|
- size="xs"
|
|
|
- @click="group.visible = true"
|
|
|
- />
|
|
|
- <q-btn
|
|
|
- round
|
|
|
- :outline="group.visible"
|
|
|
- :unelevated="!group.visible"
|
|
|
- color="negative"
|
|
|
- icon="mdi-close"
|
|
|
- size="xs"
|
|
|
- @click="group.visible = false"
|
|
|
- />
|
|
|
- </div>
|
|
|
+ <div class="row q-px-sm q-py-xs">
|
|
|
+ <span class="col text-caption text-grey-6">Grupo</span>
|
|
|
+ <span class="col-6 text-caption text-grey-6">Unidades</span>
|
|
|
+ <span class="col-auto text-caption text-grey-6">Status</span>
|
|
|
</div>
|
|
|
+
|
|
|
+ <template v-for="group in filteredGroups" :key="group.id">
|
|
|
+ <q-separator />
|
|
|
+ <div class="row items-center q-px-sm q-py-sm">
|
|
|
+ <span class="col text-body2">{{ group.name }}</span>
|
|
|
+ <span class="col-6 text-caption text-grey-6">
|
|
|
+ {{ group.unit_ids?.length ?? 0 }} unidade(s)
|
|
|
+ </span>
|
|
|
+ <div class="col-auto row q-gutter-x-xs">
|
|
|
+ <q-btn
|
|
|
+ round
|
|
|
+ unelevated
|
|
|
+ :color="group.visible ? 'positive' : 'grey-4'"
|
|
|
+ icon="mdi-check"
|
|
|
+ size="xs"
|
|
|
+ @click="group.visible = true"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ round
|
|
|
+ :outline="group.visible"
|
|
|
+ :unelevated="!group.visible"
|
|
|
+ color="negative"
|
|
|
+ icon="mdi-close"
|
|
|
+ size="xs"
|
|
|
+ @click="group.visible = false"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -372,7 +391,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, onMounted } from "vue";
|
|
|
+import { ref, computed, onMounted, watch } from "vue";
|
|
|
import { useDialogPluginComponent } from "quasar";
|
|
|
|
|
|
import CustomTabComponent from "src/components/shared/CustomTabComponent.vue";
|
|
|
@@ -384,6 +403,7 @@ import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.v
|
|
|
import { getUnitsForSelect } from "src/api/unit";
|
|
|
import { getPackage, createPackage, updatePackage } from "src/api/package";
|
|
|
import { getProductsForSelect } from "src/api/product";
|
|
|
+import { getGroups } from "src/api/group";
|
|
|
|
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
@@ -400,6 +420,7 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
|
const formRef = ref(null);
|
|
|
const loading = ref(false);
|
|
|
const loadingUnits = ref(false);
|
|
|
+const loadingGroups = ref(false);
|
|
|
const currentTab = ref("informacoes");
|
|
|
|
|
|
const tabs = [
|
|
|
@@ -456,11 +477,31 @@ const units = ref([]);
|
|
|
|
|
|
// Grupos tab
|
|
|
const groupSearch = ref("");
|
|
|
-const groups = ref([
|
|
|
- { id: 1, name: "Grupo 1", visible: true },
|
|
|
- { id: 2, name: "Grupo 2", visible: true },
|
|
|
- { id: 3, name: "Grupo 3", visible: true },
|
|
|
-]);
|
|
|
+const groups = ref([]);
|
|
|
+
|
|
|
+// Set of unit IDs that are locked because they belong to a selected group
|
|
|
+const lockedUnitIds = computed(() => {
|
|
|
+ const ids = new Set();
|
|
|
+ groups.value.forEach((g) => {
|
|
|
+ if (g.visible && g.unit_ids) {
|
|
|
+ g.unit_ids.forEach((id) => ids.add(id));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return ids;
|
|
|
+});
|
|
|
+
|
|
|
+// When group visibility changes, force locked units to visible
|
|
|
+watch(
|
|
|
+ lockedUnitIds,
|
|
|
+ (newLockedIds) => {
|
|
|
+ units.value.forEach((u) => {
|
|
|
+ if (newLockedIds.has(u.id)) {
|
|
|
+ u.visible = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ { deep: true },
|
|
|
+);
|
|
|
|
|
|
const filteredUnits = computed(() => {
|
|
|
const q = unitSearch.value.toLowerCase();
|
|
|
@@ -469,13 +510,19 @@ const filteredUnits = computed(() => {
|
|
|
});
|
|
|
|
|
|
const setAllVisible = (visible) => {
|
|
|
- units.value.forEach((u) => (u.visible = visible));
|
|
|
+ units.value.forEach((u) => {
|
|
|
+ // Cannot hide a unit that is locked by a group
|
|
|
+ if (!visible && lockedUnitIds.value.has(u.id)) return;
|
|
|
+ u.visible = visible;
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
const filteredGroups = computed(() => {
|
|
|
const q = groupSearch.value.toLowerCase();
|
|
|
if (!q) return groups.value;
|
|
|
- return groups.value.filter((g) => g.name.toLowerCase().includes(q));
|
|
|
+ return groups.value.filter((g) =>
|
|
|
+ g.name.toLowerCase().includes(q),
|
|
|
+ );
|
|
|
});
|
|
|
|
|
|
const setAllGroupsVisible = (visible) => {
|
|
|
@@ -484,16 +531,19 @@ const setAllGroupsVisible = (visible) => {
|
|
|
|
|
|
onMounted(async () => {
|
|
|
loadingUnits.value = true;
|
|
|
+ loadingGroups.value = true;
|
|
|
try {
|
|
|
- const [allUnits, allProducts] = await Promise.all([
|
|
|
+ const [allUnits, allProducts, allGroups] = await Promise.all([
|
|
|
getUnitsForSelect(),
|
|
|
getProductsForSelect(),
|
|
|
+ getGroups(),
|
|
|
]);
|
|
|
|
|
|
products.value = allProducts;
|
|
|
|
|
|
let visibilityMap = {};
|
|
|
let savedMaterials = null;
|
|
|
+ let savedGroupIds = [];
|
|
|
|
|
|
if (props.package?.id) {
|
|
|
const pkg = await getPackage(props.package.id);
|
|
|
@@ -507,12 +557,27 @@ onMounted(async () => {
|
|
|
price: m.price,
|
|
|
}));
|
|
|
}
|
|
|
+ savedGroupIds = pkg.group_ids ?? [];
|
|
|
}
|
|
|
|
|
|
+ // Build groups with visibility flag and their unit_ids
|
|
|
+ groups.value = allGroups.map((g) => ({
|
|
|
+ id: g.id,
|
|
|
+ name: g.name,
|
|
|
+ unit_ids: g.unit_ids ?? [],
|
|
|
+ visible: savedGroupIds.includes(g.id),
|
|
|
+ }));
|
|
|
+
|
|
|
+ // Units locked by groups start visible = true
|
|
|
+ const groupLockedIds = new Set();
|
|
|
+ groups.value.forEach((g) => {
|
|
|
+ if (g.visible) g.unit_ids.forEach((id) => groupLockedIds.add(id));
|
|
|
+ });
|
|
|
+
|
|
|
units.value = allUnits.map((u) => ({
|
|
|
id: u.id,
|
|
|
fantasy_name: u.fantasy_name,
|
|
|
- visible: visibilityMap[u.id] ?? true,
|
|
|
+ visible: groupLockedIds.has(u.id) ? true : (visibilityMap[u.id] ?? true),
|
|
|
}));
|
|
|
|
|
|
if (savedMaterials) {
|
|
|
@@ -520,6 +585,7 @@ onMounted(async () => {
|
|
|
}
|
|
|
} finally {
|
|
|
loadingUnits.value = false;
|
|
|
+ loadingGroups.value = false;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -534,6 +600,9 @@ const onOKClick = async () => {
|
|
|
contract_value: form.value.contract_value,
|
|
|
contract_register_value: form.value.contract_register_value,
|
|
|
contrat_discount_value: form.value.contrat_discount_value,
|
|
|
+ group_ids: groups.value
|
|
|
+ .filter((g) => g.visible)
|
|
|
+ .map((g) => g.id),
|
|
|
unit_visibilities: units.value.map((u) => ({
|
|
|
unit_id: u.id,
|
|
|
visible: u.visible,
|