@freteclick/quasar-financial-ui 1.0.113 → 1.0.114

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.
@@ -1,117 +1,214 @@
1
1
  <template>
2
- <div>
3
- <div class="col-xs-12 q-mb-md">
4
- <div class="row justify-end">
5
- <q-btn
6
- label="Adicionar despesa"
7
- icon="add"
8
- size="md"
9
- color="primary"
10
- class="q-ml-sm"
11
- @click="dialogs.expense.visible = true"
12
- />
13
- </div>
14
- </div>
15
- <Filters :filters="filters" @onRequest="onRequest" />
16
- <q-table
17
- flat
18
- :loading="isLoading"
19
- :rows="data"
20
- :columns="settings.columns"
21
- v-model:pagination="pagination"
22
- @request="onRequest"
23
- row-key="id"
24
- style="min-height: 90vh"
25
- :rows-per-page-options="[5, 10, 15, 20, 25, 50]"
26
- >
27
- <template v-slot:body="props">
28
- <q-tr :props="props">
29
- <q-td key="id" :props="props">
30
- <q-btn
31
- outline
32
- dense
33
- :to="{ name: 'PayDetails', params: { id: props.row.id } }"
34
- :label="props.cols[0].value"
35
- :style="{ color: props.row.color_status }"
36
- class="full-width"
37
- />
38
- </q-td>
39
- <q-td key="tipo" :props="props">{{ props.cols[1].value }}</q-td>
40
- <q-td key="description" :props="props">
41
- {{ props.row.description }}
42
- </q-td>
43
- <q-td key="dataVencimento" :props="props">{{
44
- props.cols[3].value
45
- }}</q-td>
46
- <q-td key="categoria" :props="props">{{ props.cols[4].value }}</q-td>
47
- <q-td key="fornecedor" :props="props">{{ props.cols[5].value }}</q-td>
48
- <q-td
49
- key="status"
50
- :props="props"
51
- :style="{ color: props.row.color_status }"
52
- >
53
- {{ $t(`invoice.statuses.${props.row.status}`) }}
54
- </q-td>
55
- <q-td key="preco" :props="props">{{ props.cols[7].value }}</q-td>
56
- </q-tr>
57
- </template>
58
- </q-table>
59
-
60
- <q-dialog
61
- v-model="dialogs.orders.visible"
62
- transition-show="scale"
63
- transition-hide="scale"
64
- >
65
- <q-card
66
- class="text-white"
67
- style="background-color: #00519b; width: 300px"
2
+ <div class="payable-invoices">
3
+ <section class="payable-invoices__panel">
4
+ <header class="payable-invoices__heading">
5
+ <div>
6
+ <span class="payable-invoices__eyebrow">DESPESAS</span>
7
+ <h2>Faturas de pagamento</h2>
8
+ <p>Acompanhe vencimentos e valores das contas da sua empresa.</p>
9
+ </div>
10
+ <q-card flat class="payable-total-card">
11
+ <div class="payable-total-card__icon">
12
+ <q-icon name="payments" />
13
+ </div>
14
+ <div class="payable-total-card__copy">
15
+ <span>Valor total a pagar</span>
16
+ <strong>{{ filteredTotalAmount }}</strong>
17
+ <small>Soma de todas as faturas filtradas</small>
18
+ </div>
19
+ </q-card>
20
+ </header>
21
+
22
+ <q-table
23
+ flat
24
+ class="payable-invoices__table"
25
+ :loading="isLoading"
26
+ :rows="data"
27
+ :columns="settings.columns"
28
+ v-model:pagination="pagination"
29
+ @request="onRequest"
30
+ row-key="id"
31
+ :rows-per-page-options="[10, 25, 50]"
32
+ :grid="$q.screen.lt.sm"
33
+ no-data-label="Nenhuma fatura encontrada"
34
+ no-results-label="Nenhuma fatura corresponde aos filtros"
35
+ loading-label="Carregando despesas..."
36
+ rows-per-page-label="Linhas por página"
68
37
  >
