@nixweb/nixloc-ui 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ <template>
2
+ <div class="actions-options">
3
+ <div class="ao-body">
4
+ <ScrollBar :minHeight="minHeight" :maxHeight="maxHeight" class="ao-scroll">
5
+ <div v-if="actionSelected.type == 'options'" class="ao-radios" role="group"
6
+ :aria-labelledby="'ao-title'">
7
+ <RadioGroup :field="fieldName" :options="filteredOptions" v-model="optionSelected" />
8
+ </div>
9
+
10
+ <div v-else class="ao-empty" aria-live="polite">
11
+ <i class="fas fa-inbox"></i>
12
+ <span>Nenhuma opção encontrada.</span>
13
+ </div>
14
+ </ScrollBar>
15
+ </div>
16
+
17
+ <footer class="ao-footer">
18
+ <div class="ao-footer-left">
19
+ <span v-if="optionSelected" class="ao-helper title">
20
+ Selecionado: <strong>{{ labelSelected }}</strong>
21
+ </span>
22
+ </div>
23
+ <div class="ao-footer-right">
24
+ <Button key="select" :disabled="!isValid" :title="btnNextLabel" type="primary" size="medium"
25
+ :clicked="next" />
26
+ </div>
27
+ </footer>
28
+ </div>
29
+ </template>
30
+
31
+ <script>
32
+ import Button from "@nixweb/nixloc-ui/src/component/forms/Button";
33
+ import RadioGroup from "@nixweb/nixloc-ui/src/component/forms/RadioGroup";
34
+ import ScrollBar from "@nixweb/nixloc-ui/src/component/layout/ScrollBar.vue";
35
+ import { mapMutations } from "vuex";
36
+
37
+ export default {
38
+ name: "ActionsOptions",
39
+ components: { Button, RadioGroup, ScrollBar },
40
+ props: {
41
+ actionSelected: { type: Object, required: true },
42
+ minHeight: { type: Number, default: 120 },
43
+ maxHeight: { type: Number, default: 260 },
44
+ btnNextLabel: { type: String, default: "Avançar" },
45
+ fieldName: { type: String, default: "group" },
46
+ searchThreshold: { type: Number, default: 10 },
47
+ value: String
48
+ },
49
+ data() {
50
+ return {
51
+ optionSelected: null,
52
+ query: ""
53
+ };
54
+ },
55
+ computed: {
56
+ optionsRaw() {
57
+ const list = (this.actionSelected && Array.isArray(this.actionSelected.options))
58
+ ? this.actionSelected.options
59
+ : [];
60
+ return list.map(o => ({ text: o.text, value: o.value }));
61
+ },
62
+ filteredOptions() {
63
+ const q = (this.query || "").toLowerCase();
64
+ if (!q) return this.optionsRaw;
65
+ return this.optionsRaw.filter(opt =>
66
+ String(opt.text).toLowerCase().includes(q) ||
67
+ String(opt.value).toLowerCase().includes(q)
68
+ );
69
+ },
70
+ isValid() {
71
+ return this.optionSelected !== null && this.optionSelected !== undefined && this.optionSelected !== "";
72
+ },
73
+ labelSelected() {
74
+ const found = this.optionsRaw.find(o => o.value === this.optionSelected);
75
+ return found ? found.text : "";
76
+ }
77
+ },
78
+ watch: {
79
+ optionSelected: {
80
+ handler(value) {
81
+ this.$emit("input", value);
82
+ },
83
+ deep: true,
84
+ },
85
+ optionsRaw: {
86
+ immediate: true,
87
+ handler(list) {
88
+ if (Array.isArray(list) && list.length === 1) {
89
+ this.optionSelected = list[0].value;
90
+ }
91
+ }
92
+ }
93
+ },
94
+ methods: {
95
+ ...mapMutations("generic", ["openModal", "hideModal"]),
96
+ next() {
97
+ if (!this.isValid) return;
98
+ this.openModal("confirm");
99
+ },
100
+ enterToNext() {
101
+ if (this.isValid) this.next();
102
+ },
103
+ }
104
+ };
105
+ </script>
106
+
107
+ <style scoped>
108
+ .actions-options {
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: 12px;
112
+ max-width: 640px;
113
+ }
114
+
115
+ .ao-body {
116
+ display: flex;
117
+ flex-direction: column;
118
+ gap: 8px;
119
+ }
120
+
121
+ .ao-scroll {
122
+ border: 1px solid #e5e7eb;
123
+ border-radius: 10px;
124
+ padding: 8px;
125
+ background: #fff;
126
+ }
127
+
128
+ .ao-radios {
129
+ display: grid;
130
+ gap: 10px;
131
+ }
132
+
133
+ .ao-empty {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 8px;
137
+ color: #6b7280;
138
+ padding: 8px;
139
+ }
140
+
141
+ .ao-footer {
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: space-between;
145
+ gap: 12px;
146
+ padding-top: 8px;
147
+ position: sticky;
148
+ bottom: 0;
149
+ }
150
+
151
+ .ao-footer-right {
152
+ display: flex;
153
+ gap: 8px;
154
+ }
155
+
156
+ .ao-helper {
157
+ color: #374151;
158
+ }
159
+ </style>
@@ -0,0 +1,213 @@
1
+ <template>
2
+ <div>
3
+ <div v-if="selected.length > 50" class="bab-alert">
4
+ <i class="fas fa-triangle-exclamation"></i>
5
+ <span> Limite máximo de 50 itens!</span>
6
+ </div>
7
+
8
+ <div v-else>
9
+ <ActionButtons :actions="actions" @confirm="confirm" />
10
+ </div>
11
+
12
+ <Modal title="Tem certeza que deseja executar?" :showValidation="false" :closeOnEsc="false" :closeButton="false"
13
+ :width="600" :height="750" v-if="showModal('confirm')">
14
+ <ActionlHeader :description="actionSelected.description" :total="selected.length"
15
+ :done="selectedDone.length" :errors="errorCount" />
16
+
17
+ <ActionItemList :selected="selected" :status="status" :errorsMap="errorsMap"
18
+ @show-error="openErrorDetails" />
19
+
20
+ <hr class="hr" />
21
+
22
+ <ActionFooter :isFinished="isFinished" :processing="processing" :selected="selected" @ok="ok"
23
+ @cancel="hideModal" @finish="finished" />
24
+ </Modal>
25
+
26
+ <Modal v-if="errorDetails.open" :title="`Falha — Item ${errorDetails.index + 1}`" :showValidation="false"
27
+ :closeOnEsc="false" :closeButton="false" :width="520" :height="360">
28
+ <ActionErrorContent :id="errorDetails.id" :index="errorDetails.index" :messages="errorsMap[errorDetails.id]"
29
+ @close="closeErrorDetails" />
30
+ </Modal>
31
+
32
+ <Modal title="Selecione a opção desejada" :showValidation="false" :closeOnEsc="true" :closeButton="true"
33
+ :width="600" :height="750" v-if="showModal('options')">
34
+ <ActionsOptions :actionSelected="actionSelected" v-model="optionSelected" />
35
+ </Modal>
36
+ </div>
37
+ </template>
38
+
39
+ <script>
40
+ import Button from "@nixweb/nixloc-ui/src/component/forms/Button";
41
+ import Modal from "@nixweb/nixloc-ui/src/component/forms/Modal";
42
+ import ScrollBar from "@nixweb/nixloc-ui/src/component/layout/ScrollBar.vue";
43
+
44
+ import ActionButtons from "@nixweb/nixloc-ui/src/component/shared/actions/ActionButtons.vue";
45
+ import ActionlHeader from "@nixweb/nixloc-ui/src/component/shared/actions/ActionHeader.vue";
46
+ import ActionsOptions from "@nixweb/nixloc-ui/src/component/shared/actions/ActionsOptions.vue";
47
+ import ActionItemList from "./ActionItemList.vue";
48
+ import ActionErrorContent from "./ActionErrorContent.vue";
49
+ import ActionFooter from "./ActionFooter.vue";
50
+
51
+ import { mapGetters, mapMutations, mapActions } from "vuex";
52
+
53
+ export default {
54
+ name: "ActionsSelected",
55
+ components: {
56
+ Button,
57
+ Modal,
58
+ ScrollBar,
59
+ ActionButtons,
60
+ ActionlHeader,
61
+ ActionsOptions,
62
+ ActionItemList,
63
+ ActionErrorContent,
64
+ ActionFooter,
65
+ },
66
+ props: {
67
+ actions: { type: Array, default: () => [] },
68
+ selected: { type: Array, default: () => [] },
69
+ },
70
+ data() {
71
+ return {
72
+ actionSelected: {},
73
+ optionSelected: "",
74
+ selectedDone: [],
75
+ processing: false,
76
+ status: {},
77
+ errorsMap: {},
78
+ errorDetails: { open: false, id: null, index: -1 },
79
+ errorIds: [],
80
+ };
81
+ },
82
+ computed: {
83
+ ...mapGetters("generic", ["showModal"]),
84
+ errorCount() {
85
+ return Object.values(this.status).filter((s) => s === "error").length;
86
+ },
87
+ isFinished() {
88
+ if (!this.selected.length) return false;
89
+ const statuses = Object.values(this.status);
90
+ return (
91
+ statuses.length === this.selected.length &&
92
+ statuses.every((s) => s === "done" || s === "error")
93
+ );
94
+ },
95
+ },
96
+ methods: {
97
+ ...mapActions("generic", ["putApi", "deleteAllApi"]),
98
+ ...mapMutations("generic", ["openModal", "hideModal", "executedSearch", "addSelected", "addEvent"]),
99
+ confirm(params) {
100
+ this.actionSelected = params;
101
+ this.selectedDone = [];
102
+ this.processing = false;
103
+ this.status = {};
104
+ this.errorsMap = {};
105
+ this.errorIds = [];
106
+ this.selected.forEach((id) => this.$set(this.status, id, "waiting"));
107
+
108
+ if (params.type != "delete") {
109
+ this.openModal("options");
110
+ } else {
111
+ this.openModal("confirm");
112
+ }
113
+
114
+ },
115
+ async put(id) {
116
+ const response = await this.putApi(
117
+ {
118
+ url: this.actionSelected.url,
119
+ obj: { id: id, any: this.optionSelected },
120
+ notNotifyToast: true
121
+ }
122
+ );
123
+ return response;
124
+ },
125
+ async remove(id) {
126
+ const response = await this.deleteAllApi(
127
+ {
128
+ url: this.actionSelected.url,
129
+ selected: [id],
130
+ notNotifyToast: true
131
+ }
132
+ );
133
+ return response;
134
+ },
135
+ async ok() {
136
+ if (this.processing) return;
137
+ this.processing = true;
138
+
139
+ for (let i = 0; i < this.selected.length; i++) {
140
+ const id = this.selected[i];
141
+ this.$set(this.status, id, "running");
142
+
143
+ try {
144
+ let result = null;
145
+ if (this.actionSelected.method == "delete")
146
+ result = await this.remove(id);
147
+
148
+ if (this.actionSelected.method == "put")
149
+ result = await this.put(id);
150
+
151
+ console.log(this.actionSelected);
152
+ console.log(result);
153
+
154
+
155
+ if (result?.success) {
156
+ this.$set(this.status, id, "done");
157
+ this.selectedDone.push(id);
158
+ } else {
159
+ const msgs = (result?.notifications || [])
160
+ .map((n) => n && n.message)
161
+ .filter(Boolean);
162
+ this.$set(this.errorsMap, id, msgs);
163
+ this.$set(this.status, id, "error");
164
+ this.errorIds.push(id);
165
+ }
166
+ } catch {
167
+ this.$set(this.errorsMap, id, ["Falha inesperada ao comunicar com a API."]);
168
+ this.$set(this.status, id, "error");
169
+ this.errorIds.push(id);
170
+ }
171
+ }
172
+
173
+ this.processing = false;
174
+ },
175
+ openErrorDetails({ id, index }) {
176
+ this.errorDetails = { open: true, id, index };
177
+ },
178
+ closeErrorDetails() {
179
+ this.errorDetails = { open: false, id: null, index: -1 };
180
+ },
181
+ finished() {
182
+ this.hideModal();
183
+ this.addEvent({ name: "deselectAll" });
184
+ this.addSelected(this.errorIds);
185
+ this.executedSearch();
186
+ },
187
+ },
188
+ };
189
+ </script>
190
+
191
+ <style scoped>
192
+ .bab-alert {
193
+ box-sizing: border-box;
194
+ margin-bottom: 10px;
195
+ color: rgba(0, 0, 0, 0.65);
196
+ font-size: 14px;
197
+ padding: 8px 15px;
198
+ border-radius: 10px;
199
+ background-color: #fff1f0;
200
+ border: 1px solid #ffa39e;
201
+ }
202
+
203
+ .bab-alert i {
204
+ color: #dc2626;
205
+ font-size: 15px;
206
+ }
207
+
208
+ .hr {
209
+ margin: 14px 0;
210
+ border: none;
211
+ border-top: 1px solid #eef2f7;
212
+ }
213
+ </style>
@@ -1,19 +1,6 @@
1
1
  <template>
