+
+
+ {{ t('components.smartCard.noData') }}
+
-
-
+
+
@@ -186,6 +149,19 @@ async function onLoad(...params) {
+
+
+
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
new file mode 100644
index 000000000..06bd16723
--- /dev/null
+++ b/src/components/ui/VnFilterPanel.vue
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+ {{ t('Applied filters') }}
+
+
+
+
+
+ {{ t('Remove filters') }}
+
+
+ {{ t('Refresh') }}
+
+
+
+
+
+
+ {{ t(`You didn't enter any filter`) }}
+
+
+
+
+
+ {{ chip.label }}:
+ "{{ chip.value }}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+es:
+ You didn't enter any filter: No has introducido ningún filtro
+ Applied filters: Filtros aplicados
+ Remove filters: Eliminar filtros
+ Refresh: Refrescar
+ Search: Buscar
+ Yes: Si
+ No: No
+
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
new file mode 100644
index 000000000..04f4d7245
--- /dev/null
+++ b/src/components/ui/VnSearchbar.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.info }}
+
+
+
+
+
+
+
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
new file mode 100644
index 000000000..10e80c727
--- /dev/null
+++ b/src/composables/useArrayData.js
@@ -0,0 +1,149 @@
+import { onMounted, ref, computed } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+import axios from 'axios';
+import { useArrayDataStore } from 'stores/useArrayDataStore';
+
+const arrayDataStore = useArrayDataStore();
+
+export function useArrayData(key, userOptions) {
+ if (!key) throw new Error('ArrayData: A key is required to use this composable');
+
+ if (!arrayDataStore.get(key)) {
+ arrayDataStore.set(key);
+ }
+
+ const store = arrayDataStore.get(key);
+ const hasMoreData = ref(false);
+ const router = useRouter();
+ const route = useRoute();
+ let canceller = null;
+
+ const page = ref(1);
+
+ if (typeof userOptions === 'object') {
+ if (userOptions.filter) store.filter = userOptions.filter;
+ if (userOptions.url) store.url = userOptions.url;
+ if (userOptions.limit) store.limit = userOptions.limit;
+ if (userOptions.order) store.order = userOptions.order;
+ }
+
+ onMounted(() => {
+ const query = route.query;
+ if (query.params) {
+ store.userParams = JSON.parse(query.params);
+ }
+ });
+
+ async function fetch({ append = false }) {
+ if (!store.url) return;
+
+ cancelRequest();
+ canceller = new AbortController();
+
+ const filter = {
+ order: store.order,
+ limit: store.limit,
+ skip: store.skip,
+ };
+
+ Object.assign(filter, store.userFilter);
+ Object.assign(store.filter, filter);
+
+ const params = {
+ filter: JSON.stringify(filter),
+ };
+
+ Object.assign(params, store.userParams);
+
+ const response = await axios.get(store.url, {
+ signal: canceller.signal,
+ params,
+ });
+
+ const { limit } = filter;
+
+ hasMoreData.value = response.data.length === limit;
+
+ if (append === true) {
+ if (!store.data) store.data = [];
+ for (const row of response.data) store.data.push(row);
+ }
+
+ if (append === false) {
+ store.data = response.data;
+
+ updateStateParams();
+ }
+
+ canceller = null;
+ }
+
+ function destroy() {
+ if (arrayDataStore.get(key)) {
+ arrayDataStore.clear(key);
+ }
+ }
+
+ function cancelRequest() {
+ if (canceller) {
+ canceller.abort();
+ canceller = null;
+ }
+ }
+
+ async function applyFilter({ filter, params }) {
+ if (filter) store.userFilter = filter;
+ if (params) store.userParams = Object.assign({}, params);
+
+ await fetch({ append: false });
+ }
+
+ async function addFilter({ filter, params }) {
+ if (filter) store.userFilter = Object.assign(store.userFilter, filter);
+ if (params) store.userParams = Object.assign(store.userParams, params);
+
+ await fetch({ append: false });
+ }
+
+ async function loadMore() {
+ if (!hasMoreData.value) return;
+
+ store.skip = store.limit * page.value;
+ page.value += 1;
+
+ await fetch({ append: true });
+ }
+
+ async function refresh() {
+ await fetch({ append: false });
+ }
+
+ function updateStateParams() {
+ const query = {};
+ if (store.order) query.order = store.order;
+ if (store.limit) query.limit = store.limit;
+ if (store.skip) query.skip = store.skip;
+ if (store.userParams && Object.keys(store.userParams).length !== 0)
+ query.params = JSON.stringify(store.userParams);
+
+ router.replace({
+ path: route.path,
+ query: query,
+ });
+ }
+
+ const totalRows = computed(() => store.data && store.data.length | 0);
+
+ return {
+ fetch,
+ applyFilter,
+ addFilter,
+ refresh,
+ destroy,
+ loadMore,
+ store,
+ hasMoreData,
+ totalRows,
+ updateStateParams,
+ };
+}
diff --git a/src/css/app.scss b/src/css/app.scss
index 73660398b..29e5bb600 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -13,3 +13,20 @@ a {
.link:hover {
color: $orange-4;
}
+
+// Removes chrome autofill background
+input:-webkit-autofill,
+select:-webkit-autofill {
+ color: $input-text-color !important;
+ font-family: $typography-font-family;
+ -webkit-text-fill-color: $input-text-color !important;
+ -webkit-background-clip: text !important;
+ background-clip: text !important;
+}
+
+body.body--light {
+ .q-header .q-toolbar {
+ background-color: white;
+ color: #555;
+ }
+}
diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss
index 3baae40e3..6ebf7a7a7 100644
--- a/src/css/quasar.variables.scss
+++ b/src/css/quasar.variables.scss
@@ -16,8 +16,6 @@ $primary: #ff9800;
$secondary: #26a69a;
$accent: #9c27b0;
-$dark: #1d1d1d;
-
$positive: #21ba45;
$negative: #c10015;
$info: #31ccec;
@@ -30,5 +28,4 @@ $border-thin-light: 1px solid $color-spacer-light;
$dark-shadow-color: #000;
$dark: #292929;
$layout-shadow-dark: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0px 10px rgba(0, 0, 0, 0.24);
-
$spacing-md: 16px;
diff --git a/src/filters/toDate.js b/src/filters/toDate.js
index 932c0557e..8fe8f3836 100644
--- a/src/filters/toDate.js
+++ b/src/filters/toDate.js
@@ -3,10 +3,14 @@ import { useI18n } from 'vue-i18n';
export default function (value, options = {}) {
if (!value) return;
- if (!options.dateStyle) options.dateStyle = 'short';
+ if (!options.dateStyle && !options.timeStyle) {
+ options.day = '2-digit';
+ options.month = '2-digit';
+ options.year = 'numeric';
+ }
const { locale } = useI18n();
const date = new Date(value);
- return new Intl.DateTimeFormat(locale.value, options).format(date)
-}
\ No newline at end of file
+ return new Intl.DateTimeFormat(locale.value, options).format(date);
+}
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index a28f6e1d7..a8d149d0a 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -2,13 +2,26 @@
import { useQuasar } from 'quasar';
import Navbar from 'src/components/NavBar.vue';
+import { useStateStore } from 'stores/useStateStore';
+
const quasar = useQuasar();
+const stateStore = useStateStore();
+
+
+
+
+
diff --git a/src/pages/Claim/Card/ClaimCard.vue b/src/pages/Claim/Card/ClaimCard.vue
index f66af86cb..e5116a360 100644
--- a/src/pages/Claim/Card/ClaimCard.vue
+++ b/src/pages/Claim/Card/ClaimCard.vue
@@ -1,12 +1,23 @@
-
+
+
+
+
@@ -19,3 +30,9 @@ const state = useState();
+
+
+es:
+ Search claim: Buscar reclamación
+ You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
+
diff --git a/src/pages/Claim/Card/ClaimRma.vue b/src/pages/Claim/Card/ClaimRma.vue
index 6f734b917..bf0e8294e 100644
--- a/src/pages/Claim/Card/ClaimRma.vue
+++ b/src/pages/Claim/Card/ClaimRma.vue
@@ -1,37 +1,49 @@
(claim = data)"
+ :filter="claimFilter"
+ @on-fetch="onFetch"
auto-load
/>
-
+
-
+
- {{
- t('claim.rma.user')
- }}
- {{
- row.worker.user.name
- }}
+
+ {{ t('claim.rma.user') }}
+
+
+ {{ row.worker.user.name }}
+
- {{
- t('claim.rma.created')
- }}
+
+ {{ t('claim.rma.created') }}
+
{{
toDate(row.created, {
@@ -126,31 +131,12 @@ function hide() {
-
+
-
-
-
-
- {{ t('globals.confirmRemove') }}
-
-
-
-
-
-
-
-
diff --git a/src/pages/Claim/ClaimFilter.vue b/src/pages/Claim/ClaimFilter.vue
new file mode 100644
index 000000000..dc33d1808
--- /dev/null
+++ b/src/pages/Claim/ClaimFilter.vue
@@ -0,0 +1,217 @@
+
+
+
+ (states = data)" auto-load />
+ (workers = data)"
+ auto-load
+ />
+
+
+
+ {{ t(`params.${tag.label}`) }}:
+ {{ formatFn(tag.value) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+en:
+ params:
+ search: Contains
+ clientFk: Customer
+ clientName: Customer
+ salesPersonFk: Salesperson
+ attenderFk: Attender
+ claimResponsibleFk: Responsible
+ claimStateFk: State
+ created: Created
+es:
+ params:
+ search: Contiene
+ clientFk: Cliente
+ clientName: Cliente
+ salesPersonFk: Comercial
+ attenderFk: Asistente
+ claimResponsibleFk: Responsable
+ claimStateFk: Estado
+ created: Creada
+ Customer ID: ID cliente
+ Client Name: Nombre del cliente
+ Salesperson: Comercial
+ Attender: Asistente
+ Responsible: Responsable
+ State: Estado
+ Item: Artículo
+ Created: Creada
+ More options: Más opciones
+
diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue
index 9ab22117e..708472108 100644
--- a/src/pages/Claim/ClaimList.vue
+++ b/src/pages/Claim/ClaimList.vue
@@ -1,32 +1,24 @@
+
+
+
+
+
+
-
+
-
+
- {{ row.client.name }}
-
-
-
+ {{ row.clientName }}
#{{ row.id }}
- {{ t('claim.list.customer') }}
- {{ row.client.name }}
+
+ {{ t('claim.list.customer') }}
+
+ {{ row.clientName }}
- {{ t('claim.list.assignedTo') }}
- {{ row.worker.user.name }}
+
+ {{ t('claim.list.assignedTo') }}
+
+ {{ row.workerName }}
- {{ t('claim.list.created') }}
- {{ toDate(row.created) }}
+
+ {{ t('claim.list.created') }}
+
+
+ {{ toDate(row.created) }}
+
- {{ t('claim.list.state') }}
+
+ {{ t('claim.list.state') }}
+
-
- {{ row.claimState.description }}
-
+
+ {{ row.stateDescription }}
+
@@ -111,16 +128,34 @@ function viewSummary(id) {
-->
-
- {{ t('components.smartCard.openCard') }}
+
+
+ {{ t('components.smartCard.openCard') }}
+
-
- {{ t('components.smartCard.openSummary') }}
+
+
+ {{ t('components.smartCard.openSummary') }}
+
- {{ t('components.smartCard.viewDescription') }}
+
+ {{ t('components.smartCard.viewDescription') }}
+
-
+
@@ -130,3 +165,9 @@ function viewSummary(id) {
+
+
+es:
+ Search claim: Buscar reclamación
+ You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
+
diff --git a/src/pages/Claim/ClaimMain.vue b/src/pages/Claim/ClaimMain.vue
index fd50f1521..e4350a6e7 100644
--- a/src/pages/Claim/ClaimMain.vue
+++ b/src/pages/Claim/ClaimMain.vue
@@ -1,12 +1,12 @@
-
+
diff --git a/src/pages/Claim/ClaimRmaList.vue b/src/pages/Claim/ClaimRmaList.vue
index 367169dc4..d4ef1fcf0 100644
--- a/src/pages/Claim/ClaimRmaList.vue
+++ b/src/pages/Claim/ClaimRmaList.vue
@@ -4,16 +4,15 @@ import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
import Paginate from 'src/components/PaginateData.vue';
+import { useArrayData } from 'src/composables/useArrayData';
+import VnConfirm from 'src/components/ui/VnConfirm.vue';
const quasar = useQuasar();
const { t } = useI18n();
-const rmas = ref([]);
-const card = ref(null);
-
-function onFetch(data) {
- rmas.value = data.value;
-}
+const arrayData = useArrayData('ClaimRmaList');
+const isLoading = ref(false);
+const input = ref();
const newRma = ref({
code: '',
@@ -24,46 +23,38 @@ function onInputUpdate(value) {
newRma.value.code = value.toUpperCase();
}
-function submit() {
+async function submit() {
const formData = newRma.value;
if (formData.code === '') return;
- axios
- .post('ClaimRmas', formData)
- .then(() => {
- newRma.value = {
- code: '',
- crated: new Date(),
- };
- })
- .then(() => card.value.refresh());
+ isLoading.value = true;
+ await axios.post('ClaimRmas', formData);
+ await arrayData.refresh();
+ isLoading.value = false;
+ input.value.$el.focus();
+
+ newRma.value = {
+ code: '',
+ created: new Date(),
+ };
}
-const confirmShown = ref(false);
-const rmaId = ref(null);
function confirm(id) {
- confirmShown.value = true;
- rmaId.value = id;
-}
-
-function remove() {
- const id = rmaId.value;
- axios
- .delete(`ClaimRmas/${id}`)
- .then(() => {
- confirmShown.value = false;
-
- quasar.notify({
- type: 'positive',
- message: 'Entry deleted',
- icon: 'check',
- });
+ quasar
+ .dialog({
+ component: VnConfirm,
})
- .then(() => card.value.refresh());
+ .onOk(() => remove(id));
}
-function hide() {
- rmaId.value = null;
+async function remove(id) {
+ await axios.delete(`ClaimRmas/${id}`);
+ await arrayData.refresh();
+ quasar.notify({
+ type: 'positive',
+ message: t('globals.rowRemoved'),
+ icon: 'check',
+ });
}
@@ -73,34 +64,74 @@ function hide() {
- {{ rmas.length }} {{ t('claim.rmaList.records') }}
+
+ {{ arrayData.totalRows }} {{ t('claim.rmaList.records') }}
+
-
-
+
-
+
- {{ t('claim.rmaList.code') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ t('claim.rmaList.code')
+ }}
{{ row.code }}
-
+
{{ t('globals.remove') }}
@@ -111,20 +142,6 @@ function hide() {
-
-
-
-
-
- {{ t('globals.confirmRemove') }}
-
-
-
-
-
-
-
-
diff --git a/src/pages/Customer/CustomerCreate.vue b/src/pages/Customer/CustomerCreate.vue
index 52a9ccbc5..f58a230d9 100644
--- a/src/pages/Customer/CustomerCreate.vue
+++ b/src/pages/Customer/CustomerCreate.vue
@@ -1,13 +1,16 @@
@@ -20,7 +23,7 @@ watch(() => customer.name, () => {
label="Your name *"
hint="Name and surname"
lazy-rules
- :rules="[val => val && val.length > 0 || 'Please type something']"
+ :rules="[(val) => (val && val.length > 0) || 'Please type something']"
/>
customer.name, () => {
label="Your age *"
lazy-rules
:rules="[
- val => val !== null && val !== '' || 'Please type your age',
- val => val > 0 && val < 100 || 'Please type a real age'
+ (val) => (val !== null && val !== '') || 'Please type your age',
+ (val) => (val > 0 && val < 100) || 'Please type a real age',
]"
/>
diff --git a/src/pages/Customer/CustomerFilter.vue b/src/pages/Customer/CustomerFilter.vue
new file mode 100644
index 000000000..0e9c5be83
--- /dev/null
+++ b/src/pages/Customer/CustomerFilter.vue
@@ -0,0 +1,200 @@
+
+
+
+ (provinces = data)" auto-load />
+ (zones = data)" auto-load />
+ (workers = data)"
+ auto-load
+ />
+
+
+
+ {{ t(`params.${tag.label}`) }}:
+ {{ formatFn(tag.value) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+en:
+ params:
+ search: Contains
+ fi: FI
+ name: Name
+ socialName: Social Name
+ salesPersonFk: Salesperson
+ provinceFk: Province
+ city: City
+ phone: Phone
+ email: Email
+ zoneFk: Zone
+ postcode: Postcode
+es:
+ params:
+ search: Contiene
+ fi: NIF
+ name: Nombre
+ socialName: Razón Social
+ salesPersonFk: Comercial
+ provinceFk: Provincia
+ city: Ciudad
+ phone: Teléfono
+ email: Email
+ zoneFk: Zona
+ postcode: CP
+ FI: NIF
+ Name: Nombre
+ Social Name: Razón social
+ Salesperson: Comercial
+ Province: Provincia
+ City: Ciudad
+ More options: Más opciones
+ Phone: Teléfono
+ Email: Email
+ Zone: Zona
+ Postcode: Código postal
+
diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue
index c4d54a355..745731962 100644
--- a/src/pages/Customer/CustomerList.vue
+++ b/src/pages/Customer/CustomerList.vue
@@ -1,14 +1,23 @@
+
+
+
+
+
+
-
+
-
+
{{ row.name }}
#{{ row.id }}
+
- {{ t('customer.list.email') }}
+
+ {{ t('customer.list.email') }}
+
{{ row.email }}
- {{ t('customer.list.phone') }}
+
+ {{ t('customer.list.phone') }}
+
{{ row.phone }}
@@ -69,11 +97,27 @@ function viewSummary(id) {
-->
-
- {{ t('components.smartCard.openCard') }}
+
+
+ {{ t('components.smartCard.openCard') }}
+
-
- {{ t('components.smartCard.openSummary') }}
+
+
+ {{ t('components.smartCard.openSummary') }}
+