69
- <q-card-section>
70
- <div class="row items-center">
71
- <div class="text-h6">
72
- {{ $t("finance.orders") }} da Fatura #{{ dialogs.orders.invoice }}
73
- </div>
74
- <q-space />
75
- <q-btn icon="close" color="white" flat round dense v-close-popup />
38
+ <template v-slot:body="props">
39
+ <q-tr :props="props" class="payable-invoices__row">
40
+ <q-td key="id" :props="props">
41
+ <q-btn
42
+ flat
43
+ dense
44
+ no-caps
45
+ icon="receipt_long"
46
+ :label="`#${props.row.id}`"
47
+ :to="{ name: 'PayDetails', params: { id: props.row.id } }"
48
+ class="payable-invoice-link"
49
+ />
50
+ </q-td>
51
+ <q-td key="tipo" :props="props">
52
+ <q-badge class="payable-type-badge">{{ props.row.tipo }}</q-badge>
53
+ </q-td>
54
+ <q-td key="description" :props="props" class="payable-description-cell">
55
+ {{ props.row.description }}
56
+ </q-td>
57
+ <q-td key="dataVencimento" :props="props">
58
+ <div class="payable-date-cell">
59
+ <q-icon name="event" />
60
+ <span>{{ props.row.dataVencimentoLabel }}</span>
61
+ </div>
62
+ </q-td>
63
+ <q-td key="categoria" :props="props">{{ props.row.categoryName }}</q-td>
64
+ <q-td key="fornecedor" :props="props">{{ props.row.fornecedor }}</q-td>
65
+ <q-td key="status" :props="props">
66
+ <q-badge
67
+ rounded
68
+ class="payable-status-badge"
69
+ :style="statusStyle(props.row.color_status)"
70
+ >
71
+ <span class="payable-status-badge__dot" />
72
+ {{ statusLabel(props.row.status) }}
73
+ </q-badge>
74
+ </q-td>
75
+ <q-td key="preco" :props="props" class="payable-price-cell">
76
+ {{ props.cols[7].value }}
77
+ </q-td>
78
+ </q-tr>
79
+ </template>
80
+
81
+ <template v-slot:item="props">
82
+ <div class="col-12">
83
+ <q-card flat bordered class="payable-mobile-card">
84
+ <div class="payable-mobile-card__top">
85
+ <q-btn
86
+ flat
87
+ dense
88
+ no-caps
89
+ icon="receipt_long"
90
+ :label="`#${props.row.id}`"
91
+ :to="{ name: 'PayDetails', params: { id: props.row.id } }"
92
+ class="payable-invoice-link"
93
+ />
94
+ <q-badge
95
+ rounded
96
+ class="payable-status-badge"
97
+ :style="statusStyle(props.row.color_status)"
98
+ >
99
+ <span class="payable-status-badge__dot" />
100
+ {{ statusLabel(props.row.status) }}
101
+ </q-badge>
102
+ </div>
103
+ <div class="payable-mobile-card__type">{{ props.row.tipo }}</div>
104
+ <p class="payable-mobile-card__description">{{ props.row.description }}</p>
105
+ <div class="payable-mobile-card__grid">
106
+ <div>
107
+ <span>Vencimento</span>
108
+ <strong>{{ props.row.dataVencimentoLabel }}</strong>
109
+ </div>
110
+ <div>
111
+ <span>Valor</span>
112
+ <strong>{{ props.cols[7].value }}</strong>
113
+ </div>
114
+ <div>
115
+ <span>Categoria</span>
116
+ <strong>{{ props.row.categoryName }}</strong>
117
+ </div>
118
+ <div>
119
+ <span>Fornecedor</span>
120
+ <strong>{{ props.row.fornecedor }}</strong>
121
+ </div>
122
+ </div>
123
+ <div class="payable-mobile-card__actions">
124
+ <q-btn
125
+ flat
126
+ dense
127
+ no-caps
128
+ icon="link"
129
+ color="primary"
130
+ :label="ordersLabel(props.row.pedidos)"
131
+ :disable="!props.row.pedidos.length"
132
+ @click="seeOrdersList(props.row.pedidos, props.row.id)"
133
+ />
134
+ <q-btn
135
+ unelevated
136
+ dense
137
+ no-caps
138
+ color="primary"
139
+ label="Abrir fatura"
140
+ :to="{ name: 'PayDetails', params: { id: props.row.id } }"
141
+ />
142
+ </div>
143
+ </q-card>
76
144
  </div>
145
+ </template>
146
+ </q-table>
147
+ </section>
148
+
149
+ <q-dialog v-model="dialogs.orders.visible" transition-show="scale" transition-hide="scale">
150
+ <q-card class="payable-orders-dialog">
151
+ <q-card-section class="payable-orders-dialog__header">
152
+ <div>
153
+ <span>DETALHES DA FATURA</span>
154
+ <h3>{{ $t("finance.orders") }} da fatura #{{ dialogs.orders.invoice }}</h3>
155
+ </div>
156
+ <q-btn icon="close" flat round dense v-close-popup aria-label="Fechar" />
77
157
  </q-card-section>