2
2
  <div>
3
3
  <div :class="{ 'g-div-molded': showMolded }">
4
- <b-row>
5
- <b-col sm="12">
6
- <div>
7
- <!-- <HorizontalFilter v-if="showHorizontalFilter">
8
- <div slot="content-filter-horizontal">
9
- <slot name="content-filter-horizontal"></slot>
10
- </div>
11
- </HorizontalFilter> -->
12
- <TableTotalPerPage />
13
- </div>
14
- </b-col>
15
- </b-row>
16
- <br>
17
4
  <b-row>
18
5
  <b-col sm="8">
19
6
  <div v-if="templateList.dragAndDrop">
@@ -21,12 +8,21 @@
21
8
  </div>
22
9
  <TableTotalization :totalization="content.totalization" />
23
10
  </b-col>
24
- <b-col sm="4">
25
- <TableTotalRecords :totalRecords="content.totalRecords" />
11
+ <b-col sm="4"></b-col>
12
+ </b-row>
13
+ <b-row>
14
+ <b-col sm="12">
15
+ <div>
16
+ <TableTotalPerPage :totalRecords="content.totalRecords" />
17
+ </div>
26
18
  </b-col>
27
19
  </b-row>
20
+ <br>
28
21
  <Loading type="line" :center="false" v-show="isLoading('loadingLine')" />
29
- <BarFloating>
22
+ <BottomActionsBar v-if="actions.length > 0" :selected="selected" @close="addSelected([])">
23
+ <ActionsSelected :selected="selected" :actions="actions" />
24
+ </BottomActionsBar>
25
+ <BarFloating v-else>
30
26
  <div class="fixed-bar-options" v-show="selected.length > 0">
31
27
  <div class="side-by-side">
32
28
  <Button v-if="buttonRemove" key="remove" :title="`Remover ${selected.length}`" type="danger" size="small"
@@ -38,7 +34,7 @@
38
34
  </div>
39
35
  </BarFloating>
40
36
  <Table :header="templateList.headerTable" :data="content.data" :showChecks="templateList.showChecks"
41
- :dragAndDrop="templateList.dragAndDrop">
37
+ :dragAndDrop="templateList.dragAndDrop" :totalRecords="content.totalRecords">
42
38
  <div slot="content-buttons-table">
43
39
  <slot name="content-buttons-table"></slot>
44
40
  </div>
@@ -65,7 +61,9 @@ import TableTotalPerPage from "@nixweb/nixloc-ui/src/component/shared/TableTotal
65
61
  import Table from "../shared/Table.vue";
66
62
  import Button from "../forms/Button.vue";
67
63
  import FixedBar from "../layout/FixedBar.vue";