78
-
79
- <q-card-section class="q-pt-none">
80
- <q-markup-table>
81
- <tbody>
82
- <tr v-for="(orderId, index) in dialogs.orders.items" :key="index">
83
- <td class="text-center">
84
- <q-btn
85
- flat
86
- color="primary"
87
- :label="`Ver ${$t('finance.order')} #${orderId}`"
88
- :to="{
89
- name: 'PurchasingOrderDetails',
90
- params: {
91
- id: orderId,
92
- },
93
- }"
94
- />
95
- </td>
96
- </tr>
97
- </tbody>
98
- </q-markup-table>
99
- </q-card-section>
158
+ <div class="payable-orders-dialog__list">
159
+ <q-list separator>
160
+ <q-item v-for="(orderId, index) in dialogs.orders.items" :key="index">
161
+ <q-item-section avatar>
162
+ <q-avatar color="blue-1" text-color="primary" icon="receipt" />
163
+ </q-item-section>
164
+ <q-item-section>{{ $t("finance.order") }} #{{ orderId }}</q-item-section>
165
+ <q-item-section side>
166
+ <q-btn
167
+ flat
168
+ dense
169
+ no-caps
170
+ color="primary"
171
+ label="Abrir"
172
+ :to="{ name: 'PurchasingOrderDetails', params: { id: orderId } }"
173
+ />
174
+ </q-item-section>
175
+ </q-item>
176
+ </q-list>
177
+ </div>
100
178
  </q-card>
101
179
  </q-dialog>
102
180
 
103
181
  <q-dialog v-model="dialogs.expense.visible">
104
- <q-card style="width: 700px; max-width: 80vw">
105
- <q-card-section class="row items-center">
106
- <div class="text-h6">Nova despesa</div>
107
- <q-space />
108
- <q-btn icon="close" flat round dense v-close-popup />
182
+ <q-card class="payable-expense-dialog">
183
+ <q-card-section class="payable-expense-dialog__header">
184
+ <div>
185
+ <span>NOVA DESPESA</span>
186
+ <h3>Adicionar despesa</h3>
187
+ <p>Preencha os dados para registrar um pagamento ou despesa recorrente.</p>
188
+ </div>
189
+ <q-btn icon="close" flat round dense v-close-popup aria-label="Fechar" />
109
190
  </q-card-section>
110
- <q-card-section>
111
- <CreateExpense @created="onRequest({ pagination, filters })" />
191
+ <q-card-section class="payable-expense-dialog__body">
192
+ <CreateExpense @created="onExpenseCreated" />
112
193
  </q-card-section>
113
194
  </q-card>
114
195
  </q-dialog>
196
+
197
+ <FilterDrawer
198
+ v-model="filtersDrawerOpen"
199
+ eyebrow="CONTAS A PAGAR"
200
+ title="Refinar despesas"
201
+ description="Escolha os critérios para atualizar a listagem."
202
+ :show-apply="false"
203
+ @clear="clearFilters"
204
+ >
205
+ <Filters
206
+ :filters="filters"
207
+ variant="receivables-drawer"
208
+ :show-category="true"
209
+ @onRequest="onFilterRequest"
210
+ />
211
+ </FilterDrawer>
115
212
  </div>
116
213
  </template>
117
214
 
@@ -123,11 +220,13 @@ import {
123
220
  } from "@freteclick/quasar-common-ui/src/utils/formatter";
124
221
  import CreateExpense from "../expense/CreateExpense";
125
222
  import { date } from "quasar";
223
+ import FilterDrawer from "@freteclick/quasar-common-ui/src/components/common/FilterDrawer.vue";
126
224
  import Filters from "../../common/Filters.vue";
127
225
 
128
226
  export default {
129
227
  components: {
130
228
  CreateExpense,
229
+ FilterDrawer,
131
230
  Filters,
132
231
  },
133
232
 
@@ -167,18 +266,21 @@ export default {
167
266
  return `#${val}`;
168
267
  },
169
268
  label: "ID",
269
+ sortable: false,
170
270
  },
171
271
  {
172
272
  name: "tipo",
173
273
  align: "left",
174
274
  field: "tipo",
175
275
  label: "Tipo",
276
+ sortable: false,
176
277
  },
177
278
  {
178
279
  name: "description",
179
280
  align: "left",
180
281
  field: "description",
181
282
  label: this.$t("finance.description"),
283
+ sortable: false,
182
284
  },
183
285
  {
184
286
  name: "dataVencimento",
@@ -188,33 +290,38 @@ export default {
188
290
  return formatDateYmdTodmY(val);
189
291
  },
190
292
  label: "Data vencimento",
293
+ sortable: false,
191
294
  },
192
295
  {
193
296
  name: "categoria",
194
297
  field: "categoryName",
195
298
  align: "left",
196
299
  label: "Categoria",
300
+ sortable: false,
197
301
  },
198
302
  {
199
303
  name: "fornecedor",
200
304
  field: "fornecedor",
201
305
  align: "left",
202
306
  label: "Fornecedor",
307
+ sortable: false,
203
308
  },
204
309
  {
205
310
  name: "status",
206
311
  field: "status",
207
312
  align: "left",
208
313
  label: "Status",
314
+ sortable: false,
209
315
  },
210
316
  {
211
317
  name: "preco",
212
318
  field: "preco",
213
- align: "left",
319
+ align: "right",
214
320
  format: (val, row) => {
215
321
  return formatMoney(val, "BRL", "pt-br");
216
322
  },
217
323
  label: "Preço",
324
+ sortable: false,
218
325
  },
219
326
  ],
220
327
  }),
@@ -229,6 +336,8 @@ export default {
229
336
  visible: false,
230
337
  },
231
338
  },
339
+ filtersDrawerOpen: false,
340
+ isClearingFilters: false,
232
341
  data: [],
233
342
  filters: {
234
343
  orderType: {
@@ -241,13 +350,14 @@ export default {
241
350
  },
242
351
  text: null,
243
352
  status: statuses[0],
353
+ category: { label: "Todas", value: null },
244
354
  company: null,
245
355
  },
246
356
  pagination: {
247
357
  sortBy: "dataVencimento",
248
- descending: false,
358
+ descending: true,
249
359
  page: 1,
250
- rowsPerPage: 25,
360
+ rowsPerPage: 10,
251
361
  rowsNumber: 10,
252
362
  },
253
363
  };
@@ -260,8 +370,15 @@ export default {
260
370
  violations: "payInvoice/violations",
261
371
  items: "payInvoice/items",
262
372
  totalItems: "payInvoice/totalItems",
373
+ totalAmount: "payInvoice/totalAmount",
263
374
  myCompany: "people/currentCompany",
264
375
  }),
376
+
377
+ filteredTotalAmount() {
378
+ if (this.totalAmount === null || this.totalAmount === undefined) return "—";
379
+
380
+ return formatMoney(Number(this.totalAmount) || 0, "BRL", "pt-br");
381
+ },
265
382
  },
266
383
 