64
+ import ActionsSelected from "@nixweb/nixloc-ui/src/component/shared/actions/ActionsSelected.vue";
68
65
  import BarFloating from "@nixweb/nixloc-ui/src/component/layout/BarFloating.vue";
66
+ import BottomActionsBar from "@nixweb/nixloc-ui/src/component/layout/BottomActionsBar.vue";
69
67
  import TableTotalRecords from "../shared/TableTotalRecords.vue";
70
68
  import TableTotalization from "../shared/TableTotalization.vue";
71
69
  import HorizontalFilter from "../shared/HorizontalFilter.vue";
@@ -82,6 +80,10 @@ export default {
82
80
  props: {
83
81
  templateList: Object,
84
82
  propsParam: Object,
83
+ actions: {
84
+ type: Array,
85
+ default: [],
86
+ },
85
87
  showMolded: {
86
88
  type: Boolean,
87
89
  default: true,
@@ -100,11 +102,13 @@ export default {
100
102
  }
101
103
  },
102
104
  components: {
105
+ ActionsSelected,
103
106
  HorizontalFilter,
104
107
  TableTotalization,
105
108
  TableTotalRecords,
106
109
  Confirmation,
107
110
  BarFloating,
111
+ BottomActionsBar,
108
112
  Alert,
109
113
  Modal,
110
114
  Loading,
@@ -7,8 +7,8 @@
7
7
  <Search />
8
8
  <br />
9
9
  </div>
10
- <ListViewWithDataHandler :templateList="templateList" :propsParam="propsParam" :showMolded="false"
11
- :showHorizontalFilter="showHorizontalFilter" :buttonRemove="buttonRemove">
10
+ <ListViewWithDataHandler :templateList="templateList" :actions="actions" :propsParam="propsParam"
11
+ :showMolded="false" :showHorizontalFilter="showHorizontalFilter" :buttonRemove="buttonRemove">
12
12
  <div slot="content-buttons-table-header">
13
13
  <slot name="content-buttons-table-header"></slot>
14
14
  </div>
@@ -36,6 +36,10 @@ export default {
36
36
  props: {
37
37
  templateList: Object,
38
38
  propsParam: Object,
39
+ actions: {
40
+ type: Array,
41
+ default: [],
42
+ },
39
43
  showSearch: {
40
44
  type: Boolean,
41
45
  default: true,
@@ -12,7 +12,7 @@
12
12
  <slot name="content-between-search-table"></slot>
13
13
  <FilterQuery v-if="filters.length > 0" :filters="filters" v-model="stringFilter" />
14
14
  <Molded>
15
- <ListViewWithDataHandler :templateList="templateList"
15
+ <ListViewWithDataHandler :templateList="templateList" :actions="actions"
16
16
  :propsParam="{ ...propsParam, stringFilter: JSON.stringify(stringFilter) }" :isFilterStorage="true"
17
17
  :buttonRemove="buttonRemove">
18
18
  <div slot="content-buttons-table-header">
@@ -44,6 +44,10 @@ export default {
44
44
  panel: Object,
45
45
  templateList: Object,
46
46
  propsParam: Object,
47
+ actions: {
48
+ type: Array,
49
+ default: [],
50
+ },
47
51
  filters: {
48
52
  type: Array,
49
53
  default: [],
@@ -418,7 +418,7 @@ export default {
418
418
  updateQueryTags: (state, tags) => {
419
419
  state.queryTags = tags;
420
420
  },
421
- _addFilterStorage: (state, obj) => { // está com _addFilterStorage para não ter duplicidade
421
+ _addFilterStorage: (state, obj) => { // está com _addFilterStorage para não ter duplicidade
422
422
  // função abaixo é para não deixar duplicidade
423
423
  const index = state._filterStorage.findIndex(x => x.routeName === obj.routeName);
424
424
  if (index !== -1) {
@@ -486,7 +486,9 @@ export default {
486
486
  return response.data;
487
487
  } else {
488
488
  context.commit('addNotifications', response.data.notifications)
489
- context.commit('addToast', 'messageError');
489
+
490
+ if (!params.notNotifyToast)
491
+ context.commit('addToast', 'messageError');
490
492
  return response.data;
491
493
  }
492
494
  }, (err) => {
@@ -604,13 +606,19 @@ export default {
604
606
  .then((response) => {
605
607
  if (response.data.success) {
606
608
  context.commit('addMethodExecutedApi', 'deleteAllApi');
607
- context.commit('addToast', 'deleteApiSucesso');
609
+
610
+ if (!params.notNotifyToast)
611
+ context.commit('addToast', 'deleteApiSucesso');
612
+
608
613
  context.commit('removeNotificarions');
609
614
  return response.data;
610
615
  } else {
611
616
  context.commit('addNotifications', response.data.notifications)
612
617
  context.commit('addMethodExecutedApi', 'deleteAllApiErro');
613
- context.commit('addToast', 'messageError');
618
+
619
+ if (!params.notNotifyToast)
620
+ context.commit('addToast', 'deleteApiSucesso');
621
+
614
622
  return response.data;
615
623
  }
616
624