267
384
  watch: {
@@ -282,36 +399,44 @@ export default {
282
399
  items(items) {
283
400
  if (!items) return;
284
401
 
285
- let data = [];
286
-
287
- for (let i in items) {
288
- let item = items[i];
289
- let orders = [];
290
-
291
- for (let o in item.order) {
292
- orders.push(
293
- item.order[o].order["@id"].match(
294
- /^\/purchasing\/orders\/([a-z0-9-]*)$/
295
- )[1]
296
- );
297
- }
298
-
299
- data.push({
300
- "@id": item["@id"],
301
- id: item["@id"].match(/^\/finance\/pay\/([a-z0-9-]*)$/)[1],
302
- tipo: this.$t(item.invoice_type),
402
+ this.data = items.map((item) => {
403
+ const orderEntries = Array.isArray(item.order) ? item.order : [];
404
+ const orders = orderEntries
405
+ .map((entry) => {
406
+ const orderUri = entry && entry.order && entry.order["@id"];
407
+ const match = String(orderUri || "").match(
408
+ /\/purchasing\/orders\/([a-z0-9-]+)$/i
409
+ );
410
+
411
+ return match ? match[1] : null;
412
+ })
413
+ .filter(Boolean);
414
+ const firstOrder =
415
+ (orderEntries.find((entry) => entry && entry.order) || {}).order || {};
416
+ const provider = firstOrder.provider || {};
417
+ const invoiceUri = String(item["@id"] || "");
418
+ const invoiceId = invoiceUri.match(/\/finance\/pay\/([a-z0-9-]+)$/i);
419
+
420
+ return {
421
+ "@id": invoiceUri,
422
+ id: (invoiceId && invoiceId[1]) || item.id,
423
+ tipo: item.invoice_type ? this.$t(item.invoice_type) : "—",
303
424
  pedidos: orders,
304
- description: item.description,
305
- color_status: item.status.color,
306
- dataVencimento: item.dueDate,
307
- fornecedor: `${item.order[0].order.provider.name} ${item.order[0].order.provider.alias}`,
308
- status: item.status.status,
309
- categoryName: item.categoryName,
310
- preco: item.price,
311
- });
312
- }
313
-
314
- this.data = data;
425
+ description: item.description || "—",
426
+ color_status: (item.status && item.status.color) || "",
427
+ dataVencimento: item.dueDate || "",
428
+ dataVencimentoLabel: item.dueDate
429
+ ? formatDateYmdTodmY(item.dueDate)
430
+ : "—",
431
+ fornecedor:
432
+ [provider.name, provider.alias]
433
+ .filter((value, index, values) => value && values.indexOf(value) === index)
434
+ .join(" · ") || "—",
435
+ status: (item.status && item.status.status) || "",
436
+ categoryName: item.categoryName || "—",
437
+ preco: item.price || 0,
438
+ };
439
+ });
315
440
  },
316
441
  },
317
442
 
@@ -337,23 +462,73 @@ export default {
337
462
  this.dialogs.orders.visible = true;
338
463
  },
339
464
 
465
+ ordersLabel(orders) {
466
+ const count = Array.isArray(orders) ? orders.length : 0;
467
+ return count === 1 ? "1 pedido" : `${count} pedidos`;
468
+ },
469
+
470
+ statusLabel(status) {
471
+ return status ? this.$t(`invoice.statuses.${status}`) : "—";
472
+ },
473
+
474
+ statusStyle(color) {
475
+ return color ? { color } : {};
476
+ },
477
+
478
+ openFiltersDrawer() {
479
+ this.filtersDrawerOpen = true;
480
+ },
481
+
482
+ openExpenseDialog() {
483
+ this.dialogs.expense.visible = true;
484
+ },
485
+
486
+ onExpenseCreated() {
487
+ this.dialogs.expense.visible = false;
488
+ this.pagination.page = 1;
489
+ this.onRequest({ pagination: this.pagination });
490
+ },
491
+
492
+ clearFilters() {
493
+ this.isClearingFilters = true;
494
+ this.filters.text = null;
495
+ this.filters.orderType = { label: "Todos", value: null };
496
+ this.filters.status = { label: "Todos", value: null };
497
+ this.filters.category = { label: "Todas", value: null };
498
+ this.filters.date = { from: "", to: "" };
499
+ this.pagination.page = 1;
500
+
501
+ this.$nextTick(() => {
502
+ this.isClearingFilters = false;
503
+ this.onRequest({ pagination: this.pagination });
504
+ });
505
+ },
506
+
507
+ onFilterRequest() {
508
+ if (this.isClearingFilters) return;
509
+
510
+ this.pagination.page = 1;
511
+ this.onRequest({ pagination: this.pagination });
512
+ },
513
+
340
514
  onRequest(props) {
341
515
  if (this.isLoading) return;
342
516
 
343
- let pagination = props ? props.pagination : this.pagination;
517
+ const requestedPagination =
518
+ props && props.pagination ? props.pagination : this.pagination;
519
+ const page = Number(requestedPagination.page) || 1;
520
+ const rowsPerPage = Number(requestedPagination.rowsPerPage) || 10;
344
521
 
345
- let { page, rowsPerPage, sortBy, descending } = pagination;
346
-
347
- let params = { itemsPerPage: rowsPerPage, page };
522
+ const params = { itemsPerPage: rowsPerPage, page };
348
523
 
349
524
  params.from = this.formatDate(this.filters.date.from) || "";
350
525
  params.to = this.formatDate(this.filters.date.to) || "";
351
- params.orderType = this.filters.orderType.value || "";
526
+ params.orderType = (this.filters.orderType && this.filters.orderType.value) || "";
352
527
 
353
528
  if (this.filters.text != null && this.filters.text.length > 0) {
354
529
  if (this.filters.text.length < 2) return;
355
530
 
356
- params["searchBy"] = this.filters.text;
531
+ params.searchBy = this.filters.text.trim();
357
532
  }
358
533
 
359
534
  if (this.filters.status != null) {
@@ -366,6 +541,10 @@ export default {
366
541
  }
367
542
  }
368
543
 
544
+ if (this.filters.category && this.filters.category.value) {
545
+ params.category = this.filters.category.value;
546
+ }
547
+
369
548
  if (this.filters.company != null) {
370
549
  params["myCompany"] = this.filters.company.id;
371
550
  }
@@ -374,15 +553,436 @@ export default {
374
553
  params["order.order"] = this.orderId;
375
554
  }
376
555
 
377
- params["invoice[dueDate]"] = "desc";
378
-
379
556
  this.getItems(params).then(() => {
380
- this.pagination.page = page;
381
- this.pagination.rowsPerPage = rowsPerPage;
382
- this.pagination.sortBy = sortBy;
383
- this.pagination.descending = descending;
557
+ this.pagination = {
558
+ ...this.pagination,
559
+ page,
560
+ rowsPerPage,
561
+ sortBy: "dataVencimento",
562
+ descending: true,
563
+ };
384
564
  });
385
565
  },
386
566
  },
387
567
  };
388
568
  </script>
569
+
570
+ <style lang="scss" scoped>
571
+ .payable-invoices {
572
+ width: 100%;
573
+ min-width: 0;
574
+ color: #1b2d43;
575
+ }
576
+
577
+ .payable-invoices__panel {
578
+ width: 100%;
579
+ min-width: 0;
580
+ box-sizing: border-box;
581
+ overflow: hidden;
582
+ border: 1px solid #e5ebf3;
583
+ border-radius: 19px;
584
+ background: #fff;
585
+ box-shadow: 0 12px 32px rgba(25, 48, 78, 0.05);
586
+ }
587
+
588
+ .payable-invoices__heading {
589
+ display: flex;
590
+ align-items: center;
591
+ justify-content: space-between;
592
+ gap: 22px;
593
+ padding: 22px 25px;
594
+ border-bottom: 1px solid #edf1f6;
595
+ }
596
+
597
+ .payable-invoices__eyebrow {
598
+ color: #7890aa;
599
+ font-size: 10px;
600
+ font-weight: 750;
601
+ letter-spacing: 0.13em;
602
+ }
603
+
604
+ .payable-invoices__heading h2 {
605
+ margin: 5px 0 0;
606
+ color: #1a2e45;
607
+ font-size: 19px;
608
+ font-weight: 720;
609
+ letter-spacing: -0.02em;
610
+ }
611
+
612
+ .payable-invoices__heading p {
613
+ margin: 5px 0 0;
614
+ color: #7b8b9c;
615
+ font-size: 12px;
616
+ }
617
+
618
+ .payable-total-card {
619
+ display: flex;
620
+ width: min(100%, 410px);
621
+ min-width: 280px;
622
+ align-items: center;
623
+ gap: 12px;
624
+ margin-left: auto;
625
+ padding: 12px 16px;
626
+ border: 1px solid #e7edf4;
627
+ border-radius: 14px;
628
+ background: #fbfdff;
629
+ }
630
+
631
+ .payable-total-card__icon {
632
+ display: grid;
633
+ width: 42px;
634
+ height: 42px;
635
+ flex: 0 0 auto;
636
+ place-items: center;
637
+ border-radius: 13px;
638
+ color: #27805a;
639
+ background: #e8f7ef;
640
+ font-size: 21px;
641
+ }
642
+
643
+ .payable-total-card__copy {
644
+ display: flex;
645
+ min-width: 0;
646
+ flex-direction: column;
647
+ }
648
+
649
+ .payable-total-card__copy > span {
650
+ color: #6f7f91;
651
+ font-size: 11px;
652
+ font-weight: 650;
653
+ }
654
+
655
+ .payable-total-card__copy strong {
656
+ overflow: hidden;
657
+ margin-top: 2px;
658
+ color: #1a2e45;
659
+ font-size: clamp(17px, 1.5vw, 22px);
660
+ font-weight: 750;
661
+ letter-spacing: -0.035em;
662
+ text-overflow: ellipsis;
663
+ white-space: nowrap;
664
+ }
665
+
666
+ .payable-total-card__copy small {
667
+ margin-top: 1px;
668
+ color: #93a0af;
669
+ font-size: 10px;
670
+ }
671
+
672
+ .payable-invoices__table {
673
+ width: 100%;
674
+ min-width: 0;
675
+ color: #33465b;
676
+ }
677
+
678
+ .payable-invoices__table :deep(.q-table thead tr) {
679
+ height: 48px;
680
+ background: #f8fafc;
681
+ }
682
+
683
+ .payable-invoices__table :deep(.q-table thead th) {
684
+ padding: 0 16px;
685
+ color: #7b8b9c;
686
+ font-size: 10px;
687
+ font-weight: 750;
688
+ letter-spacing: 0.07em;
689
+ text-transform: uppercase;
690
+ white-space: nowrap;
691
+ }
692
+
693
+ .payable-invoices__table :deep(.q-table tbody td) {
694
+ height: 64px;
695
+ padding: 10px 16px;
696
+ border-color: #edf1f5;
697
+ font-size: 12px;
698
+ white-space: nowrap;
699
+ }
700
+
701
+ .payable-invoices__table :deep(.q-table tbody tr) {
702
+ transition: background-color 0.15s ease;
703
+ }
704
+
705
+ .payable-invoices__table :deep(.q-table tbody tr:hover) {
706
+ background: #f8fbff;
707
+ }
708
+
709
+ .payable-invoices__table :deep(.q-table__bottom) {
710
+ min-height: 58px;
711
+ padding: 8px 18px;
712
+ border-top: 1px solid #edf1f5;
713
+ color: #718096;
714
+ font-size: 12px;
715
+ }
716
+
717
+ .payable-invoice-link {
718
+ border-radius: 8px;
719
+ color: #245d9c;
720
+ font-size: 12px;
721
+ font-weight: 750;
722
+ }
723
+
724
+ .payable-invoice-link :deep(.q-icon) {
725
+ font-size: 17px;
726
+ }
727
+
728
+ .payable-type-badge {
729
+ padding: 6px 9px;
730
+ border-radius: 7px;
731
+ color: #526b86;
732
+ background: #eef3f8;
733
+ font-size: 10px;
734
+ font-weight: 650;
735
+ }
736
+
737
+ .payable-description-cell {
738
+ max-width: 240px;
739
+ overflow: hidden;
740
+ text-overflow: ellipsis;
741
+ white-space: nowrap;
742
+ }
743
+
744
+ .payable-date-cell {
745
+ display: flex;
746
+ align-items: center;
747
+ gap: 7px;
748
+ white-space: nowrap;
749
+ }
750
+
751
+ .payable-date-cell .q-icon {
752
+ color: #8294a8;
753
+ font-size: 16px;
754
+ }
755
+
756
+ .payable-status-badge {
757
+ display: inline-flex;
758
+ align-items: center;
759
+ gap: 6px;
760
+ padding: 6px 9px;
761
+ border-radius: 20px;
762
+ background: #f0f4f7;
763
+ font-size: 10px;
764
+ font-weight: 700;
765
+ }
766
+
767
+ .payable-status-badge__dot {
768
+ width: 6px;
769
+ height: 6px;
770
+ border-radius: 50%;
771
+ background: currentColor;
772
+ }
773
+
774
+ .payable-price-cell {
775
+ color: #1e3e5e;
776
+ font-weight: 750;
777
+ white-space: nowrap;
778
+ }
779
+
780
+ .payable-mobile-card {
781
+ margin: 4px 0;
782
+ padding: 16px;
783
+ border-color: #e7edf4;
784
+ border-radius: 15px;
785
+ background: #fff;
786
+ }
787
+
788
+ .payable-mobile-card__top,
789
+ .payable-mobile-card__actions {
790
+ display: flex;
791
+ align-items: center;
792
+ justify-content: space-between;
793
+ gap: 10px;
794
+ }
795
+
796
+ .payable-mobile-card__type {
797
+ margin: 6px 0 0;
798
+ color: #728398;
799
+ font-size: 11px;
800
+ }
801
+
802
+ .payable-mobile-card__description {
803
+ margin: 8px 0 14px;
804
+ color: #33465b;
805
+ font-size: 13px;
806
+ font-weight: 600;
807
+ }
808
+
809
+ .payable-mobile-card__grid {
810
+ display: grid;
811
+ grid-template-columns: repeat(2, minmax(0, 1fr));
812
+ gap: 13px 10px;
813
+ }
814
+
815
+ .payable-mobile-card__grid > div {
816
+ display: flex;
817
+ min-width: 0;
818
+ flex-direction: column;
819
+ gap: 4px;
820
+ }
821
+
822
+ .payable-mobile-card__grid span {
823
+ color: #8795a5;
824
+ font-size: 10px;
825
+ }
826
+
827
+ .payable-mobile-card__grid strong {
828
+ overflow: hidden;
829
+ color: #263b52;
830
+ font-size: 12px;
831
+ font-weight: 650;
832
+ text-overflow: ellipsis;
833
+ white-space: nowrap;
834
+ }
835
+
836
+ .payable-mobile-card__actions {
837
+ margin-top: 16px;
838
+ padding-top: 12px;
839
+ border-top: 1px solid #edf1f5;
840
+ }
841
+
842
+ .payable-orders-dialog {
843
+ width: min(100%, 620px);
844
+ overflow: hidden;
845
+ border-radius: 18px;
846
+ }
847
+
848
+ .payable-orders-dialog {
849
+ display: flex;
850
+ width: min(100%, 520px);
851
+ max-height: min(82vh, 760px);
852
+ flex-direction: column;
853
+ }
854
+
855
+ .payable-orders-dialog__header,
856
+ .payable-expense-dialog__header {
857
+ display: flex;
858
+ align-items: center;
859
+ justify-content: space-between;
860
+ gap: 12px;
861
+ padding: 20px 22px;
862
+ border-bottom: 1px solid #edf1f6;
863
+ }
864
+
865
+ .payable-orders-dialog__header span,
866
+ .payable-expense-dialog__header span {
867
+ color: #7890aa;
868
+ font-size: 9px;
869
+ font-weight: 750;
870
+ letter-spacing: 0.12em;
871
+ }
872
+
873
+ .payable-orders-dialog__header h3,
874
+ .payable-expense-dialog__header h3 {
875
+ margin: 5px 0 0;
876
+ color: #1a2e45;
877
+ font-size: 17px;
878
+ font-weight: 700;
879
+ }
880
+
881
+ .payable-orders-dialog__list {
882
+ min-height: 0;
883
+ flex: 1 1 auto;
884
+ overflow-y: auto;
885
+ overscroll-behavior: contain;
886
+ }
887
+
888
+ .payable-expense-dialog__body {
889
+ min-height: 0;
890
+ flex: 1 1 auto;
891
+ overflow-y: auto;
892
+ padding: 22px 26px 26px;
893
+ }
894
+
895
+ .payable-expense-dialog {
896
+ display: flex;
897
+ width: min(100%, 780px);
898
+ max-height: min(92vh, 900px);
899
+ flex-direction: column;
900
+ overflow: hidden;
901
+ border-radius: 20px;
902
+ }
903
+
904
+ .payable-expense-dialog__header {
905
+ padding: 22px 26px 19px;
906
+ border-bottom: 0;
907
+ color: #fff;
908
+ background: linear-gradient(115deg, #15395d 0%, #1b568b 100%);
909
+ }
910
+
911
+ .payable-expense-dialog__header span {
912
+ color: #c3d8ee;
913
+ }
914
+
915
+ .payable-expense-dialog__header h3 {
916
+ color: #fff;
917
+ font-size: 21px;
918
+ }
919
+
920
+ .payable-expense-dialog__header p {
921
+ margin: 5px 0 0;
922
+ color: #d6e4f2;
923
+ font-size: 12px;
924
+ }
925
+
926
+ .payable-expense-dialog__header .q-btn {
927
+ color: #fff;
928
+ }
929
+
930
+ @media (min-width: 601px) {
931
+ .payable-invoices__table :deep(.q-table__container),
932
+ .payable-invoices__table :deep(.q-table__middle) {
933
+ width: 100%;
934
+ max-width: 100%;
935
+ min-width: 0;
936
+ }
937
+
938
+ .payable-invoices__table :deep(.q-table__middle) {
939
+ overflow-x: auto !important;
940
+ overflow-y: hidden;
941
+ overscroll-behavior-x: contain;
942
+ -webkit-overflow-scrolling: touch;
943
+ }
944
+
945
+ .payable-invoices__table :deep(.q-table__middle > table) {
946
+ min-width: 1280px;
947
+ }
948
+ }
949
+
950
+ @media (max-width: 900px) {
951
+ .payable-invoices__heading {
952
+ align-items: flex-start;
953
+ flex-direction: column;
954
+ }
955
+
956
+ .payable-total-card {
957
+ width: 100%;
958
+ min-width: 0;
959
+ margin-left: 0;
960
+ }
961
+ }
962
+
963
+ @media (max-width: 600px) {
964
+ .payable-invoices__heading {
965
+ gap: 14px;
966
+ padding: 18px 17px;
967
+ }
968
+
969
+ .payable-mobile-card__actions {
970
+ flex-wrap: wrap;
971
+ }
972
+
973
+ .payable-expense-dialog {
974
+ width: calc(100vw - 24px);
975
+ max-height: 92vh;
976
+ border-radius: 17px;
977
+ }
978
+
979
+ .payable-expense-dialog__header {
980
+ padding: 19px 18px 16px;
981
+ }
982
+
983
+ .payable-expense-dialog__body {
984
+ padding: 17px 18px 20px;
985
+ }
986
+
987
+ }
988
+ </style>