Compare commits
No commits in common. "dev" and "fix_vnTable_onDataSaved" have entirely different histories.
dev
...
fix_vnTabl
366
CHANGELOG.md
366
CHANGELOG.md
|
@ -1,369 +1,3 @@
|
||||||
# Version 24.48 - 2024-11-25
|
|
||||||
|
|
||||||
### Added 🆕
|
|
||||||
|
|
||||||
- chore: correct checkNotification (fix_customer_issues) by:alexm
|
|
||||||
- chore: perf (warmFix_order_equalSalix) by:alexm
|
|
||||||
- chore: refs #6818 add spaces by:jorgep
|
|
||||||
- chore: refs #6818 drop useless code & comment by:jorgep
|
|
||||||
- chore: refs #7273 sticky add btn & refactor by:jorgep
|
|
||||||
- chore: refs #7524 fix test by:jorgep
|
|
||||||
- chore: refs #8039 not required by:alexm
|
|
||||||
- chore: refs #8078 fiz tests by:jorgep
|
|
||||||
- chore: refs #8078 rollback ref by:jorgep
|
|
||||||
- chore: remove console.log (warmFix_invoiceOut_Global) by:alexm
|
|
||||||
- chore: typo (fix_itemType-redirection) by:alexm
|
|
||||||
- feat: #6943 use openURL quasar by:Javier Segarra
|
|
||||||
- feat: #7782 add cypress report by:Javier Segarra
|
|
||||||
- feat: #7782 cypress.config watchForFileChanges by:Javier Segarra
|
|
||||||
- feat: #7782 npm run resetDatabase by:Javier Segarra
|
|
||||||
- feat: #7782 waitUntil domContentLoad by:Javier Segarra
|
|
||||||
- feat: added composable to confirm orders by:Jon
|
|
||||||
- feat: add /reports in gitignore (warmFix_reports_in_gitignore) by:alexm
|
|
||||||
- feat: apply changes for customerModule by:Javier Segarra
|
|
||||||
- feat: disabled buttons by:Javier Segarra
|
|
||||||
- feat: move buttons to DescriptorMenu by:Javier Segarra
|
|
||||||
- feat: refs #6818 add icon by:jorgep
|
|
||||||
- feat: refs #6818 fetch url & default channel by:jorgep
|
|
||||||
- feat: refs #6818 saysimple integration by:jorgep
|
|
||||||
- feat: refs #6839 module searching (6839-addSearchMenu) by:jorgep
|
|
||||||
- feat: refs #6839 normalize search by:jorgep
|
|
||||||
- feat: refs #6919 sync entry data by:jorgep
|
|
||||||
- feat: refs #7006 itemType basic data new inputs by:guillermo
|
|
||||||
- feat: refs #7006 itemTypeLog added by:guillermo
|
|
||||||
- feat: refs #7193 modified parking to use the scope and corrected small errors by:Jon
|
|
||||||
- feat: refs #7206 added inactive label and corrected minor errors by:Jon
|
|
||||||
- feat: refs #7308 #7308 remove warnings related to useSession by:Javier Segarra
|
|
||||||
- feat: refs #7349 usa back con permisos by:jgallego
|
|
||||||
- feat: refs #7524 add front test by:jorgep
|
|
||||||
- feat: refs #7874 improve vn-notes ui by:jorgep
|
|
||||||
- feat: refs #7970 notify changes by:Jon
|
|
||||||
- feat(): refs #8039 canceledError not notify by:alexm
|
|
||||||
- feat: refs #8039 notify error unify by:alexm
|
|
||||||
- feat: refs #8039 show duplicate request in local by:alexm
|
|
||||||
- feat: refs #8078 add shortcut multi selection by:jorgep
|
|
||||||
- feat: refs #8078 add tests by:jorgep
|
|
||||||
- feat: refs#8087 Redadas en travel by:Carlos Andrés
|
|
||||||
- feat: refs #8087 Traspasar redadas a travels by:Carlos Andrés
|
|
||||||
- feat: remove comments by:Javier Segarra
|
|
||||||
- feat(Supplier): add companySize by:alexm
|
|
||||||
- feat: use composable to unify logic by:Javier Segarra
|
|
||||||
- feat(VnInput): empty to null by:alexm
|
|
||||||
- feat(VnSelect): order data equal salix by:alexm
|
|
||||||
- feat(VnSelect): refs #7136 add scroll (7136-vnSelect_paginate_simplify_2) by:alexm
|
|
||||||
|
|
||||||
### Changed 📦
|
|
||||||
|
|
||||||
- chore: perf (warmFix_order_equalSalix) by:alexm
|
|
||||||
- chore: refs #7273 sticky add btn & refactor by:jorgep
|
|
||||||
- fix: better performance (warmFix_accountAcls) by:alexm
|
|
||||||
- perf: minor bugs detected by:Javier Segarra
|
|
||||||
- perf: refs #6943 #6943 merge command by:Javier Segarra
|
|
||||||
- perf: refs #7283 #7283 declare composable inst4ead code duplicated by:Javier Segarra
|
|
||||||
- perf: refs #7283 #7283 handle composable i18n by:Javier Segarra
|
|
||||||
- perf: refs #7283 #7283 handle i18n by:Javier Segarra
|
|
||||||
- perf: refs #7283 #7283 i18n params by:Javier Segarra
|
|
||||||
- perf: refs #7308 #7308 remove comments by:Javier Segarra
|
|
||||||
- perf: remove appendParams by:Javier Segarra
|
|
||||||
- perf: use const in VnLocation by:Javier Segarra
|
|
||||||
- perf: use required instead :required="true" by:Javier Segarra
|
|
||||||
- refactor: apply QPopupProxy by:wbuezas
|
|
||||||
- refactor: changed confirmOrder directory by:Jon
|
|
||||||
- refactor: change keyup.enter for update:model-value by:wbuezas
|
|
||||||
- refactor(InvoiceInBasicData): use VnDms by:alexm
|
|
||||||
- refactor: modified composable by:Jon
|
|
||||||
- refactor: refs #6818 change channel source by:jorgep
|
|
||||||
- refactor: refs #6818 channel logic by:jorgep
|
|
||||||
- refactor: refs #6919 export filter by:jorgep
|
|
||||||
- refactor: refs #7132 1st wave of changes in global translations files by:Jon
|
|
||||||
- refactor: refs #7132 account's module translations by:Jon
|
|
||||||
- refactor: refs #7132 customer's module translations by:Jon
|
|
||||||
- refactor: refs #7132 deleted pageTitles repeated by:Jon
|
|
||||||
- refactor: refs #7132 delete duplicate translations' keys by:Jon
|
|
||||||
- refactor: refs #7132 deleted useless code by:Jon
|
|
||||||
- refactor: refs #7132 global translations files changed by:Jon
|
|
||||||
- refactor: refs #7266 Changed method name by:guillermo
|
|
||||||
- refactor: refs #7950 Created cmr model by:guillermo
|
|
||||||
- refactor: refs #7970 added emit by:Jon
|
|
||||||
- refactor: refs #7970 refactored VnConfirm to emit events by:Jon
|
|
||||||
- refactor: refs #8185 modified LeftMenu to avoid duplicates by:Jon
|
|
||||||
- refactor: remove unused variable by:wbuezas
|
|
||||||
- refactor: revert catalog changes by:Jon
|
|
||||||
- refactor: small change by:wbuezas
|
|
||||||
- test: refactor e2e by:alexm
|
|
||||||
- test: refs #8039 add hasNotify and, refactor: agencyWorkCenter test by:alexm
|
|
||||||
|
|
||||||
### Fixed 🛠️
|
|
||||||
|
|
||||||
- chore: refs #7524 fix test by:jorgep
|
|
||||||
- fix: better performance (warmFix_accountAcls) by:alexm
|
|
||||||
- fix: catalog view category and type filter by:wbuezas
|
|
||||||
- fix: category and tags filters by:Jon
|
|
||||||
- fix: changed route.query by:Jon
|
|
||||||
- fix: change type vnput by:Javier Segarra
|
|
||||||
- fix(ClaimList): stateCode orderBy priority by:alexm
|
|
||||||
- fix: entryFilters by:carlossa
|
|
||||||
- fix: filter panel by:Jon
|
|
||||||
- fix(InvoiceOutGlobal): parallelism by:alexm
|
|
||||||
- fix: itemBotanical by:Javier Segarra
|
|
||||||
- fix: itemType redirection and fix filters by:alexm
|
|
||||||
- fix: logout spec (warmFix_logout.spec) by:alexm
|
|
||||||
- fix: merge errors by:alexm
|
|
||||||
- fix: order catalog by:wbuezas
|
|
||||||
- fix: order catalog fixes by:wbuezas
|
|
||||||
- fix: refs #6818 use right icon by:jorgep
|
|
||||||
- fix: refs #6896 fixed module problems by:Jon
|
|
||||||
- fix: refs #7193 fixed e2e test by:Jon
|
|
||||||
- fix: refs #7206 deleted duplicate code by:Jon
|
|
||||||
- fix: refs #7273 use same filter by:jorgep
|
|
||||||
- fix: refs #7283 #7283 bugs by:Javier Segarra
|
|
||||||
- fix: refs #7283 #7283 ItemDiary subToolbar by:Javier Segarra
|
|
||||||
- fix: refs #7283 #7283 ItemSummary bugs by:Javier Segarra
|
|
||||||
- fix: refs #7283 Account image resolution by:guillermo
|
|
||||||
- fix: refs #7283 css by:jorgep
|
|
||||||
- fix: refs #7283 filter by:carlossa
|
|
||||||
- fix: refs #7283 fix image by:carlossa
|
|
||||||
- fix: refs #7283 fix pr by:carlossa
|
|
||||||
- fix: refs #7283 fix preview by:carlossa
|
|
||||||
- fix: refs #7283 fix required by:carlossa
|
|
||||||
- fix: refs #7283 item filters by:carlossa
|
|
||||||
- fix: refs #7283 itemtype fix by:carlossa
|
|
||||||
- fix: refs #7283 order translation by:carlossa
|
|
||||||
- fix: refs #7283 preview by:carlossa
|
|
||||||
- fix: refs #7283 tooltips !Item by:Javier Segarra
|
|
||||||
- fix: refs #7306 clean warning by:carlossa
|
|
||||||
- fix: refs #7310 clean warning by:carlossa
|
|
||||||
- fix: refs #7323 locale #7396 by:jorgep
|
|
||||||
- fix: refs #7323 show advanced fields by:jorgep
|
|
||||||
- fix: refs #7349 dependencia no usada by:jgallego
|
|
||||||
- fix: refs #7524 e2e & worker module by:jorgep
|
|
||||||
- fix: refs #7874 add title by:jorgep
|
|
||||||
- fix: refs #7874 show name by:jorgep
|
|
||||||
- fix: refs #7943 use correct data-key by:jorgep
|
|
||||||
- fix: refs #7943 use summary by:jorgep
|
|
||||||
- fix: refs #8039 bad tests by:alexm
|
|
||||||
- fix: refs #8039 o not handle unnecessary errors by:alexm
|
|
||||||
- fix: refs #8078 e2e #7970 by:jorgep
|
|
||||||
- fix: refs #8078 handleSelection by:jorgep
|
|
||||||
- fix: refs #8078 improve cy command (8078-enableMultiSelection) by:jorgep
|
|
||||||
- fix: refs #8078 improve handleSelection by:jorgep
|
|
||||||
- fix: reset category by:wbuezas
|
|
||||||
- fix: tag chips by:Jon
|
|
||||||
- fix: vnSearchbar spec (warmFix_vnSearchBar.spec) by:alexm
|
|
||||||
- fix(VnSelect): setOptions when applyFilter by:alexm
|
|
||||||
- fix: worker test e2e by:Jon
|
|
||||||
- Merge branch 'dev' into fix_customer_issues by:Javier Segarra
|
|
||||||
- refactor: revert catalog changes by:Jon
|
|
||||||
- refs #7283 fix conflicts by:carlossa
|
|
||||||
- refs #7283 fix descriptorproxy by:carlossa
|
|
||||||
- refs #7283 fixedPrice by:carlossa
|
|
||||||
- refs #7283 fixedPrices by:carlossa
|
|
||||||
- refs #7283 fix itemFixed by:carlossa
|
|
||||||
- refs #7283 fix itemFixedPrice by:carlossa
|
|
||||||
- refs #7283 fix itemMigration by:carlossa
|
|
||||||
- refs #7283 fix itemMigration list filters by:carlossa
|
|
||||||
- refs #7283 fix items by:carlossa
|
|
||||||
- refs #7283 fix items error get images by:carlossa
|
|
||||||
- refs #7283 fix items images by:carlossa
|
|
||||||
- refs #7283 fix request by:carlossa
|
|
||||||
- refs #7283 fix searchbar by:carlossa
|
|
||||||
- refs #7283 fix viewSummary by:carlossa
|
|
||||||
- refs #7283 fix yml list basicData by:carlossa
|
|
||||||
- refs #7283 itemRequest fix by:carlossa
|
|
||||||
- refs #7283 itemRequest fix deny by:carlossa
|
|
||||||
- refs #7283 itemRequest fix reload by:carlossa
|
|
||||||
- refs #72983 fix filters by:carlossa
|
|
||||||
- revert: commit by:Javier Segarra
|
|
||||||
- revert e57a253c6f649382da187d1129449d265fb26d3b by:Javier Segarra
|
|
||||||
- test: #8162 fix clientList spec by:Javier Segarra
|
|
||||||
- test: #8162 fix vnLocation spec by:Javier Segarra
|
|
||||||
- test: fix arrayData by:Javier Segarra
|
|
||||||
- test: fix e2e by:alexm
|
|
||||||
- test: fix e2e by:Javier Segarra
|
|
||||||
- test: refs #8039 fix WorkerNotification e2e by:alexm
|
|
||||||
- test: refs #8039 fix ZoneWarehouse e2e by:alexm
|
|
||||||
- warmfix: ItemLastEntries to date (origin/warmfix_itemLastEntriesFilter) by:Javier Segarra
|
|
||||||
|
|
||||||
# Version 24.40 - 2024-10-02
|
|
||||||
|
|
||||||
### Added 🆕
|
|
||||||
|
|
||||||
- chore: refs #4074 admit several acls by:jorgep
|
|
||||||
- chore: refs #4074 drop workerCreate by:jorgep
|
|
||||||
- chore: refs #4074 fix tests by:jorgep
|
|
||||||
- chore: refs #4074 wip replace useRole for useAcl by:jorgep
|
|
||||||
- chore: refs #7155 remove console.log by:alexm
|
|
||||||
- chore: refs #7155 typo by:alexm
|
|
||||||
- chore: refs #7663 add test by:jorgep
|
|
||||||
- chore: refs #7663 create test wip by:jorgep
|
|
||||||
- chore: refs #7663 drop useless code (origin/7663-setWeight) by:jorgep
|
|
||||||
- chore: refs #7828 fix e2e by:jorgep
|
|
||||||
- feat(AccountBasicData): add twoFactorFk by:alexm
|
|
||||||
- feat: add max rule by:Javier Segarra
|
|
||||||
- feat: add shortcut add event in some subSections by:Javier Segarra
|
|
||||||
- feat: add shortcut more buttons (origin/add_shortcut_add_subSections) by:Javier Segarra
|
|
||||||
- feat: add tooltip CustomerNewCustomAgent by:Javier Segarra
|
|
||||||
- feat: apply color when today by:Javier Segarra
|
|
||||||
- feat: change label because its more natural by:Javier Segarra
|
|
||||||
- feat: change order by:Javier Segarra
|
|
||||||
- feat: change QBadge color by:Javier Segarra
|
|
||||||
- feat: change url CustomerList by:Javier Segarra
|
|
||||||
- feat: copy customer countryFk by:Javier Segarra
|
|
||||||
- feat: create VnSelectEnum and add in AccountBasicData and ClaimBasicData by:alexm
|
|
||||||
- feat: CustomerBalance by:Javier Segarra
|
|
||||||
- feat: CustomerConsumptionFilter by:Javier Segarra
|
|
||||||
- feat: customer consumption (origin/7830-customerDesplegables, 7830-customerDesplegables) by:alexm
|
|
||||||
- feat: CustomerCreateTicket by:Javier Segarra
|
|
||||||
- feat: CustomerCredit section by:Javier Segarra
|
|
||||||
- feat: CustomerGreuges by:Javier Segarra
|
|
||||||
- feat: CustomerSample to VnTable by:Javier Segarra
|
|
||||||
- feat: global handler (origin/fix_global_handler, fix_global_handler) by:alexm
|
|
||||||
- feat: goToSupplier by:Javier Segarra
|
|
||||||
- feat: handle newValue by:Javier Segarra
|
|
||||||
- feat: handle same multiple CP by:Javier Segarra
|
|
||||||
- feat: hide menus on small view (origin/hideMenu) by:jorgep
|
|
||||||
- feat: minor changes by:Javier Segarra
|
|
||||||
- feat: orderCreateDialog by:Javier Segarra
|
|
||||||
- feat: refs #4074 drop useless code by:jorgep
|
|
||||||
- feat: refs #4074 useAcl in vnSelectDialog by:jorgep
|
|
||||||
- feat: refs #6346 new wagon type section by:Jon
|
|
||||||
- feat: refs #7404 add m3 and fix detail by:pablone
|
|
||||||
- feat: refs #7404 add some style to the form and reorganize fields by:pablone
|
|
||||||
- feat: refs #7404 add travel m3 to reserves form by:pablone
|
|
||||||
- feat: refs #7404 style dynamic text color by:pablone
|
|
||||||
- feat: refs #7404 travel m3 form by:pablone
|
|
||||||
- feat: refs #7500 added VnImg to show files by:Jon
|
|
||||||
- feat: refs #7663 add setWeight menu opt (wip) by:jorgep
|
|
||||||
- feat: refs #7663 fine tunning by:jorgep
|
|
||||||
- feat: refs #7828 create axios instance which no manage errors (origin/7828-makeCorrectCalls) by:jorgep
|
|
||||||
- feat: refs #7828 useAcl & cherry pick mail data worker by:jorgep
|
|
||||||
- feat: remove cli warnings by:Javier Segarra
|
|
||||||
- feat: show preparation field by:Javier Segarra
|
|
||||||
- feat: stateGroupedFilter by:Javier Segarra
|
|
||||||
- feat: translations fixed by:jgallego
|
|
||||||
- feat(TravelList): add daysOnward by:alexm
|
|
||||||
- feat: travel m3 by:pablone
|
|
||||||
- feat: use disableInifiniteScroll property by:Javier Segarra
|
|
||||||
- feat: VnImg draggable by:Javier Segarra
|
|
||||||
- feat: vnLocation changes by:Javier Segarra
|
|
||||||
- feat: vnSelect exprBuilder by:Javier Segarra
|
|
||||||
- fix: refs #7404 remove some style by:pablone
|
|
||||||
- fix: refs #7404 style non center pop up (origin/7404-fixFront) by:pablone
|
|
||||||
- fix: refs #7404 translates and some minor style fixes by:pablone
|
|
||||||
- fix: styles by:Javier Segarra
|
|
||||||
- perf: improve style by:Javier Segarra
|
|
||||||
|
|
||||||
### Changed 📦
|
|
||||||
|
|
||||||
- perf: CustomerBalance by:Javier Segarra
|
|
||||||
- perf: CustomerBasicData by:Javier Segarra
|
|
||||||
- perf: CustomerBasicData.salesPersonFk by:Javier Segarra
|
|
||||||
- perf: CustomerSummary by:Javier Segarra
|
|
||||||
- perf: customerSummaryTable by:Javier Segarra
|
|
||||||
- perf: disable card option by:Javier Segarra
|
|
||||||
- perf: i18n by:Javier Segarra
|
|
||||||
- perf: improve by:Javier Segarra
|
|
||||||
- perf: improve style by:Javier Segarra
|
|
||||||
- perf: imrpove exprBuilder by:Javier Segarra
|
|
||||||
- perf: minor comments by:Javier Segarra
|
|
||||||
- perf: refs #6346 previous changes by:Jon
|
|
||||||
- perf: sendEmail customerConsumption by:Javier Segarra
|
|
||||||
- perf: solve reload CardSummary component by:Javier Segarra
|
|
||||||
- perf: update CustommerDescriptor by:Javier Segarra
|
|
||||||
- refactor: refs #4074 accept array by:jorgep
|
|
||||||
- refactor: refs #4074 rollback by:jorgep
|
|
||||||
- refactor: refs #4074 use acl & drop useless roles by:jorgep
|
|
||||||
- refactor: refs #4074 useAcl in navigationStore & router by:jorgep
|
|
||||||
- refactor: refs #4074 use fn (origin/4074-useAcls) by:jorgep
|
|
||||||
- refactor: refs #4074 use VnTitle by:jorgep
|
|
||||||
- refactor: refs #6346 deleted front error checking by:Jon
|
|
||||||
- refactor: refs #6346 requested changes by:Jon
|
|
||||||
- refactor: refs #6346 wagons to VnTable by:Jon
|
|
||||||
- refactor: refs #7500 deleted useless code by:Jon
|
|
||||||
- refactor: refs #7500 refactor vnimg when storage is dms by:Jon
|
|
||||||
- refactor: refs #7828 wip by:jorgep
|
|
||||||
|
|
||||||
### Fixed 🛠️
|
|
||||||
|
|
||||||
- chore: refs #4074 fix tests by:jorgep
|
|
||||||
- chore: refs #7828 fix e2e by:jorgep
|
|
||||||
- feat: refs #7404 add m3 and fix detail by:pablone
|
|
||||||
- feat: translations fixed by:jgallego
|
|
||||||
- fix: #5938 grouped filter by:Javier Segarra
|
|
||||||
- fix: #6943 fix customerSummaryTable by:Javier Segarra
|
|
||||||
- fix: #6943 show nickname salesPerson by:Javier Segarra
|
|
||||||
- fix: address-create i18n by:Javier Segarra
|
|
||||||
- fix: comments (origin/6943_fix_customer_module, 6943_fix_customer_module) by:Javier Segarra
|
|
||||||
- fix: CusomerSummary to Address by:Javier Segarra
|
|
||||||
- fix: CustomerAddress mobile by:Javier Segarra
|
|
||||||
- fix: CustomerBillingData by:Javier Segarra
|
|
||||||
- fix: Customerconsumption by:Javier Segarra
|
|
||||||
- fix: customer credit opinion by:alexm
|
|
||||||
- fix: CustomerCreditOpinion workerDescriptor by:Javier Segarra
|
|
||||||
- fix: CustomerDescriptorAccount by:Javier Segarra
|
|
||||||
- fix: CustomerDescriptor.bussinessTypeFk by:Javier Segarra
|
|
||||||
- fix: CustomerFilter by:Javier Segarra
|
|
||||||
- fix: CustomerGreuges by:Javier Segarra
|
|
||||||
- fix: CustomerMandates by:Javier Segarra
|
|
||||||
- fix: Customer module find salesPersons out of first get by:Javier Segarra
|
|
||||||
- fix: CustomerRecovery transalate label by:Javier Segarra
|
|
||||||
- fix: CustomerSamples by:Javier Segarra
|
|
||||||
- fix: customerSummaryToTicketList button by:Javier Segarra
|
|
||||||
- fix: CustomerWebPayment by:Javier Segarra
|
|
||||||
- fix: CustommerSummaryTable Proxy by:Javier Segarra
|
|
||||||
- fix: deleted code by:Jon
|
|
||||||
- fix: duplicate code by:alexm
|
|
||||||
- fix: emit:updateModelValue by:Javier Segarra
|
|
||||||
- fix: fixed wagon tests by:Jon
|
|
||||||
- fix: fix wagon list reload by:Jon
|
|
||||||
- fix: i18n en preparation label by:Javier Segarra
|
|
||||||
- fix: infiniteScroll by:Javier Segarra
|
|
||||||
- fix: isFullMovable checkbox by:Javier Segarra
|
|
||||||
- fix: merge conflicts by:Javier Segarra
|
|
||||||
- fix: merge in dev by:alexm
|
|
||||||
- fix: missing code by:Jon
|
|
||||||
- fix: Options VnSelect properties by:Javier Segarra
|
|
||||||
- fix: refs #4074 await to watch by:jorgep
|
|
||||||
- fix: refs #4074 drop wrong acl by:jorgep
|
|
||||||
- fix: refs #4074 workerCard data-key by:jorgep
|
|
||||||
- fix: refs #6346 fix list and create by:pablone
|
|
||||||
- fix: refs #6943 prevent null (origin/6943-warmfix-preventNull) by:jorgep
|
|
||||||
- fix: refs #7155 remove userParams in watcher (7155-travel_daysOnward) by:alexm
|
|
||||||
- fix: refs #7155 use chip-locale (origin/7155-travel_daysOnward_2, 7155-travel_daysOnward_2) by:alexm
|
|
||||||
- fix: refs #7404 remove console.log by:pablone
|
|
||||||
- fix: refs #7404 remove from test by:pablone
|
|
||||||
- fix: refs #7404 remove some style by:pablone
|
|
||||||
- fix: refs #7404 revert commit prevent production access by:pablone
|
|
||||||
- fix: refs #7404 style non center pop up (origin/7404-fixFront) by:pablone
|
|
||||||
- fix: refs #7404 translates and some minor style fixes by:pablone
|
|
||||||
- fix: refs #7500 fixed e2e test by:Jon
|
|
||||||
- fix: refs #7500 fixed showing images wrongly by:Jon
|
|
||||||
- fix: refs #7830 customer credit by:pablone
|
|
||||||
- fix: refs #7830 remove console.log by:pablone
|
|
||||||
- fix: remove console.log by:pablone
|
|
||||||
- fix: remove FetchData by:Javier Segarra
|
|
||||||
- fix: remove FIXME (origin/6943_fix_customerSummaryTable) by:Javier Segarra
|
|
||||||
- fix: remove print variable by:Javier Segarra
|
|
||||||
- fix: remove promise execution by:Javier Segarra
|
|
||||||
- fix: reset VnTable scroll properties by:Javier Segarra
|
|
||||||
- fix: rule by:Javier Segarra
|
|
||||||
- fix: solve conflicts from master to test by:Javier Segarra
|
|
||||||
- fix: split params (origin/warmfix-addSearchUrl) by:jorgep
|
|
||||||
- fix: state cell by:Javier Segarra
|
|
||||||
- fix: stop call back event hasMoreData by:Javier Segarra
|
|
||||||
- fix: styles by:Javier Segarra
|
|
||||||
- fix: SupplierFiscalData VnLocation (origin/fix_supplierFD_location) by:Javier Segarra
|
|
||||||
- fix: VnLocation test by:Javier Segarra
|
|
||||||
- fix(VnTable): header background-color by:alexm
|
|
||||||
- fix(VnTable): sanitizer value is defined by:carlossa
|
|
||||||
- fix: wagon reload (origin/FixWagonRedirect) by:Jon
|
|
||||||
- fix: workerDms filter workerFk by:alexm
|
|
||||||
- fix(WorkerList): add type email by:alexm
|
|
||||||
- Merge remote-tracking branch 'origin/7830-customerDesplegables' into 6943_fix_customerSummaryTable by:Javier Segarra
|
|
||||||
- refs #7155 scopeDays fix (origin/7155-scopeDays) by:carlossa
|
|
||||||
- revert: vnUSerLink change by:Javier Segarra
|
|
||||||
- test: fix test (7677_vnLocation_perf) by:Javier Segarra
|
|
||||||
|
|
||||||
# Version 24.38 - 2024-09-17
|
# Version 24.38 - 2024-09-17
|
||||||
|
|
||||||
### Added 🆕
|
### Added 🆕
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
const { defineConfig } = require('cypress');
|
const { defineConfig } = require('cypress');
|
||||||
// https://docs.cypress.io/app/tooling/reporters
|
|
||||||
// https://docs.cypress.io/app/references/configuration
|
|
||||||
// https://www.npmjs.com/package/cypress-mochawesome-reporter
|
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
|
@ -14,23 +11,12 @@ module.exports = defineConfig({
|
||||||
video: false,
|
video: false,
|
||||||
specPattern: 'test/cypress/integration/**/*.spec.js',
|
specPattern: 'test/cypress/integration/**/*.spec.js',
|
||||||
experimentalRunAllSpecs: true,
|
experimentalRunAllSpecs: true,
|
||||||
watchForFileChanges: true,
|
|
||||||
reporter: 'cypress-mochawesome-reporter',
|
|
||||||
reporterOptions: {
|
|
||||||
charts: true,
|
|
||||||
reportPageTitle: 'Cypress Inline Reporter',
|
|
||||||
reportFilename: '[status]_[datetime]-report',
|
|
||||||
embeddedScreenshots: true,
|
|
||||||
reportDir: 'test/cypress/reports',
|
|
||||||
inlineAssets: true,
|
|
||||||
},
|
|
||||||
component: {
|
component: {
|
||||||
componentFolder: 'src',
|
componentFolder: 'src',
|
||||||
testFiles: '**/*.spec.js',
|
testFiles: '**/*.spec.js',
|
||||||
supportFile: 'test/cypress/support/unit.js',
|
supportFile: 'test/cypress/support/unit.js',
|
||||||
},
|
},
|
||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
require('cypress-mochawesome-reporter/plugin')(on);
|
|
||||||
// implement node event listeners here
|
// implement node event listeners here
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "24.50.0",
|
"version": "24.38.0",
|
||||||
"description": "Salix frontend",
|
"description": "Salix frontend",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Verdnatura",
|
"author": "Verdnatura",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@8.15.1",
|
"packageManager": "pnpm@8.15.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"resetDatabase": "cd ../salix && gulp docker",
|
|
||||||
"lint": "eslint --ext .js,.vue ./",
|
"lint": "eslint --ext .js,.vue ./",
|
||||||
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||||
"test:e2e": "cypress open",
|
"test:e2e": "cypress open",
|
||||||
"test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run",
|
"test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run",
|
||||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest",
|
||||||
"test:unit:ci": "vitest run",
|
"test:unit:ci": "vitest run",
|
||||||
|
@ -43,7 +42,6 @@
|
||||||
"@vue/test-utils": "^2.4.4",
|
"@vue/test-utils": "^2.4.4",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"cypress": "^13.6.6",
|
"cypress": "^13.6.6",
|
||||||
"cypress-mochawesome-reporter": "^3.8.2",
|
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-cypress": "^2.13.3",
|
"eslint-plugin-cypress": "^2.13.3",
|
||||||
|
|
379
pnpm-lock.yaml
379
pnpm-lock.yaml
|
@ -70,9 +70,6 @@ devDependencies:
|
||||||
cypress:
|
cypress:
|
||||||
specifier: ^13.6.6
|
specifier: ^13.6.6
|
||||||
version: 13.6.6
|
version: 13.6.6
|
||||||
cypress-mochawesome-reporter:
|
|
||||||
specifier: ^3.8.2
|
|
||||||
version: 3.8.2(cypress@13.6.6)(mocha@10.7.3)
|
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.41.0
|
specifier: ^8.41.0
|
||||||
version: 8.56.0
|
version: 8.56.0
|
||||||
|
@ -832,8 +829,8 @@ packages:
|
||||||
vue-i18n:
|
vue-i18n:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@intlify/message-compiler': 10.0.0
|
'@intlify/message-compiler': 10.0.0-beta.5
|
||||||
'@intlify/shared': 10.0.0
|
'@intlify/shared': 10.0.0-beta.5
|
||||||
jsonc-eslint-parser: 1.4.1
|
jsonc-eslint-parser: 1.4.1
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
vue-i18n: 9.9.1(vue@3.4.19)
|
vue-i18n: 9.9.1(vue@3.4.19)
|
||||||
|
@ -847,11 +844,11 @@ packages:
|
||||||
'@intlify/message-compiler': 9.9.1
|
'@intlify/message-compiler': 9.9.1
|
||||||
'@intlify/shared': 9.9.1
|
'@intlify/shared': 9.9.1
|
||||||
|
|
||||||
/@intlify/message-compiler@10.0.0:
|
/@intlify/message-compiler@10.0.0-beta.5:
|
||||||
resolution: {integrity: sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==}
|
resolution: {integrity: sha512-hLLchnM1dmtSEruerkzvU9vePsLqBXz3RU85SCx/Vd12fFQiymP+/5Rn9MJ8MyfLmIOLDEx4PRh+/GkIQP6oog==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@intlify/shared': 10.0.0
|
'@intlify/shared': 10.0.0-beta.5
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -862,8 +859,8 @@ packages:
|
||||||
'@intlify/shared': 9.9.1
|
'@intlify/shared': 9.9.1
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
|
|
||||||
/@intlify/shared@10.0.0:
|
/@intlify/shared@10.0.0-beta.5:
|
||||||
resolution: {integrity: sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==}
|
resolution: {integrity: sha512-g9bq5Y1bOcC9qxtNk4UWtF3sXm6Wh0fGISb7vD5aLyF7yQv7ZFjxQjJzBP2GqG/9+PAGYutqjP1GGadNqFtyAQ==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -887,7 +884,7 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@intlify/bundle-utils': 4.0.0(vue-i18n@9.9.1)
|
'@intlify/bundle-utils': 4.0.0(vue-i18n@9.9.1)
|
||||||
'@intlify/shared': 10.0.0
|
'@intlify/shared': 10.0.0-beta.5
|
||||||
'@rollup/pluginutils': 4.2.1
|
'@rollup/pluginutils': 4.2.1
|
||||||
'@vue/compiler-sfc': 3.4.19
|
'@vue/compiler-sfc': 3.4.19
|
||||||
debug: 4.3.4(supports-color@8.1.1)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
|
@ -2002,10 +1999,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
fill-range: 7.0.1
|
fill-range: 7.0.1
|
||||||
|
|
||||||
/browser-stdout@1.3.1:
|
|
||||||
resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/browserslist@4.23.0:
|
/browserslist@4.23.0:
|
||||||
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
|
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
|
||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
|
@ -2113,16 +2106,6 @@ packages:
|
||||||
upper-case: 1.1.3
|
upper-case: 1.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/camelcase@5.3.1:
|
|
||||||
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/camelcase@6.3.0:
|
|
||||||
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/camelcase@7.0.1:
|
/camelcase@7.0.1:
|
||||||
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
|
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
@ -2272,22 +2255,6 @@ packages:
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/cliui@6.0.0:
|
|
||||||
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
|
||||||
dependencies:
|
|
||||||
string-width: 4.2.3
|
|
||||||
strip-ansi: 6.0.1
|
|
||||||
wrap-ansi: 6.2.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/cliui@7.0.4:
|
|
||||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
|
||||||
dependencies:
|
|
||||||
string-width: 4.2.3
|
|
||||||
strip-ansi: 6.0.1
|
|
||||||
wrap-ansi: 7.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/cliui@8.0.1:
|
/cliui@8.0.1:
|
||||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -2591,23 +2558,6 @@ packages:
|
||||||
/csstype@3.1.3:
|
/csstype@3.1.3:
|
||||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||||
|
|
||||||
/cypress-mochawesome-reporter@3.8.2(cypress@13.6.6)(mocha@10.7.3):
|
|
||||||
resolution: {integrity: sha512-oJZkNzhNmN9ZD+LmZyFuPb8aWaIijyHyqYh52YOBvR6B6ckfJNCHP3A98a+/nG0H4t46CKTNwo+wNpMa4d2kjA==}
|
|
||||||
engines: {node: '>=14'}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
cypress: '>=6.2.0'
|
|
||||||
dependencies:
|
|
||||||
commander: 10.0.1
|
|
||||||
cypress: 13.6.6
|
|
||||||
fs-extra: 10.1.0
|
|
||||||
mochawesome: 7.1.3(mocha@10.7.3)
|
|
||||||
mochawesome-merge: 4.3.0
|
|
||||||
mochawesome-report-generator: 6.2.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- mocha
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/cypress@13.6.6:
|
/cypress@13.6.6:
|
||||||
resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==}
|
resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==}
|
||||||
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
|
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
|
||||||
|
@ -2677,10 +2627,6 @@ packages:
|
||||||
time-zone: 1.0.0
|
time-zone: 1.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/dateformat@4.6.3:
|
|
||||||
resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/dayjs@1.11.10:
|
/dayjs@1.11.10:
|
||||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2730,29 +2676,6 @@ packages:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
supports-color: 8.1.1
|
supports-color: 8.1.1
|
||||||
|
|
||||||
/debug@4.3.7(supports-color@8.1.1):
|
|
||||||
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
|
|
||||||
engines: {node: '>=6.0'}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.3
|
|
||||||
supports-color: 8.1.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/decamelize@1.2.0:
|
|
||||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/decamelize@4.0.0:
|
|
||||||
resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/decompress-response@6.0.0:
|
/decompress-response@6.0.0:
|
||||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -2835,11 +2758,6 @@ packages:
|
||||||
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||||
|
|
||||||
/diff@5.2.0:
|
|
||||||
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
|
|
||||||
engines: {node: '>=0.3.1'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/doctrine@3.0.0:
|
/doctrine@3.0.0:
|
||||||
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
|
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
|
@ -3632,14 +3550,6 @@ packages:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
/find-up@4.1.0:
|
|
||||||
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
locate-path: 5.0.0
|
|
||||||
path-exists: 4.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/find-up@5.0.0:
|
/find-up@5.0.0:
|
||||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -3736,15 +3646,6 @@ packages:
|
||||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/fs-extra@10.1.0:
|
|
||||||
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
dependencies:
|
|
||||||
graceful-fs: 4.2.11
|
|
||||||
jsonfile: 6.1.0
|
|
||||||
universalify: 2.0.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fs-extra@11.2.0:
|
/fs-extra@11.2.0:
|
||||||
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
|
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
|
||||||
engines: {node: '>=14.14'}
|
engines: {node: '>=14.14'}
|
||||||
|
@ -3753,15 +3654,6 @@ packages:
|
||||||
jsonfile: 6.1.0
|
jsonfile: 6.1.0
|
||||||
universalify: 2.0.1
|
universalify: 2.0.1
|
||||||
|
|
||||||
/fs-extra@7.0.1:
|
|
||||||
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
|
|
||||||
engines: {node: '>=6 <7 || >=8'}
|
|
||||||
dependencies:
|
|
||||||
graceful-fs: 4.2.11
|
|
||||||
jsonfile: 4.0.0
|
|
||||||
universalify: 0.1.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fs-extra@9.1.0:
|
/fs-extra@9.1.0:
|
||||||
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
|
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -3783,10 +3675,6 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/fsu@1.1.1:
|
|
||||||
resolution: {integrity: sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/function-bind@1.1.2:
|
/function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
@ -3887,18 +3775,6 @@ packages:
|
||||||
once: 1.4.0
|
once: 1.4.0
|
||||||
path-is-absolute: 1.0.1
|
path-is-absolute: 1.0.1
|
||||||
|
|
||||||
/glob@8.1.0:
|
|
||||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
deprecated: Glob versions prior to v9 are no longer supported
|
|
||||||
dependencies:
|
|
||||||
fs.realpath: 1.0.0
|
|
||||||
inflight: 1.0.6
|
|
||||||
inherits: 2.0.4
|
|
||||||
minimatch: 5.1.6
|
|
||||||
once: 1.4.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/global-directory@4.0.1:
|
/global-directory@4.0.1:
|
||||||
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
|
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
@ -4313,11 +4189,6 @@ packages:
|
||||||
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
/is-plain-obj@2.1.0:
|
|
||||||
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-plain-obj@3.0.0:
|
/is-plain-obj@3.0.0:
|
||||||
resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==}
|
resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -4490,12 +4361,6 @@ packages:
|
||||||
resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
|
resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/jsonfile@4.0.0:
|
|
||||||
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
|
|
||||||
optionalDependencies:
|
|
||||||
graceful-fs: 4.2.11
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/jsonfile@6.1.0:
|
/jsonfile@6.1.0:
|
||||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4587,13 +4452,6 @@ packages:
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/locate-path@5.0.0:
|
|
||||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
p-locate: 4.1.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/locate-path@6.0.0:
|
/locate-path@6.0.0:
|
||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -4628,26 +4486,10 @@ packages:
|
||||||
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
|
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/lodash.isempty@4.4.0:
|
|
||||||
resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/lodash.isfunction@3.0.9:
|
|
||||||
resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/lodash.isobject@3.0.2:
|
|
||||||
resolution: {integrity: sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/lodash.isplainobject@4.0.6:
|
/lodash.isplainobject@4.0.6:
|
||||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/lodash.isstring@4.0.1:
|
|
||||||
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/lodash.kebabcase@4.1.1:
|
/lodash.kebabcase@4.1.1:
|
||||||
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -4710,13 +4552,6 @@ packages:
|
||||||
wrap-ansi: 6.2.0
|
wrap-ansi: 6.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/loose-envify@1.4.0:
|
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
js-tokens: 4.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/loupe@2.3.7:
|
/loupe@2.3.7:
|
||||||
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
|
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4887,79 +4722,6 @@ packages:
|
||||||
ufo: 1.4.0
|
ufo: 1.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/mocha@10.7.3:
|
|
||||||
resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==}
|
|
||||||
engines: {node: '>= 14.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
ansi-colors: 4.1.3
|
|
||||||
browser-stdout: 1.3.1
|
|
||||||
chokidar: 3.6.0
|
|
||||||
debug: 4.3.7(supports-color@8.1.1)
|
|
||||||
diff: 5.2.0
|
|
||||||
escape-string-regexp: 4.0.0
|
|
||||||
find-up: 5.0.0
|
|
||||||
glob: 8.1.0
|
|
||||||
he: 1.2.0
|
|
||||||
js-yaml: 4.1.0
|
|
||||||
log-symbols: 4.1.0
|
|
||||||
minimatch: 5.1.6
|
|
||||||
ms: 2.1.3
|
|
||||||
serialize-javascript: 6.0.2
|
|
||||||
strip-json-comments: 3.1.1
|
|
||||||
supports-color: 8.1.1
|
|
||||||
workerpool: 6.5.1
|
|
||||||
yargs: 16.2.0
|
|
||||||
yargs-parser: 20.2.9
|
|
||||||
yargs-unparser: 2.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mochawesome-merge@4.3.0:
|
|
||||||
resolution: {integrity: sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
fs-extra: 7.0.1
|
|
||||||
glob: 7.2.3
|
|
||||||
yargs: 15.4.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mochawesome-report-generator@6.2.0:
|
|
||||||
resolution: {integrity: sha512-Ghw8JhQFizF0Vjbtp9B0i//+BOkV5OWcQCPpbO0NGOoxV33o+gKDYU0Pr2pGxkIHnqZ+g5mYiXF7GMNgAcDpSg==}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
chalk: 4.1.2
|
|
||||||
dateformat: 4.6.3
|
|
||||||
escape-html: 1.0.3
|
|
||||||
fs-extra: 10.1.0
|
|
||||||
fsu: 1.1.1
|
|
||||||
lodash.isfunction: 3.0.9
|
|
||||||
opener: 1.5.2
|
|
||||||
prop-types: 15.8.1
|
|
||||||
tcomb: 3.2.29
|
|
||||||
tcomb-validation: 3.4.1
|
|
||||||
validator: 13.11.0
|
|
||||||
yargs: 17.7.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mochawesome@7.1.3(mocha@10.7.3):
|
|
||||||
resolution: {integrity: sha512-Vkb3jR5GZ1cXohMQQ73H3cZz7RoxGjjUo0G5hu0jLaW+0FdUxUwg3Cj29bqQdh0rFcnyV06pWmqmi5eBPnEuNQ==}
|
|
||||||
peerDependencies:
|
|
||||||
mocha: '>=7'
|
|
||||||
dependencies:
|
|
||||||
chalk: 4.1.2
|
|
||||||
diff: 5.2.0
|
|
||||||
json-stringify-safe: 5.0.1
|
|
||||||
lodash.isempty: 4.4.0
|
|
||||||
lodash.isfunction: 3.0.9
|
|
||||||
lodash.isobject: 3.0.2
|
|
||||||
lodash.isstring: 4.0.1
|
|
||||||
mocha: 10.7.3
|
|
||||||
mochawesome-report-generator: 6.2.0
|
|
||||||
strip-ansi: 6.0.1
|
|
||||||
uuid: 8.3.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ms@2.0.0:
|
/ms@2.0.0:
|
||||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||||
|
|
||||||
|
@ -5108,11 +4870,6 @@ packages:
|
||||||
is-wsl: 2.2.0
|
is-wsl: 2.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/opener@1.5.2:
|
|
||||||
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
|
|
||||||
hasBin: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/optionator@0.9.3:
|
/optionator@0.9.3:
|
||||||
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
|
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
@ -5158,13 +4915,6 @@ packages:
|
||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/p-limit@2.3.0:
|
|
||||||
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
dependencies:
|
|
||||||
p-try: 2.2.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/p-limit@3.1.0:
|
/p-limit@3.1.0:
|
||||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -5179,13 +4929,6 @@ packages:
|
||||||
yocto-queue: 1.0.0
|
yocto-queue: 1.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/p-locate@4.1.0:
|
|
||||||
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
p-limit: 2.3.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/p-locate@5.0.0:
|
/p-locate@5.0.0:
|
||||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -5207,11 +4950,6 @@ packages:
|
||||||
aggregate-error: 3.1.0
|
aggregate-error: 3.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/p-try@2.2.0:
|
|
||||||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/package-json@8.1.1:
|
/package-json@8.1.1:
|
||||||
resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==}
|
resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
@ -5401,14 +5139,6 @@ packages:
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/prop-types@15.8.1:
|
|
||||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
|
||||||
dependencies:
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
object-assign: 4.1.1
|
|
||||||
react-is: 16.13.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/proto-list@1.2.4:
|
/proto-list@1.2.4:
|
||||||
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
|
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
|
||||||
|
|
||||||
|
@ -5512,10 +5242,6 @@ packages:
|
||||||
strip-json-comments: 2.0.1
|
strip-json-comments: 2.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-is@16.13.1:
|
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/react-is@17.0.2:
|
/react-is@17.0.2:
|
||||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -5602,10 +5328,6 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/require-main-filename@2.0.0:
|
|
||||||
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/requires-port@1.0.0:
|
/requires-port@1.0.0:
|
||||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
|
|
||||||
|
@ -5851,10 +5573,6 @@ packages:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
/set-blocking@2.0.0:
|
|
||||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/set-function-length@1.2.1:
|
/set-function-length@1.2.1:
|
||||||
resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
|
resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -6111,16 +5829,6 @@ packages:
|
||||||
readable-stream: 3.6.2
|
readable-stream: 3.6.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tcomb-validation@3.4.1:
|
|
||||||
resolution: {integrity: sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==}
|
|
||||||
dependencies:
|
|
||||||
tcomb: 3.2.29
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tcomb@3.2.29:
|
|
||||||
resolution: {integrity: sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/text-extensions@2.4.0:
|
/text-extensions@2.4.0:
|
||||||
resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
|
resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -6340,11 +6048,6 @@ packages:
|
||||||
crypto-random-string: 4.0.0
|
crypto-random-string: 4.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/universalify@0.1.2:
|
|
||||||
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
|
|
||||||
engines: {node: '>= 4.0.0'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/universalify@0.2.0:
|
/universalify@0.2.0:
|
||||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
@ -6434,6 +6137,7 @@ packages:
|
||||||
/validator@13.11.0:
|
/validator@13.11.0:
|
||||||
resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
|
resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vary@1.1.2:
|
/vary@1.1.2:
|
||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
|
@ -6780,10 +6484,6 @@ packages:
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/which-module@2.0.1:
|
|
||||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/which@2.0.2:
|
/which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -6811,10 +6511,6 @@ packages:
|
||||||
resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
|
resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/workerpool@6.5.1:
|
|
||||||
resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/wrap-ansi@6.2.0:
|
/wrap-ansi@6.2.0:
|
||||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -6863,10 +6559,6 @@ packages:
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/y18n@4.0.3:
|
|
||||||
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/y18n@5.0.8:
|
/y18n@5.0.8:
|
||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -6892,64 +6584,11 @@ packages:
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/yargs-parser@18.1.3:
|
|
||||||
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
dependencies:
|
|
||||||
camelcase: 5.3.1
|
|
||||||
decamelize: 1.2.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yargs-parser@20.2.9:
|
|
||||||
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yargs-parser@21.1.1:
|
/yargs-parser@21.1.1:
|
||||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/yargs-unparser@2.0.0:
|
|
||||||
resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
camelcase: 6.3.0
|
|
||||||
decamelize: 4.0.0
|
|
||||||
flat: 5.0.2
|
|
||||||
is-plain-obj: 2.1.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yargs@15.4.1:
|
|
||||||
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
cliui: 6.0.0
|
|
||||||
decamelize: 1.2.0
|
|
||||||
find-up: 4.1.0
|
|
||||||
get-caller-file: 2.0.5
|
|
||||||
require-directory: 2.1.1
|
|
||||||
require-main-filename: 2.0.0
|
|
||||||
set-blocking: 2.0.0
|
|
||||||
string-width: 4.2.3
|
|
||||||
which-module: 2.0.1
|
|
||||||
y18n: 4.0.3
|
|
||||||
yargs-parser: 18.1.3
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yargs@16.2.0:
|
|
||||||
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
cliui: 7.0.4
|
|
||||||
escalade: 3.1.2
|
|
||||||
get-caller-file: 2.0.5
|
|
||||||
require-directory: 2.1.1
|
|
||||||
string-width: 4.2.3
|
|
||||||
y18n: 5.0.8
|
|
||||||
yargs-parser: 20.2.9
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yargs@17.7.2:
|
/yargs@17.7.2:
|
||||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
|
@ -2,22 +2,18 @@ import axios from 'axios';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { Router } from 'src/router';
|
import { Router } from 'src/router';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
|
|
||||||
|
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
const stateQuery = useStateQueryStore();
|
|
||||||
const baseUrl = '/api/';
|
|
||||||
|
|
||||||
axios.defaults.baseURL = baseUrl;
|
axios.defaults.baseURL = '/api/';
|
||||||
const axiosNoError = axios.create({ baseURL: baseUrl });
|
|
||||||
|
|
||||||
const onRequest = (config) => {
|
const onRequest = (config) => {
|
||||||
const token = session.getToken();
|
const token = session.getToken();
|
||||||
if (token.length && !config.headers.Authorization) {
|
if (token.length && !config.headers.Authorization) {
|
||||||
config.headers.Authorization = token;
|
config.headers.Authorization = token;
|
||||||
}
|
}
|
||||||
stateQuery.add(config);
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,10 +22,10 @@ const onRequestError = (error) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onResponse = (response) => {
|
const onResponse = (response) => {
|
||||||
const config = response.config;
|
const { method } = response.config;
|
||||||
stateQuery.remove(config);
|
|
||||||
|
|
||||||
if (config.method === 'patch') {
|
const isSaveRequest = method === 'patch';
|
||||||
|
if (isSaveRequest) {
|
||||||
notify('globals.dataSaved', 'positive');
|
notify('globals.dataSaved', 'positive');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +33,37 @@ const onResponse = (response) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onResponseError = (error) => {
|
const onResponseError = (error) => {
|
||||||
stateQuery.remove(error.config);
|
let message = '';
|
||||||
|
|
||||||
if (session.isLoggedIn() && error.response?.status === 401) {
|
const response = error.response;
|
||||||
|
const responseData = response && response.data;
|
||||||
|
const responseError = responseData && response.data.error;
|
||||||
|
if (responseError) {
|
||||||
|
message = responseError.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (response?.status) {
|
||||||
|
case 422:
|
||||||
|
if (error.name == 'ValidationError')
|
||||||
|
message +=
|
||||||
|
' "' +
|
||||||
|
responseError.details.context +
|
||||||
|
'.' +
|
||||||
|
Object.keys(responseError.details.codes).join(',') +
|
||||||
|
'"';
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
message = 'errors.statusInternalServerError';
|
||||||
|
break;
|
||||||
|
case 502:
|
||||||
|
message = 'errors.statusBadGateway';
|
||||||
|
break;
|
||||||
|
case 504:
|
||||||
|
message = 'errors.statusGatewayTimeout';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.isLoggedIn() && response?.status === 401) {
|
||||||
session.destroy(false);
|
session.destroy(false);
|
||||||
const hash = window.location.hash;
|
const hash = window.location.hash;
|
||||||
const url = hash.slice(1);
|
const url = hash.slice(1);
|
||||||
|
@ -48,12 +72,12 @@ const onResponseError = (error) => {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify(message, 'negative');
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
axios.interceptors.request.use(onRequest, onRequestError);
|
axios.interceptors.request.use(onRequest, onRequestError);
|
||||||
axios.interceptors.response.use(onResponse, onResponseError);
|
axios.interceptors.response.use(onResponse, onResponseError);
|
||||||
axiosNoError.interceptors.request.use(onRequest);
|
|
||||||
axiosNoError.interceptors.response.use(onResponse);
|
|
||||||
|
|
||||||
export { onRequest, onResponseError, axiosNoError };
|
export { onRequest, onResponseError };
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import { QInput } from 'quasar';
|
|
||||||
import setDefault from './setDefault';
|
|
||||||
|
|
||||||
setDefault(QInput, 'dense', true);
|
|
|
@ -1,4 +0,0 @@
|
||||||
import { QSelect } from 'quasar';
|
|
||||||
import setDefault from './setDefault';
|
|
||||||
|
|
||||||
setDefault(QSelect, 'dense', true);
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import routes from 'src/router/modules';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
let isNotified = false;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
created: function () {
|
||||||
|
const router = useRouter();
|
||||||
|
const keyBindingMap = routes
|
||||||
|
.filter((route) => route.meta.keyBinding)
|
||||||
|
.reduce((map, route) => {
|
||||||
|
map['Key' + route.meta.keyBinding.toUpperCase()] = route.path;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const handleKeyDown = (event) => {
|
||||||
|
const { ctrlKey, altKey, code } = event;
|
||||||
|
|
||||||
|
if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) {
|
||||||
|
event.preventDefault();
|
||||||
|
router.push(keyBindingMap[code]);
|
||||||
|
isNotified = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyUp = (event) => {
|
||||||
|
const { ctrlKey, altKey } = event;
|
||||||
|
|
||||||
|
// Resetea la bandera cuando se sueltan las teclas ctrl o alt
|
||||||
|
if (!ctrlKey || !altKey) {
|
||||||
|
isNotified = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
|
window.addEventListener('keyup', handleKeyUp);
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,3 +1 @@
|
||||||
export * from './defaults/qTable';
|
export * from './defaults/qTable';
|
||||||
export * from './defaults/qInput';
|
|
||||||
export * from './defaults/qSelect';
|
|
||||||
|
|
|
@ -1,51 +1,10 @@
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
import qFormMixin from './qformMixin';
|
import qFormMixin from './qformMixin';
|
||||||
|
import mainShortcutMixin from './mainShortcutMixin';
|
||||||
import keyShortcut from './keyShortcut';
|
import keyShortcut from './keyShortcut';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
|
||||||
import { CanceledError } from 'axios';
|
|
||||||
|
|
||||||
const { notify } = useNotify();
|
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
app.mixin(qFormMixin);
|
app.mixin(qFormMixin);
|
||||||
|
app.mixin(mainShortcutMixin);
|
||||||
app.directive('shortcut', keyShortcut);
|
app.directive('shortcut', keyShortcut);
|
||||||
app.config.errorHandler = (error) => {
|
|
||||||
let message;
|
|
||||||
const response = error.response;
|
|
||||||
const responseData = response?.data;
|
|
||||||
const responseError = responseData && response.data.error;
|
|
||||||
if (responseError) {
|
|
||||||
message = responseError.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (response?.status) {
|
|
||||||
case 422:
|
|
||||||
if (error.name == 'ValidationError')
|
|
||||||
message +=
|
|
||||||
' "' +
|
|
||||||
responseError.details.context +
|
|
||||||
'.' +
|
|
||||||
Object.keys(responseError.details.codes).join(',') +
|
|
||||||
'"';
|
|
||||||
break;
|
|
||||||
case 500:
|
|
||||||
message = 'errors.statusInternalServerError';
|
|
||||||
break;
|
|
||||||
case 502:
|
|
||||||
message = 'errors.statusBadGateway';
|
|
||||||
break;
|
|
||||||
case 504:
|
|
||||||
message = 'errors.statusGatewayTimeout';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(error);
|
|
||||||
if (error instanceof CanceledError) {
|
|
||||||
const env = process.env.NODE_ENV;
|
|
||||||
if (env && env !== 'development') return;
|
|
||||||
message = 'Duplicate request';
|
|
||||||
}
|
|
||||||
|
|
||||||
notify(message ?? 'globals.error', 'negative', 'error');
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref, onMounted, nextTick, computed } from 'vue';
|
import { reactive, ref, onMounted, nextTick } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
@ -7,29 +7,27 @@ import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
import { useState } from 'src/composables/useState';
|
|
||||||
|
defineProps({ showEntityField: { type: Boolean, default: true } });
|
||||||
|
|
||||||
const emit = defineEmits(['onDataSaved']);
|
const emit = defineEmits(['onDataSaved']);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const bicInputRef = ref(null);
|
const bicInputRef = ref(null);
|
||||||
const state = useState();
|
const bankEntityFormData = reactive({
|
||||||
|
name: null,
|
||||||
const customer = computed(() => state.get('customer'));
|
bic: null,
|
||||||
|
countryFk: null,
|
||||||
|
id: null,
|
||||||
|
});
|
||||||
|
|
||||||
const countriesFilter = {
|
const countriesFilter = {
|
||||||
fields: ['id', 'name', 'code'],
|
fields: ['id', 'name', 'code'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const bankEntityFormData = reactive({
|
|
||||||
name: null,
|
|
||||||
bic: null,
|
|
||||||
countryFk: customer.value?.countryFk,
|
|
||||||
});
|
|
||||||
|
|
||||||
const countriesOptions = ref([]);
|
const countriesOptions = ref([]);
|
||||||
|
|
||||||
const onDataSaved = (...args) => {
|
const onDataSaved = (formData, requestResponse) => {
|
||||||
emit('onDataSaved', ...args);
|
emit('onDataSaved', formData, requestResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -41,6 +39,7 @@ onMounted(async () => {
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
url="Countries"
|
url="Countries"
|
||||||
|
:filter="countriesFilter"
|
||||||
auto-load
|
auto-load
|
||||||
@on-fetch="(data) => (countriesOptions = data)"
|
@on-fetch="(data) => (countriesOptions = data)"
|
||||||
/>
|
/>
|
||||||
|
@ -50,7 +49,6 @@ onMounted(async () => {
|
||||||
:title="t('title')"
|
:title="t('title')"
|
||||||
:subtitle="t('subtitle')"
|
:subtitle="t('subtitle')"
|
||||||
:form-initial-data="bankEntityFormData"
|
:form-initial-data="bankEntityFormData"
|
||||||
:filter="countriesFilter"
|
|
||||||
@on-data-saved="onDataSaved"
|
@on-data-saved="onDataSaved"
|
||||||
>
|
>
|
||||||
<template #form-inputs="{ data, validate }">
|
<template #form-inputs="{ data, validate }">
|
||||||
|
@ -82,13 +80,7 @@ onMounted(async () => {
|
||||||
:rules="validate('bankEntity.countryFk')"
|
:rules="validate('bankEntity.countryFk')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-if="showEntityField" class="col">
|
||||||
v-if="
|
|
||||||
countriesOptions.find((c) => c.id === data.countryFk)?.code ==
|
|
||||||
'ES'
|
|
||||||
"
|
|
||||||
class="col"
|
|
||||||
>
|
|
||||||
<VnInput
|
<VnInput
|
||||||
:label="t('id')"
|
:label="t('id')"
|
||||||
v-model="data.id"
|
v-model="data.id"
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
import VnInputDate from './common/VnInputDate.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const manualInvoiceFormData = reactive({
|
||||||
|
maxShipped: Date.vnNew(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const formModelPopupRef = ref();
|
||||||
|
const invoiceOutSerialsOptions = ref([]);
|
||||||
|
const taxAreasOptions = ref([]);
|
||||||
|
const ticketsOptions = ref([]);
|
||||||
|
const clientsOptions = ref([]);
|
||||||
|
const isLoading = computed(() => formModelPopupRef.value?.isLoading);
|
||||||
|
|
||||||
|
const onDataSaved = async (formData, requestResponse) => {
|
||||||
|
emit('onDataSaved', formData, requestResponse);
|
||||||
|
if (requestResponse && requestResponse.id)
|
||||||
|
router.push({ name: 'InvoiceOutSummary', params: { id: requestResponse.id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="InvoiceOutSerials"
|
||||||
|
:filter="{ where: { code: { neq: 'R' } }, order: ['code'] }"
|
||||||
|
@on-fetch="(data) => (invoiceOutSerialsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="TaxAreas"
|
||||||
|
:filter="{ order: ['code'] }"
|
||||||
|
@on-fetch="(data) => (taxAreasOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModelPopup
|
||||||
|
ref="formModelPopupRef"
|
||||||
|
:title="t('Create manual invoice')"
|
||||||
|
url-create="InvoiceOuts/createManualInvoice"
|
||||||
|
model="invoiceOut"
|
||||||
|
:form-initial-data="manualInvoiceFormData"
|
||||||
|
@on-data-saved="onDataSaved"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data }">
|
||||||
|
<span v-if="isLoading" class="text-primary invoicing-text">
|
||||||
|
<QIcon name="warning" class="fill-icon q-mr-sm" size="md" />
|
||||||
|
{{ t('Invoicing in progress...') }}
|
||||||
|
</span>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('Ticket')"
|
||||||
|
:options="ticketsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="id"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.ticketFk"
|
||||||
|
@update:model-value="data.clientFk = null"
|
||||||
|
url="Tickets"
|
||||||
|
:where="{ refFk: null }"
|
||||||
|
:fields="['id', 'nickname']"
|
||||||
|
:filter-options="{ order: 'shipped DESC' }"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
|
||||||
|
<QItemLabel caption>{{ scope.opt?.nickname }}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelect>
|
||||||
|
<span class="row items-center" style="max-width: max-content">{{
|
||||||
|
t('Or')
|
||||||
|
}}</span>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('Client')"
|
||||||
|
:options="clientsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.clientFk"
|
||||||
|
@update:model-value="data.ticketFk = null"
|
||||||
|
url="Clients"
|
||||||
|
:fields="['id', 'name']"
|
||||||
|
:filter-options="{ order: 'name ASC' }"
|
||||||
|
/>
|
||||||
|
<VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('Serial')"
|
||||||
|
:options="invoiceOutSerialsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.serial"
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('Area')"
|
||||||
|
:options="taxAreasOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.taxArea"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnInput
|
||||||
|
:label="t('Reference')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="data.reference"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.invoicing-text {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: $primary;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Create manual invoice: Crear factura manual
|
||||||
|
Ticket: Ticket
|
||||||
|
Client: Cliente
|
||||||
|
Max date: Fecha límite
|
||||||
|
Serial: Serie
|
||||||
|
Area: Area
|
||||||
|
Reference: Referencia
|
||||||
|
Or: O
|
||||||
|
Invoicing in progress...: Facturación en progreso...
|
||||||
|
</i18n>
|
|
@ -1,38 +1,35 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectProvince from 'components/VnSelectProvince.vue';
|
import VnSelectProvince from 'components/VnSelectProvince.vue';
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['onDataSaved']);
|
const emit = defineEmits(['onDataSaved']);
|
||||||
const $props = defineProps({
|
|
||||||
countryFk: {
|
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
provinceSelected: {
|
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const cityFormData = ref({
|
const cityFormData = reactive({
|
||||||
name: null,
|
name: null,
|
||||||
provinceFk: null,
|
provinceFk: null,
|
||||||
});
|
});
|
||||||
onMounted(() => {
|
|
||||||
cityFormData.value.provinceFk = $props.provinceSelected;
|
const provincesOptions = ref([]);
|
||||||
});
|
|
||||||
const onDataSaved = (...args) => {
|
const onDataSaved = (...args) => {
|
||||||
emit('onDataSaved', ...args);
|
emit('onDataSaved', ...args);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (provincesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces"
|
||||||
|
/>
|
||||||
<FormModelPopup
|
<FormModelPopup
|
||||||
:title="t('New city')"
|
:title="t('New city')"
|
||||||
:subtitle="t('Please, ensure you put the correct data!')"
|
:subtitle="t('Please, ensure you put the correct data!')"
|
||||||
|
@ -47,14 +44,8 @@ const onDataSaved = (...args) => {
|
||||||
:label="t('Name')"
|
:label="t('Name')"
|
||||||
v-model="data.name"
|
v-model="data.name"
|
||||||
:rules="validate('city.name')"
|
:rules="validate('city.name')"
|
||||||
required
|
|
||||||
/>
|
|
||||||
<VnSelectProvince
|
|
||||||
:province-selected="$props.provinceSelected"
|
|
||||||
:country-fk="$props.countryFk"
|
|
||||||
v-model="data.provinceFk"
|
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
|
<VnSelectProvince v-model="data.provinceFk" />
|
||||||
</VnRow>
|
</VnRow>
|
||||||
</template>
|
</template>
|
||||||
</FormModelPopup>
|
</FormModelPopup>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
@ -22,14 +22,10 @@ const postcodeFormData = reactive({
|
||||||
townFk: null,
|
townFk: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const townsFetchDataRef = ref(false);
|
const provincesFetchDataRef = ref(null);
|
||||||
const countriesRef = ref(false);
|
const countriesOptions = ref([]);
|
||||||
const townsRef = ref(false);
|
|
||||||
const provincesFetchDataRef = ref(false);
|
|
||||||
const provincesOptions = ref([]);
|
const provincesOptions = ref([]);
|
||||||
const town = ref({});
|
const town = ref({});
|
||||||
const townFilter = ref({});
|
|
||||||
const countryFilter = ref({});
|
|
||||||
|
|
||||||
function onDataSaved(formData) {
|
function onDataSaved(formData) {
|
||||||
const newPostcode = {
|
const newPostcode = {
|
||||||
|
@ -41,7 +37,7 @@ function onDataSaved(formData) {
|
||||||
({ id }) => id === formData.provinceFk
|
({ id }) => id === formData.provinceFk
|
||||||
);
|
);
|
||||||
newPostcode.province = provinceObject?.name;
|
newPostcode.province = provinceObject?.name;
|
||||||
const countryObject = countriesRef.value.opts.find(
|
const countryObject = countriesOptions.value.find(
|
||||||
({ id }) => id === formData.countryFk
|
({ id }) => id === formData.countryFk
|
||||||
);
|
);
|
||||||
newPostcode.country = countryObject?.name;
|
newPostcode.country = countryObject?.name;
|
||||||
|
@ -58,54 +54,33 @@ async function onCityCreated(newTown, formData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTown(newTown, data) {
|
function setTown(newTown, data) {
|
||||||
|
if (!newTown) return;
|
||||||
town.value = newTown;
|
town.value = newTown;
|
||||||
data.provinceFk = newTown?.provinceFk ?? newTown;
|
data.provinceFk = newTown.provinceFk;
|
||||||
data.countryFk = newTown?.province?.countryFk ?? newTown;
|
data.countryFk = newTown.province.countryFk;
|
||||||
}
|
|
||||||
|
|
||||||
async function setCountry(countryFk, data) {
|
|
||||||
data.townFk = null;
|
|
||||||
data.provinceFk = null;
|
|
||||||
data.countryFk = countryFk;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleProvinces(data) {
|
|
||||||
provincesOptions.value = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setProvince(id, data) {
|
async function setProvince(id, data) {
|
||||||
|
await provincesFetchDataRef.value.fetch();
|
||||||
const newProvince = provincesOptions.value.find((province) => province.id == id);
|
const newProvince = provincesOptions.value.find((province) => province.id == id);
|
||||||
if (!newProvince) return;
|
if (!newProvince) return;
|
||||||
|
|
||||||
data.countryFk = newProvince.countryFk;
|
data.countryFk = newProvince.countryFk;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onProvinceCreated(data) {
|
|
||||||
await provincesFetchDataRef.value.fetch({
|
|
||||||
where: { countryFk: postcodeFormData.countryFk },
|
|
||||||
});
|
|
||||||
postcodeFormData.provinceFk = data.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const whereTowns = computed(() => {
|
|
||||||
return {
|
|
||||||
provinceFk: {
|
|
||||||
inq: provincesOptions.value.map(({ id }) => id),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
ref="provincesFetchDataRef"
|
ref="provincesFetchDataRef"
|
||||||
@on-fetch="handleProvinces"
|
@on-fetch="(data) => (provincesOptions = data)"
|
||||||
:sort-by="['name ASC']"
|
|
||||||
:limit="30"
|
|
||||||
auto-load
|
auto-load
|
||||||
url="Provinces/location"
|
url="Provinces/location"
|
||||||
/>
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (countriesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Countries"
|
||||||
|
/>
|
||||||
<FormModelPopup
|
<FormModelPopup
|
||||||
url-create="postcodes"
|
url-create="postcodes"
|
||||||
model="postcode"
|
model="postcode"
|
||||||
|
@ -121,27 +96,18 @@ const whereTowns = computed(() => {
|
||||||
:label="t('Postcode')"
|
:label="t('Postcode')"
|
||||||
v-model="data.code"
|
v-model="data.code"
|
||||||
:rules="validate('postcode.code')"
|
:rules="validate('postcode.code')"
|
||||||
clearable
|
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<VnSelectDialog
|
<VnSelectDialog
|
||||||
ref="townsRef"
|
|
||||||
:sort-by="['name ASC']"
|
|
||||||
:limit="30"
|
|
||||||
auto-load
|
|
||||||
url="Towns/location"
|
|
||||||
:where="whereTowns"
|
|
||||||
:label="t('City')"
|
:label="t('City')"
|
||||||
|
url="Towns/location"
|
||||||
@update:model-value="(value) => setTown(value, data)"
|
@update:model-value="(value) => setTown(value, data)"
|
||||||
:tooltip="t('Create city')"
|
|
||||||
v-model="data.townFk"
|
v-model="data.townFk"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
:rules="validate('postcode.city')"
|
:rules="validate('postcode.city')"
|
||||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
:roles-allowed-to-create="['deliveryAssistant', 'administrative']"
|
||||||
:emit-value="false"
|
:emit-value="false"
|
||||||
:clearable="true"
|
clearable
|
||||||
required
|
|
||||||
>
|
>
|
||||||
<template #option="{ itemProps, opt }">
|
<template #option="{ itemProps, opt }">
|
||||||
<QItem v-bind="itemProps">
|
<QItem v-bind="itemProps">
|
||||||
|
@ -156,8 +122,6 @@ const whereTowns = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewCityForm
|
<CreateNewCityForm
|
||||||
:country-fk="data.countryFk"
|
|
||||||
:province-selected="data.provinceFk"
|
|
||||||
@on-data-saved="
|
@on-data-saved="
|
||||||
(_, requestResponse) =>
|
(_, requestResponse) =>
|
||||||
onCityCreated(requestResponse, data)
|
onCityCreated(requestResponse, data)
|
||||||
|
@ -168,29 +132,17 @@ const whereTowns = computed(() => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnSelectProvince
|
<VnSelectProvince
|
||||||
:country-fk="data.countryFk"
|
|
||||||
:province-selected="data.provinceFk"
|
|
||||||
@update:model-value="(value) => setProvince(value, data)"
|
@update:model-value="(value) => setProvince(value, data)"
|
||||||
v-model="data.provinceFk"
|
v-model="data.provinceFk"
|
||||||
@on-province-fetched="handleProvinces"
|
|
||||||
@on-province-created="onProvinceCreated"
|
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
ref="countriesRef"
|
|
||||||
:limit="30"
|
|
||||||
:filter="countryFilter"
|
|
||||||
:sort-by="['name ASC']"
|
|
||||||
auto-load
|
|
||||||
url="Countries"
|
|
||||||
required
|
|
||||||
:label="t('Country')"
|
:label="t('Country')"
|
||||||
|
:options="countriesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
v-model="data.countryFk"
|
v-model="data.countryFk"
|
||||||
:rules="validate('postcode.countryFk')"
|
:rules="validate('postcode.countryFk')"
|
||||||
@update:model-value="(value) => setCountry(value, data)"
|
|
||||||
/>
|
/>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
</template>
|
</template>
|
||||||
|
@ -200,7 +152,6 @@ const whereTowns = computed(() => {
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
New postcode: Nuevo código postal
|
New postcode: Nuevo código postal
|
||||||
Create city: Crear población
|
|
||||||
Please, ensure you put the correct data!: ¡Por favor, asegúrese de poner los datos correctos!
|
Please, ensure you put the correct data!: ¡Por favor, asegúrese de poner los datos correctos!
|
||||||
City: Población
|
City: Población
|
||||||
Province: Provincia
|
Province: Provincia
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
@ -15,29 +16,23 @@ const provinceFormData = reactive({
|
||||||
name: null,
|
name: null,
|
||||||
autonomyFk: null,
|
autonomyFk: null,
|
||||||
});
|
});
|
||||||
const $props = defineProps({
|
|
||||||
countryFk: {
|
const autonomiesOptions = ref([]);
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const autonomiesRef = ref([]);
|
|
||||||
|
|
||||||
const onDataSaved = (dataSaved, requestResponse) => {
|
const onDataSaved = (dataSaved, requestResponse) => {
|
||||||
requestResponse.autonomy = autonomiesRef.value.opts.find(
|
requestResponse.autonomy = autonomiesOptions.value.find(
|
||||||
(autonomy) => autonomy.id == requestResponse.autonomyFk
|
(autonomy) => autonomy.id == requestResponse.autonomyFk
|
||||||
);
|
);
|
||||||
emit('onDataSaved', dataSaved, requestResponse);
|
emit('onDataSaved', dataSaved, requestResponse);
|
||||||
};
|
};
|
||||||
const where = computed(() => {
|
|
||||||
if (!$props.countryFk) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return { countryFk: $props.countryFk };
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (autonomiesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Autonomies/location"
|
||||||
|
/>
|
||||||
<FormModelPopup
|
<FormModelPopup
|
||||||
:title="t('New province')"
|
:title="t('New province')"
|
||||||
:subtitle="t('Please, ensure you put the correct data!')"
|
:subtitle="t('Please, ensure you put the correct data!')"
|
||||||
|
@ -52,17 +47,10 @@ const where = computed(() => {
|
||||||
:label="t('Name')"
|
:label="t('Name')"
|
||||||
v-model="data.name"
|
v-model="data.name"
|
||||||
:rules="validate('province.name')"
|
:rules="validate('province.name')"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
required
|
|
||||||
ref="autonomiesRef"
|
|
||||||
auto-load
|
|
||||||
:where="where"
|
|
||||||
url="Autonomies/location"
|
|
||||||
:sort-by="['name ASC']"
|
|
||||||
:limit="30"
|
|
||||||
:label="t('Autonomy')"
|
:label="t('Autonomy')"
|
||||||
|
:options="autonomiesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
|
|
|
@ -38,7 +38,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
@on-fetch="(data) => (warehousesOptions = data)"
|
@on-fetch="(data) => (warehousesOptions = data)"
|
||||||
auto-load
|
auto-load
|
||||||
url="Warehouses"
|
url="Warehouses"
|
||||||
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
|
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
|
||||||
/>
|
/>
|
||||||
<FetchData
|
<FetchData
|
||||||
@on-fetch="(data) => (temperaturesOptions = data)"
|
@on-fetch="(data) => (temperaturesOptions = data)"
|
||||||
|
@ -50,7 +50,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
model="thermograph"
|
model="thermograph"
|
||||||
:title="t('New thermograph')"
|
:title="t('New thermograph')"
|
||||||
:form-initial-data="thermographFormData"
|
:form-initial-data="thermographFormData"
|
||||||
@on-data-saved="(_, response) => onDataSaved(response)"
|
@on-data-saved="onDataSaved($event)"
|
||||||
>
|
>
|
||||||
<template #form-inputs="{ data, validate }">
|
<template #form-inputs="{ data, validate }">
|
||||||
<VnRow>
|
<VnRow>
|
||||||
|
|
|
@ -10,7 +10,6 @@ import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
import SkeletonTable from 'components/ui/SkeletonTable.vue';
|
import SkeletonTable from 'components/ui/SkeletonTable.vue';
|
||||||
import { tMobile } from 'src/composables/tMobile';
|
import { tMobile } from 'src/composables/tMobile';
|
||||||
import getDifferences from 'src/filters/getDifferences';
|
|
||||||
|
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
@ -78,7 +77,7 @@ const isLoading = ref(false);
|
||||||
const hasChanges = ref(false);
|
const hasChanges = ref(false);
|
||||||
const originalData = ref();
|
const originalData = ref();
|
||||||
const vnPaginateRef = ref();
|
const vnPaginateRef = ref();
|
||||||
const formData = ref([]);
|
const formData = ref();
|
||||||
const saveButtonRef = ref(null);
|
const saveButtonRef = ref(null);
|
||||||
const watchChanges = ref();
|
const watchChanges = ref();
|
||||||
const formUrl = computed(() => $props.url);
|
const formUrl = computed(() => $props.url);
|
||||||
|
@ -95,7 +94,6 @@ defineExpose({
|
||||||
saveChanges,
|
saveChanges,
|
||||||
getChanges,
|
getChanges,
|
||||||
formData,
|
formData,
|
||||||
originalData,
|
|
||||||
vnPaginateRef,
|
vnPaginateRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -176,13 +174,14 @@ async function saveChanges(data) {
|
||||||
const changes = data || getChanges();
|
const changes = data || getChanges();
|
||||||
try {
|
try {
|
||||||
await axios.post($props.saveUrl || $props.url + '/crud', changes);
|
await axios.post($props.saveUrl || $props.url + '/crud', changes);
|
||||||
} finally {
|
} catch (e) {
|
||||||
isLoading.value = false;
|
return (isLoading.value = false);
|
||||||
}
|
}
|
||||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||||
if (changes.creates?.length) await vnPaginateRef.value.fetch();
|
if (changes.creates?.length) await vnPaginateRef.value.fetch();
|
||||||
|
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
|
isLoading.value = false;
|
||||||
emit('saveChanges', data);
|
emit('saveChanges', data);
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
|
@ -235,8 +234,6 @@ async function remove(data) {
|
||||||
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
||||||
fetch(newData);
|
fetch(newData);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
reset();
|
|
||||||
}
|
}
|
||||||
emit('update:selected', []);
|
emit('update:selected', []);
|
||||||
}
|
}
|
||||||
|
@ -268,6 +265,28 @@ function getChanges() {
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDifferences(obj1, obj2) {
|
||||||
|
let diff = {};
|
||||||
|
delete obj1.$index;
|
||||||
|
delete obj2.$index;
|
||||||
|
|
||||||
|
for (let key in obj1) {
|
||||||
|
if (obj2[key] && JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
|
||||||
|
diff[key] = obj2[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let key in obj2) {
|
||||||
|
if (
|
||||||
|
obj1[key] === undefined ||
|
||||||
|
JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])
|
||||||
|
) {
|
||||||
|
diff[key] = obj2[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
function isEmpty(obj) {
|
function isEmpty(obj) {
|
||||||
if (obj == null) return true;
|
if (obj == null) return true;
|
||||||
if (obj === undefined) return true;
|
if (obj === undefined) return true;
|
||||||
|
@ -373,7 +392,6 @@ watch(formUrl, async () => {
|
||||||
@click="onSubmit"
|
@click="onSubmit"
|
||||||
:disable="!hasChanges"
|
:disable="!hasChanges"
|
||||||
:title="t('globals.save')"
|
:title="t('globals.save')"
|
||||||
data-cy="crudModelDefaultSaveBtn"
|
|
||||||
/>
|
/>
|
||||||
<slot name="moreAfterActions" />
|
<slot name="moreAfterActions" />
|
||||||
</QBtnGroup>
|
</QBtnGroup>
|
||||||
|
|
|
@ -156,22 +156,26 @@ const rotateRight = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
if (!newPhoto.files && !newPhoto.url) {
|
try {
|
||||||
notify(t('Select an image'), 'negative');
|
if (!newPhoto.files && !newPhoto.url) {
|
||||||
return;
|
notify(t('Select an image'), 'negative');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
type: 'blob',
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.value
|
||||||
|
.result(options)
|
||||||
|
.then((result) => {
|
||||||
|
const file = new File([result], newPhoto.files?.name || '');
|
||||||
|
newPhoto.blob = file;
|
||||||
|
})
|
||||||
|
.then(() => makeRequest());
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error uploading image');
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
|
||||||
type: 'blob',
|
|
||||||
};
|
|
||||||
|
|
||||||
editor.value
|
|
||||||
.result(options)
|
|
||||||
.then((result) => {
|
|
||||||
const file = new File([result], newPhoto.files?.name || '');
|
|
||||||
newPhoto.blob = file;
|
|
||||||
})
|
|
||||||
.then(() => makeRequest());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeRequest = async () => {
|
const makeRequest = async () => {
|
||||||
|
|
|
@ -51,17 +51,21 @@ const onDataSaved = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
isLoading.value = true;
|
try {
|
||||||
const rowsToEdit = $props.rows.map((row) => ({ id: row.id, itemFk: row.itemFk }));
|
isLoading.value = true;
|
||||||
const payload = {
|
const rowsToEdit = $props.rows.map((row) => ({ id: row.id, itemFk: row.itemFk }));
|
||||||
field: selectedField.value.field,
|
const payload = {
|
||||||
newValue: newValue.value,
|
field: selectedField.value.field,
|
||||||
lines: rowsToEdit,
|
newValue: newValue.value,
|
||||||
};
|
lines: rowsToEdit,
|
||||||
|
};
|
||||||
|
|
||||||
await axios.post($props.editUrl, payload);
|
await axios.post($props.editUrl, payload);
|
||||||
onDataSaved();
|
onDataSaved();
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error submitting table cell edit');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeForm = () => {
|
const closeForm = () => {
|
||||||
|
|
|
@ -44,7 +44,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
async function fetch(fetchFilter = {}) {
|
async function fetch(fetchFilter = {}) {
|
||||||
try {
|
try {
|
||||||
const filter = { ...fetchFilter, ...$props.filter }; // eslint-disable-line vue/no-dupe-keys
|
const filter = Object.assign(fetchFilter, $props.filter); // eslint-disable-line vue/no-dupe-keys
|
||||||
if ($props.where && !fetchFilter.where) filter.where = $props.where;
|
if ($props.where && !fetchFilter.where) filter.where = $props.where;
|
||||||
if ($props.sortBy) filter.order = $props.sortBy;
|
if ($props.sortBy) filter.order = $props.sortBy;
|
||||||
if ($props.limit) filter.limit = $props.limit;
|
if ($props.limit) filter.limit = $props.limit;
|
||||||
|
|
|
@ -50,25 +50,25 @@ const loading = ref(false);
|
||||||
|
|
||||||
const tableColumns = computed(() => [
|
const tableColumns = computed(() => [
|
||||||
{
|
{
|
||||||
label: t('globals.id'),
|
label: t('entry.buys.id'),
|
||||||
name: 'id',
|
name: 'id',
|
||||||
field: 'id',
|
field: 'id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.name'),
|
label: t('entry.buys.name'),
|
||||||
name: 'name',
|
name: 'name',
|
||||||
field: 'name',
|
field: 'name',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.size'),
|
label: t('entry.buys.size'),
|
||||||
name: 'size',
|
name: 'size',
|
||||||
field: 'size',
|
field: 'size',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.producer'),
|
label: t('entry.buys.producer'),
|
||||||
name: 'producerName',
|
name: 'producerName',
|
||||||
field: 'producer',
|
field: 'producer',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -84,30 +84,34 @@ const tableColumns = computed(() => [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
let filter = itemFilter;
|
try {
|
||||||
const params = itemFilterParams;
|
let filter = itemFilter;
|
||||||
const where = {};
|
const params = itemFilterParams;
|
||||||
for (let key in params) {
|
const where = {};
|
||||||
const value = params[key];
|
for (let key in params) {
|
||||||
if (!value) continue;
|
const value = params[key];
|
||||||
|
if (!value) continue;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'name':
|
case 'name':
|
||||||
where[key] = { like: `%${value}%` };
|
where[key] = { like: `%${value}%` };
|
||||||
break;
|
break;
|
||||||
case 'producerFk':
|
case 'producerFk':
|
||||||
case 'typeFk':
|
case 'typeFk':
|
||||||
case 'size':
|
case 'size':
|
||||||
case 'inkFk':
|
case 'inkFk':
|
||||||
where[key] = value;
|
where[key] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
filter.where = where;
|
||||||
filter.where = where;
|
|
||||||
|
|
||||||
const { data } = await axios.get(props.url, {
|
const { data } = await axios.get(props.url, {
|
||||||
params: { filter: JSON.stringify(filter) },
|
params: { filter: JSON.stringify(filter) },
|
||||||
});
|
});
|
||||||
tableRows.value = data;
|
tableRows.value = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching entries items');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeForm = () => {
|
const closeForm = () => {
|
||||||
|
@ -148,10 +152,10 @@ const selectItem = ({ id }) => {
|
||||||
</span>
|
</span>
|
||||||
<h1 class="title">{{ t('Filter item') }}</h1>
|
<h1 class="title">{{ t('Filter item') }}</h1>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnInput :label="t('globals.name')" v-model="itemFilterParams.name" />
|
<VnInput :label="t('entry.buys.name')" v-model="itemFilterParams.name" />
|
||||||
<VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" />
|
<VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" />
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('globals.producer')"
|
:label="t('entry.buys.producer')"
|
||||||
:options="producersOptions"
|
:options="producersOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -159,7 +163,7 @@ const selectItem = ({ id }) => {
|
||||||
v-model="itemFilterParams.producerFk"
|
v-model="itemFilterParams.producerFk"
|
||||||
/>
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('globals.type')"
|
:label="t('entry.buys.type')"
|
||||||
:options="ItemTypesOptions"
|
:options="ItemTypesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
|
|
@ -48,13 +48,13 @@ const loading = ref(false);
|
||||||
|
|
||||||
const tableColumns = computed(() => [
|
const tableColumns = computed(() => [
|
||||||
{
|
{
|
||||||
label: t('globals.id'),
|
label: t('entry.basicData.id'),
|
||||||
name: 'id',
|
name: 'id',
|
||||||
field: 'id',
|
field: 'id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.warehouseOut'),
|
label: t('entry.basicData.warehouseOut'),
|
||||||
name: 'warehouseOutFk',
|
name: 'warehouseOutFk',
|
||||||
field: 'warehouseOutFk',
|
field: 'warehouseOutFk',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -62,7 +62,7 @@ const tableColumns = computed(() => [
|
||||||
warehousesOptions.value.find((warehouse) => warehouse.id === val).name,
|
warehousesOptions.value.find((warehouse) => warehouse.id === val).name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.warehouseIn'),
|
label: t('entry.basicData.warehouseIn'),
|
||||||
name: 'warehouseInFk',
|
name: 'warehouseInFk',
|
||||||
field: 'warehouseInFk',
|
field: 'warehouseInFk',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -70,14 +70,14 @@ const tableColumns = computed(() => [
|
||||||
warehousesOptions.value.find((warehouse) => warehouse.id === val).name,
|
warehousesOptions.value.find((warehouse) => warehouse.id === val).name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.shipped'),
|
label: t('entry.basicData.shipped'),
|
||||||
name: 'shipped',
|
name: 'shipped',
|
||||||
field: 'shipped',
|
field: 'shipped',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
format: (val) => toDate(val),
|
format: (val) => toDate(val),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.landed'),
|
label: t('entry.basicData.landed'),
|
||||||
name: 'landed',
|
name: 'landed',
|
||||||
field: 'landed',
|
field: 'landed',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -86,28 +86,32 @@ const tableColumns = computed(() => [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
let filter = travelFilter;
|
try {
|
||||||
const params = travelFilterParams;
|
let filter = travelFilter;
|
||||||
const where = {};
|
const params = travelFilterParams;
|
||||||
for (let key in params) {
|
const where = {};
|
||||||
const value = params[key];
|
for (let key in params) {
|
||||||
if (!value) continue;
|
const value = params[key];
|
||||||
|
if (!value) continue;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'agencyModeFk':
|
case 'agencyModeFk':
|
||||||
case 'warehouseInFk':
|
case 'warehouseInFk':
|
||||||
case 'warehouseOutFk':
|
case 'warehouseOutFk':
|
||||||
case 'shipped':
|
case 'shipped':
|
||||||
case 'landed':
|
case 'landed':
|
||||||
where[key] = value;
|
where[key] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
filter.where = where;
|
filter.where = where;
|
||||||
const { data } = await axios.get('Travels', {
|
const { data } = await axios.get('Travels', {
|
||||||
params: { filter: JSON.stringify(filter) },
|
params: { filter: JSON.stringify(filter) },
|
||||||
});
|
});
|
||||||
tableRows.value = data;
|
tableRows.value = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching travels');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeForm = () => {
|
const closeForm = () => {
|
||||||
|
@ -142,7 +146,7 @@ const selectTravel = ({ id }) => {
|
||||||
<h1 class="title">{{ t('Filter travels') }}</h1>
|
<h1 class="title">{{ t('Filter travels') }}</h1>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('globals.agency')"
|
:label="t('entry.basicData.agency')"
|
||||||
:options="agenciesOptions"
|
:options="agenciesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -150,7 +154,7 @@ const selectTravel = ({ id }) => {
|
||||||
v-model="travelFilterParams.agencyModeFk"
|
v-model="travelFilterParams.agencyModeFk"
|
||||||
/>
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('globals.warehouseOut')"
|
:label="t('entry.basicData.warehouseOut')"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -158,7 +162,7 @@ const selectTravel = ({ id }) => {
|
||||||
v-model="travelFilterParams.warehouseOutFk"
|
v-model="travelFilterParams.warehouseOutFk"
|
||||||
/>
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('globals.warehouseIn')"
|
:label="t('entry.basicData.warehouseIn')"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -166,11 +170,11 @@ const selectTravel = ({ id }) => {
|
||||||
v-model="travelFilterParams.warehouseInFk"
|
v-model="travelFilterParams.warehouseInFk"
|
||||||
/>
|
/>
|
||||||
<VnInputDate
|
<VnInputDate
|
||||||
:label="t('globals.shipped')"
|
:label="t('entry.basicData.shipped')"
|
||||||
v-model="travelFilterParams.shipped"
|
v-model="travelFilterParams.shipped"
|
||||||
/>
|
/>
|
||||||
<VnInputDate
|
<VnInputDate
|
||||||
:label="t('globals.landed')"
|
:label="t('entry.basicData.landed')"
|
||||||
v-model="travelFilterParams.landed"
|
v-model="travelFilterParams.landed"
|
||||||
/>
|
/>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
|
@ -91,10 +91,6 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
maxWidth: {
|
|
||||||
type: [String, Boolean],
|
|
||||||
default: '800px',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||||
const modelValue = computed(
|
const modelValue = computed(
|
||||||
|
@ -110,7 +106,6 @@ const originalData = ref({});
|
||||||
const formData = computed(() => state.get(modelValue));
|
const formData = computed(() => state.get(modelValue));
|
||||||
const defaultButtons = computed(() => ({
|
const defaultButtons = computed(() => ({
|
||||||
save: {
|
save: {
|
||||||
dataCy: 'saveDefaultBtn',
|
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
icon: 'save',
|
icon: 'save',
|
||||||
label: 'globals.save',
|
label: 'globals.save',
|
||||||
|
@ -118,7 +113,6 @@ const defaultButtons = computed(() => ({
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
},
|
},
|
||||||
reset: {
|
reset: {
|
||||||
dataCy: 'resetDefaultBtn',
|
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
icon: 'restart_alt',
|
icon: 'restart_alt',
|
||||||
label: 'globals.reset',
|
label: 'globals.reset',
|
||||||
|
@ -209,9 +203,7 @@ async function save() {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
formData.value = trimData(formData.value);
|
formData.value = trimData(formData.value);
|
||||||
const body = $props.mapper
|
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||||
? $props.mapper(formData.value, originalData.value)
|
|
||||||
: formData.value;
|
|
||||||
const method = $props.urlCreate ? 'post' : 'patch';
|
const method = $props.urlCreate ? 'post' : 'patch';
|
||||||
const url =
|
const url =
|
||||||
$props.urlCreate || $props.urlUpdate || $props.url || arrayData.store.url;
|
$props.urlCreate || $props.urlUpdate || $props.url || arrayData.store.url;
|
||||||
|
@ -225,6 +217,9 @@ async function save() {
|
||||||
updateAndEmit('onDataSaved', formData.value, response?.data);
|
updateAndEmit('onDataSaved', formData.value, response?.data);
|
||||||
if ($props.reload) await arrayData.fetch({});
|
if ($props.reload) await arrayData.fetch({});
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
notify('errors.writeRequest', 'negative');
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
@ -280,7 +275,6 @@ defineExpose({
|
||||||
hasChanges,
|
hasChanges,
|
||||||
reset,
|
reset,
|
||||||
fetch,
|
fetch,
|
||||||
formData,
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -291,7 +285,6 @@ defineExpose({
|
||||||
@submit="save"
|
@submit="save"
|
||||||
@reset="reset"
|
@reset="reset"
|
||||||
class="q-pa-md"
|
class="q-pa-md"
|
||||||
:style="maxWidth ? 'max-width: ' + maxWidth : ''"
|
|
||||||
id="formModel"
|
id="formModel"
|
||||||
>
|
>
|
||||||
<QCard>
|
<QCard>
|
||||||
|
@ -326,7 +319,6 @@ defineExpose({
|
||||||
:title="t(defaultButtons.reset.label)"
|
:title="t(defaultButtons.reset.label)"
|
||||||
/>
|
/>
|
||||||
<QBtnDropdown
|
<QBtnDropdown
|
||||||
data-cy="saveAndContinueDefaultBtn"
|
|
||||||
v-if="$props.goTo"
|
v-if="$props.goTo"
|
||||||
@click="saveAndGo"
|
@click="saveAndGo"
|
||||||
:label="tMobile('globals.saveAndContinue')"
|
:label="tMobile('globals.saveAndContinue')"
|
||||||
|
@ -381,6 +373,7 @@ defineExpose({
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
#formModel {
|
#formModel {
|
||||||
|
max-width: 800px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ defineExpose({
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
@click="emit('onDataCanceled')"
|
@click="emit('onDataCanceled')"
|
||||||
v-close-popup
|
v-close-popup
|
||||||
data-cy="FormModelPopup_cancel"
|
|
||||||
/>
|
/>
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('globals.save')"
|
:label="t('globals.save')"
|
||||||
|
@ -71,7 +70,6 @@ defineExpose({
|
||||||
class="q-ml-sm"
|
class="q-ml-sm"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
data-cy="FormModelPopup_save"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const emit = defineEmits(['onSubmit']);
|
const emit = defineEmits(['onSubmit']);
|
||||||
|
|
||||||
const $props = defineProps({
|
defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
@ -25,21 +25,16 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
submitOnEnter: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const closeButton = ref(null);
|
const closeButton = ref(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
if ($props.submitOnEnter) {
|
emit('onSubmit');
|
||||||
emit('onSubmit');
|
closeForm();
|
||||||
closeForm();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeForm = () => {
|
const closeForm = () => {
|
||||||
|
|
|
@ -88,16 +88,20 @@ const applyTags = (params, search) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchItemTypes = async (id) => {
|
const fetchItemTypes = async (id) => {
|
||||||
const filter = {
|
try {
|
||||||
fields: ['id', 'name', 'categoryFk'],
|
const filter = {
|
||||||
where: { categoryFk: id },
|
fields: ['id', 'name', 'categoryFk'],
|
||||||
include: 'category',
|
where: { categoryFk: id },
|
||||||
order: 'name ASC',
|
include: 'category',
|
||||||
};
|
order: 'name ASC',
|
||||||
const { data } = await axios.get('ItemTypes', {
|
};
|
||||||
params: { filter: JSON.stringify(filter) },
|
const { data } = await axios.get('ItemTypes', {
|
||||||
});
|
params: { filter: JSON.stringify(filter) },
|
||||||
itemTypesOptions.value = data;
|
});
|
||||||
|
itemTypesOptions.value = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching item types', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCategoryClass = (category, params) => {
|
const getCategoryClass = (category, params) => {
|
||||||
|
@ -107,19 +111,23 @@ const getCategoryClass = (category, params) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSelectedTagValues = async (tag) => {
|
const getSelectedTagValues = async (tag) => {
|
||||||
if (!tag?.selectedTag?.id) return;
|
try {
|
||||||
tag.value = null;
|
if (!tag?.selectedTag?.id) return;
|
||||||
const filter = {
|
tag.value = null;
|
||||||
fields: ['value'],
|
const filter = {
|
||||||
order: 'value ASC',
|
fields: ['value'],
|
||||||
limit: 30,
|
order: 'value ASC',
|
||||||
};
|
limit: 30,
|
||||||
|
};
|
||||||
|
|
||||||
const params = { filter: JSON.stringify(filter) };
|
const params = { filter: JSON.stringify(filter) };
|
||||||
const { data } = await axios.get(`Tags/${tag.selectedTag.id}/filterValue`, {
|
const { data } = await axios.get(`Tags/${tag.selectedTag.id}/filterValue`, {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
tag.valueOptions = data;
|
tag.valueOptions = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error getting selected tag values');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeTag = (index, params, search) => {
|
const removeTag = (index, params, search) => {
|
||||||
|
@ -240,7 +248,7 @@ const removeTag = (index, params, search) => {
|
||||||
>
|
>
|
||||||
<QItemSection class="col">
|
<QItemSection class="col">
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('globals.tag')"
|
:label="t('components.itemsFilterPanel.tag')"
|
||||||
v-model="value.selectedTag"
|
v-model="value.selectedTag"
|
||||||
:options="tagOptions"
|
:options="tagOptions"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -289,12 +297,11 @@ const removeTag = (index, params, search) => {
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem class="q-mt-lg">
|
<QItem class="q-mt-lg">
|
||||||
<QBtn
|
<QIcon
|
||||||
icon="add_circle"
|
name="add_circle"
|
||||||
shortcut="+"
|
|
||||||
flat
|
|
||||||
class="fill-icon-on-hover q-px-xs"
|
class="fill-icon-on-hover q-px-xs"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
size="sm"
|
||||||
@click="tagValues.push({})"
|
@click="tagValues.push({})"
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { onMounted, watch, ref, reactive, computed } from 'vue';
|
import { onMounted, watch, ref, reactive } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QSeparator, useQuasar } from 'quasar';
|
import { QSeparator, useQuasar } from 'quasar';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
@ -9,7 +9,6 @@ import { toLowerCamel } from 'src/filters';
|
||||||
import routes from 'src/router/modules';
|
import routes from 'src/router/modules';
|
||||||
import LeftMenuItem from './LeftMenuItem.vue';
|
import LeftMenuItem from './LeftMenuItem.vue';
|
||||||
import LeftMenuItemGroup from './LeftMenuItemGroup.vue';
|
import LeftMenuItemGroup from './LeftMenuItemGroup.vue';
|
||||||
import VnInput from './common/VnInput.vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -22,52 +21,17 @@ const props = defineProps({
|
||||||
default: 'main',
|
default: 'main',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const initialized = ref(false);
|
|
||||||
const items = ref([]);
|
|
||||||
const expansionItemElements = reactive({});
|
const expansionItemElements = reactive({});
|
||||||
const pinnedModules = computed(() => {
|
|
||||||
const map = new Map();
|
|
||||||
items.value.forEach((item) => item.isPinned && map.set(item.name, item));
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
const search = ref(null);
|
|
||||||
|
|
||||||
const filteredItems = computed(() => {
|
|
||||||
if (!search.value) return items.value;
|
|
||||||
const normalizedSearch = normalize(search.value);
|
|
||||||
return items.value.filter((item) => {
|
|
||||||
const locale = normalize(t(item.title));
|
|
||||||
return locale.includes(normalizedSearch);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const filteredPinnedModules = computed(() => {
|
|
||||||
if (!search.value) return pinnedModules.value;
|
|
||||||
const normalizedSearch = search.value
|
|
||||||
.normalize('NFD')
|
|
||||||
.replace(/[\u0300-\u036f]/g, '')
|
|
||||||
.toLowerCase();
|
|
||||||
const map = new Map();
|
|
||||||
for (const [key, pinnedModule] of pinnedModules.value) {
|
|
||||||
const locale = t(pinnedModule.title)
|
|
||||||
.normalize('NFD')
|
|
||||||
.replace(/[\u0300-\u036f]/g, '')
|
|
||||||
.toLowerCase();
|
|
||||||
if (locale.includes(normalizedSearch)) map.set(key, pinnedModule);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await navigation.fetchPinned();
|
await navigation.fetchPinned();
|
||||||
getRoutes();
|
getRoutes();
|
||||||
initialized.value = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.matched,
|
() => route.matched,
|
||||||
() => {
|
() => {
|
||||||
if (!initialized.value) return;
|
|
||||||
items.value = [];
|
items.value = [];
|
||||||
getRoutes();
|
getRoutes();
|
||||||
},
|
},
|
||||||
|
@ -102,6 +66,8 @@ function addChildren(module, route, parent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const items = ref([]);
|
||||||
|
|
||||||
function getRoutes() {
|
function getRoutes() {
|
||||||
if (props.source === 'main') {
|
if (props.source === 'main') {
|
||||||
const modules = Object.assign([], navigation.getModules().value);
|
const modules = Object.assign([], navigation.getModules().value);
|
||||||
|
@ -157,57 +123,21 @@ async function togglePinned(item, event) {
|
||||||
const handleItemExpansion = (itemName) => {
|
const handleItemExpansion = (itemName) => {
|
||||||
expansionItemElements[itemName].scrollToLastElement();
|
expansionItemElements[itemName].scrollToLastElement();
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalize(text) {
|
|
||||||
return text
|
|
||||||
.normalize('NFD')
|
|
||||||
.replace(/[\u0300-\u036f]/g, '')
|
|
||||||
.toLowerCase();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QList padding class="column-max-width">
|
<QList padding class="column-max-width">
|
||||||
<template v-if="$props.source === 'main'">
|
<template v-if="$props.source === 'main'">
|
||||||
<template v-if="$route?.matched[1]?.name === 'Dashboard'">
|
<template v-if="$route?.matched[1]?.name === 'Dashboard'">
|
||||||
<QItem class="q-pb-md">
|
<QItem class="header">
|
||||||
<VnInput
|
<QItemSection avatar>
|
||||||
v-model="search"
|
<QIcon name="view_module" />
|
||||||
:label="t('Search modules')"
|
</QItemSection>
|
||||||
class="full-width"
|
<QItemSection> {{ t('globals.modules') }}</QItemSection>
|
||||||
filled
|
|
||||||
dense
|
|
||||||
/>
|
|
||||||
</QItem>
|
</QItem>
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
<template v-if="filteredPinnedModules.size">
|
<template v-for="item in items" :key="item.name">
|
||||||
<LeftMenuItem
|
<template v-if="item.children">
|
||||||
v-for="[key, pinnedModule] of filteredPinnedModules"
|
|
||||||
:key="key"
|
|
||||||
:item="pinnedModule"
|
|
||||||
group="modules"
|
|
||||||
>
|
|
||||||
<template #side>
|
|
||||||
<QBtn
|
|
||||||
v-if="pinnedModule.isPinned === true"
|
|
||||||
@click="togglePinned(pinnedModule, $event)"
|
|
||||||
icon="remove_circle"
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
>
|
|
||||||
<QTooltip>
|
|
||||||
{{ t('components.leftMenu.removeFromPinned') }}
|
|
||||||
</QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
</template>
|
|
||||||
</LeftMenuItem>
|
|
||||||
<QSeparator />
|
|
||||||
</template>
|
|
||||||
<template v-for="item in filteredItems" :key="item.name">
|
|
||||||
<template
|
|
||||||
v-if="item.children && !filteredPinnedModules.has(item.name)"
|
|
||||||
>
|
|
||||||
<LeftMenuItem :item="item" group="modules">
|
<LeftMenuItem :item="item" group="modules">
|
||||||
<template #side>
|
<template #side>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
@ -326,7 +256,3 @@ function normalize(text) {
|
||||||
color: var(--vn-label-color);
|
color: var(--vn-label-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
|
||||||
es:
|
|
||||||
Search modules: Buscar módulos
|
|
||||||
</i18n>
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ const itemComputed = computed(() => {
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.q-item {
|
.q-item {
|
||||||
min-height: 5vh;
|
min-height: 5vh;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { onMounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
|
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import PinnedModules from './PinnedModules.vue';
|
import PinnedModules from './PinnedModules.vue';
|
||||||
import UserPanel from 'components/UserPanel.vue';
|
import UserPanel from 'components/UserPanel.vue';
|
||||||
|
@ -13,7 +12,6 @@ import VnAvatar from './ui/VnAvatar.vue';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const stateQuery = useStateQueryStore();
|
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
const appName = 'Lilium';
|
const appName = 'Lilium';
|
||||||
|
@ -52,14 +50,6 @@ const pinnedModulesRef = ref();
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
|
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
|
||||||
<QSpinner
|
|
||||||
color="primary"
|
|
||||||
class="q-ml-md"
|
|
||||||
:class="{
|
|
||||||
'no-visible': !stateQuery.isLoading().value,
|
|
||||||
}"
|
|
||||||
size="xs"
|
|
||||||
/>
|
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<div id="searchbar" class="searchbar"></div>
|
<div id="searchbar" class="searchbar"></div>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
|
|
|
@ -24,9 +24,9 @@ const { notify } = useNotify();
|
||||||
|
|
||||||
const rectificativeTypeOptions = ref([]);
|
const rectificativeTypeOptions = ref([]);
|
||||||
const siiTypeInvoiceOutsOptions = ref([]);
|
const siiTypeInvoiceOutsOptions = ref([]);
|
||||||
|
const inheritWarehouse = ref(true);
|
||||||
const invoiceParams = reactive({
|
const invoiceParams = reactive({
|
||||||
id: $props.invoiceOutData?.id,
|
id: $props.invoiceOutData?.id,
|
||||||
inheritWarehouse: true,
|
|
||||||
});
|
});
|
||||||
const invoiceCorrectionTypesOptions = ref([]);
|
const invoiceCorrectionTypesOptions = ref([]);
|
||||||
|
|
||||||
|
@ -39,10 +39,14 @@ const refund = async () => {
|
||||||
invoiceCorrectionTypeFk: invoiceParams.invoiceCorrectionTypeFk,
|
invoiceCorrectionTypeFk: invoiceParams.invoiceCorrectionTypeFk,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await axios.post('InvoiceOuts/refundAndInvoice', params);
|
try {
|
||||||
notify(t('Refunded invoice'), 'positive');
|
const { data } = await axios.post('InvoiceOuts/refundAndInvoice', params);
|
||||||
const [id] = data?.refundId || [];
|
notify(t('Refunded invoice'), 'positive');
|
||||||
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });
|
const [id] = data?.refundId || [];
|
||||||
|
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error refunding invoice', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -134,7 +138,7 @@ const refund = async () => {
|
||||||
<div>
|
<div>
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
:label="t('Inherit warehouse')"
|
:label="t('Inherit warehouse')"
|
||||||
v-model="invoiceParams.inheritWarehouse"
|
v-model="inheritWarehouse"
|
||||||
/>
|
/>
|
||||||
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
|
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
|
||||||
<QTooltip>{{ t('Inherit warehouse tooltip') }}</QTooltip>
|
<QTooltip>{{ t('Inherit warehouse tooltip') }}</QTooltip>
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
<script setup>
|
|
||||||
defineProps({ row: { type: Object, required: true } });
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<span>
|
|
||||||
<QIcon
|
|
||||||
v-if="row.isTaxDataChecked === 0"
|
|
||||||
name="vn:no036"
|
|
||||||
color="primary"
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
<QTooltip>{{ $t('salesTicketsTable.noVerifiedData') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
<QIcon v-if="row.hasTicketRequest" name="vn:buyrequest" color="primary" size="xs">
|
|
||||||
<QTooltip>{{ $t('salesTicketsTable.purchaseRequest') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
<QIcon v-if="row.itemShortage" name="vn:unavailable" color="primary" size="xs">
|
|
||||||
<QTooltip>{{ $t('salesTicketsTable.notVisible') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
<QIcon v-if="row.isFreezed" name="vn:frozen" color="primary" size="xs">
|
|
||||||
<QTooltip>{{ $t('salesTicketsTable.clientFrozen') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
<QIcon
|
|
||||||
v-if="row.risk"
|
|
||||||
name="vn:risk"
|
|
||||||
:color="row.hasHighRisk ? 'negative' : 'primary'"
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
<QTooltip>
|
|
||||||
{{ $t('salesTicketsTable.risk') }}: {{ row.risk - row.credit }}
|
|
||||||
</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
<QIcon v-if="row.hasComponentLack" name="vn:components" color="primary" size="xs">
|
|
||||||
<QTooltip>{{ $t('salesTicketsTable.componentLack') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
<QIcon v-if="row.isTooLittle" name="vn:isTooLittle" color="primary" size="xs">
|
|
||||||
<QTooltip>{{ $t('salesTicketsTable.tooLittle') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
|
@ -49,32 +49,36 @@ const makeInvoice = async () => {
|
||||||
makeInvoice: checked.value,
|
makeInvoice: checked.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (checked.value && hasToInvoiceByAddress) {
|
try {
|
||||||
const response = await new Promise((resolve) => {
|
if (checked.value && hasToInvoiceByAddress) {
|
||||||
quasar
|
const response = await new Promise((resolve) => {
|
||||||
.dialog({
|
quasar
|
||||||
component: VnConfirm,
|
.dialog({
|
||||||
componentProps: {
|
component: VnConfirm,
|
||||||
title: t('Bill destination client'),
|
componentProps: {
|
||||||
message: t('transferInvoiceInfo'),
|
title: t('Bill destination client'),
|
||||||
},
|
message: t('transferInvoiceInfo'),
|
||||||
})
|
},
|
||||||
.onOk(() => {
|
})
|
||||||
resolve(true);
|
.onOk(() => {
|
||||||
})
|
resolve(true);
|
||||||
.onCancel(() => {
|
})
|
||||||
resolve(false);
|
.onCancel(() => {
|
||||||
});
|
resolve(false);
|
||||||
});
|
});
|
||||||
if (!response) {
|
});
|
||||||
return;
|
if (!response) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await axios.post('InvoiceOuts/transfer', params);
|
const { data } = await axios.post('InvoiceOuts/transfer', params);
|
||||||
notify(t('Transferred invoice'), 'positive');
|
notify(t('Transferred invoice'), 'positive');
|
||||||
const id = data?.[0];
|
const id = data?.[0];
|
||||||
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });
|
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error transfering invoice', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,12 @@ import FetchData from 'components/FetchData.vue';
|
||||||
import { useClipboard } from 'src/composables/useClipboard';
|
import { useClipboard } from 'src/composables/useClipboard';
|
||||||
import { useRole } from 'src/composables/useRole';
|
import { useRole } from 'src/composables/useRole';
|
||||||
import VnAvatar from './ui/VnAvatar.vue';
|
import VnAvatar from './ui/VnAvatar.vue';
|
||||||
import useNotify from 'src/composables/useNotify';
|
|
||||||
|
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
const { copyText } = useClipboard();
|
const { copyText } = useClipboard();
|
||||||
const { notify } = useNotify();
|
|
||||||
|
|
||||||
const userLocale = computed({
|
const userLocale = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -55,7 +53,6 @@ const user = state.getUser();
|
||||||
const warehousesData = ref();
|
const warehousesData = ref();
|
||||||
const companiesData = ref();
|
const companiesData = ref();
|
||||||
const accountBankData = ref();
|
const accountBankData = ref();
|
||||||
const isEmployee = computed(() => useRole().isEmployee());
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
updatePreferences();
|
updatePreferences();
|
||||||
|
@ -73,28 +70,18 @@ function updatePreferences() {
|
||||||
|
|
||||||
async function saveDarkMode(value) {
|
async function saveDarkMode(value) {
|
||||||
const query = `/UserConfigs/${user.value.id}`;
|
const query = `/UserConfigs/${user.value.id}`;
|
||||||
try {
|
await axios.patch(query, {
|
||||||
await axios.patch(query, {
|
darkMode: value,
|
||||||
darkMode: value,
|
});
|
||||||
});
|
user.value.darkMode = value;
|
||||||
user.value.darkMode = value;
|
|
||||||
onDataSaved();
|
|
||||||
} catch (error) {
|
|
||||||
onDataError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveLanguage(value) {
|
async function saveLanguage(value) {
|
||||||
const query = `/VnUsers/${user.value.id}`;
|
const query = `/VnUsers/${user.value.id}`;
|
||||||
try {
|
await axios.patch(query, {
|
||||||
await axios.patch(query, {
|
lang: value,
|
||||||
lang: value,
|
});
|
||||||
});
|
user.value.lang = value;
|
||||||
user.value.lang = value;
|
|
||||||
onDataSaved();
|
|
||||||
} catch (error) {
|
|
||||||
onDataError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
|
@ -110,23 +97,11 @@ function localUserData() {
|
||||||
state.setUser(user.value);
|
state.setUser(user.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveUserData(param, value) {
|
function saveUserData(param, value) {
|
||||||
try {
|
axios.post('UserConfigs/setUserConfig', { [param]: value });
|
||||||
await axios.post('UserConfigs/setUserConfig', { [param]: value });
|
localUserData();
|
||||||
localUserData();
|
|
||||||
onDataSaved();
|
|
||||||
} catch (error) {
|
|
||||||
onDataError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const isEmployee = computed(() => useRole().isEmployee());
|
||||||
const onDataSaved = () => {
|
|
||||||
notify('globals.dataSaved', 'positive');
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDataError = () => {
|
|
||||||
notify('errors.updateUserConfig', 'negative');
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -7,71 +7,38 @@ import VnSelectDialog from 'components/common/VnSelectDialog.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import CreateNewProvinceForm from './CreateNewProvinceForm.vue';
|
import CreateNewProvinceForm from './CreateNewProvinceForm.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['onProvinceCreated', 'onProvinceFetched']);
|
const emit = defineEmits(['onProvinceCreated']);
|
||||||
const $props = defineProps({
|
const provinceFk = defineModel({ type: Number });
|
||||||
countryFk: {
|
watch(provinceFk, async () => await provincesFetchDataRef.value.fetch());
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
provinceSelected: {
|
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const provinceFk = defineModel({ type: Number, default: null });
|
|
||||||
|
|
||||||
const { validate } = useValidator();
|
const { validate } = useValidator();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const filter = ref({
|
|
||||||
include: { relation: 'country' },
|
const provincesOptions = ref();
|
||||||
where: {
|
|
||||||
countryFk: $props.countryFk,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const provincesOptions = ref($props.provinces);
|
|
||||||
const provincesFetchDataRef = ref();
|
const provincesFetchDataRef = ref();
|
||||||
provinceFk.value = $props.provinceSelected;
|
|
||||||
if (!$props.countryFk) {
|
|
||||||
filter.value.where = {};
|
|
||||||
}
|
|
||||||
async function onProvinceCreated(_, data) {
|
async function onProvinceCreated(_, data) {
|
||||||
await provincesFetchDataRef.value.fetch({ where: { countryFk: $props.countryFk } });
|
await provincesFetchDataRef.value.fetch();
|
||||||
provinceFk.value = data.id;
|
provinceFk.value = data.id;
|
||||||
emit('onProvinceCreated', data);
|
emit('onProvinceCreated', data);
|
||||||
}
|
}
|
||||||
async function handleProvinces(data) {
|
|
||||||
provincesOptions.value = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => $props.countryFk,
|
|
||||||
async () => {
|
|
||||||
if ($props.countryFk) {
|
|
||||||
filter.value.where.countryFk = $props.countryFk;
|
|
||||||
} else filter.value.where = {};
|
|
||||||
await provincesFetchDataRef.value.fetch({});
|
|
||||||
emit('onProvinceFetched', provincesOptions.value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
ref="provincesFetchDataRef"
|
ref="provincesFetchDataRef"
|
||||||
:filter="filter"
|
:filter="{ include: { relation: 'country' } }"
|
||||||
@on-fetch="handleProvinces"
|
@on-fetch="(data) => (provincesOptions = data)"
|
||||||
url="Provinces"
|
|
||||||
auto-load
|
auto-load
|
||||||
|
url="Provinces"
|
||||||
/>
|
/>
|
||||||
<VnSelectDialog
|
<VnSelectDialog
|
||||||
:label="t('Province')"
|
:label="t('Province')"
|
||||||
:options="provincesOptions"
|
:options="provincesOptions"
|
||||||
:tooltip="t('Create province')"
|
|
||||||
hide-selected
|
hide-selected
|
||||||
:clearable="true"
|
|
||||||
v-model="provinceFk"
|
v-model="provinceFk"
|
||||||
:rules="validate && validate('postcode.provinceFk')"
|
:rules="validate && validate('postcode.provinceFk')"
|
||||||
:acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]"
|
:roles-allowed-to-create="['deliveryAssistant', 'administrative']"
|
||||||
>
|
>
|
||||||
<template #option="{ itemProps, opt }">
|
<template #option="{ itemProps, opt }">
|
||||||
<QItem v-bind="itemProps">
|
<QItem v-bind="itemProps">
|
||||||
|
@ -82,15 +49,11 @@ watch(
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewProvinceForm
|
<CreateNewProvinceForm @on-data-saved="onProvinceCreated" />
|
||||||
:country-fk="$props.countryFk"
|
|
||||||
@on-data-saved="onProvinceCreated"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</VnSelectDialog>
|
</VnSelectDialog>
|
||||||
</template>
|
</template>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Province: Provincia
|
Province: Provincia
|
||||||
Create province: Crear provincia
|
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { markRaw, computed } from 'vue';
|
import { markRaw, computed, defineModel } from 'vue';
|
||||||
import { QIcon, QCheckbox } from 'quasar';
|
import { QIcon, QCheckbox } from 'quasar';
|
||||||
import { dashIfEmpty } from 'src/filters';
|
import { dashIfEmpty } from 'src/filters';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { markRaw, computed } from 'vue';
|
import { markRaw, computed, defineModel } from 'vue';
|
||||||
import { QCheckbox } from 'quasar';
|
import { QCheckbox } from 'quasar';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
import VnInputTime from 'components/common/VnInputTime.vue';
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
||||||
|
|
||||||
|
defineExpose({ addFilter });
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
column: {
|
column: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -25,12 +27,9 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
searchUrl: {
|
searchUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'table',
|
default: 'params',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({ addFilter, props: $props });
|
|
||||||
|
|
||||||
const model = defineModel(undefined, { required: true });
|
const model = defineModel(undefined, { required: true });
|
||||||
const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
|
const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
|
||||||
const columnFilter = computed(() => $props.column?.columnFilter);
|
const columnFilter = computed(() => $props.column?.columnFilter);
|
||||||
|
@ -116,11 +115,11 @@ const components = {
|
||||||
rawSelect: selectComponent,
|
rawSelect: selectComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
async function addFilter(value, name) {
|
async function addFilter(value) {
|
||||||
value ??= undefined;
|
value ??= undefined;
|
||||||
if (value && typeof value === 'object') value = model.value;
|
if (value && typeof value === 'object') value = model.value;
|
||||||
value = value === '' ? undefined : value;
|
value = value === '' ? undefined : value;
|
||||||
let field = columnFilter.value?.name ?? $props.column.name ?? name;
|
let field = columnFilter.value?.name ?? $props.column.name;
|
||||||
|
|
||||||
if (columnFilter.value?.inWhere) {
|
if (columnFilter.value?.inWhere) {
|
||||||
if (columnFilter.value.alias) field = columnFilter.value.alias + '.' + field;
|
if (columnFilter.value.alias) field = columnFilter.value.alias + '.' + field;
|
||||||
|
@ -143,10 +142,6 @@ function alignRow() {
|
||||||
const showFilter = computed(
|
const showFilter = computed(
|
||||||
() => $props.column?.columnFilter !== false && $props.column.name != 'tableActions'
|
() => $props.column?.columnFilter !== false && $props.column.name != 'tableActions'
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTabPressed = async () => {
|
|
||||||
if (model.value) enterEvent['keyup.enter']();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
@ -161,7 +156,6 @@ const onTabPressed = async () => {
|
||||||
v-model="model"
|
v-model="model"
|
||||||
:components="components"
|
:components="components"
|
||||||
component-prop="columnFilter"
|
component-prop="columnFilter"
|
||||||
@keydown.tab="onTabPressed"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useArrayData } from 'composables/useArrayData';
|
||||||
const model = defineModel({ type: Object });
|
const model = defineModel({ type: Object });
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
name: {
|
name: {
|
||||||
type: [String, Boolean],
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
|
@ -17,7 +17,7 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
searchUrl: {
|
searchUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'table',
|
default: 'params',
|
||||||
},
|
},
|
||||||
vertical: {
|
vertical: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
@ -10,7 +10,7 @@ import FormModelPopup from 'components/FormModelPopup.vue';
|
||||||
|
|
||||||
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||||
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
||||||
import VnFilter from 'components/VnTable/VnFilter.vue';
|
import VnTableFilter from 'components/VnTable/VnFilter.vue';
|
||||||
import VnTableChip from 'components/VnTable/VnChip.vue';
|
import VnTableChip from 'components/VnTable/VnChip.vue';
|
||||||
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
|
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
|
||||||
import VnLv from 'components/ui/VnLv.vue';
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
@ -53,16 +53,12 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
bottom: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
cardClass: {
|
cardClass: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'flex-one',
|
default: 'flex-one',
|
||||||
},
|
},
|
||||||
searchUrl: {
|
searchUrl: {
|
||||||
type: [String, Boolean],
|
type: String,
|
||||||
default: 'table',
|
default: 'table',
|
||||||
},
|
},
|
||||||
isEditable: {
|
isEditable: {
|
||||||
|
@ -75,7 +71,7 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
hasSubToolbar: {
|
hasSubToolbar: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: null,
|
default: true,
|
||||||
},
|
},
|
||||||
disableOption: {
|
disableOption: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -101,18 +97,6 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '90vh',
|
default: '90vh',
|
||||||
},
|
},
|
||||||
chipLocale: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
disabledAttr: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
@ -133,8 +117,6 @@ const showForm = ref(false);
|
||||||
const splittedColumns = ref({ columns: [] });
|
const splittedColumns = ref({ columns: [] });
|
||||||
const columnsVisibilitySkipped = ref();
|
const columnsVisibilitySkipped = ref();
|
||||||
const createForm = ref();
|
const createForm = ref();
|
||||||
const tableFilterRef = ref([]);
|
|
||||||
const tableRef = ref();
|
|
||||||
|
|
||||||
const tableModes = [
|
const tableModes = [
|
||||||
{
|
{
|
||||||
|
@ -151,8 +133,8 @@ const tableModes = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
const urlParams = route.query[$props.searchUrl];
|
setUserParams(route.query[$props.searchUrl]);
|
||||||
hasParams.value = urlParams && Object.keys(urlParams).length !== 0;
|
hasParams.value = params.value && Object.keys(params.value).length !== 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -160,9 +142,11 @@ onMounted(() => {
|
||||||
quasar.platform.is.mobile && !$props.disableOption?.card
|
quasar.platform.is.mobile && !$props.disableOption?.card
|
||||||
? CARD_MODE
|
? CARD_MODE
|
||||||
: $props.defaultMode;
|
: $props.defaultMode;
|
||||||
stateStore.rightDrawer = quasar.screen.gt.xs;
|
stateStore.rightDrawer = true;
|
||||||
columnsVisibilitySkipped.value = [
|
columnsVisibilitySkipped.value = [
|
||||||
...splittedColumns.value.columns.filter((c) => !c.visible).map((c) => c.name),
|
...splittedColumns.value.columns
|
||||||
|
.filter((c) => c.visible == false)
|
||||||
|
.map((c) => c.name),
|
||||||
...['tableActions'],
|
...['tableActions'],
|
||||||
];
|
];
|
||||||
createForm.value = $props.create;
|
createForm.value = $props.create;
|
||||||
|
@ -183,8 +167,7 @@ watch(
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.query[$props.searchUrl],
|
() => route.query[$props.searchUrl],
|
||||||
(val) => setUserParams(val),
|
(val) => setUserParams(val)
|
||||||
{ immediate: true, deep: true }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
||||||
|
@ -235,8 +218,8 @@ function splitColumns(columns) {
|
||||||
if (col.create) splittedColumns.value.create.push(col);
|
if (col.create) splittedColumns.value.create.push(col);
|
||||||
if (col.cardVisible) splittedColumns.value.cardVisible.push(col);
|
if (col.cardVisible) splittedColumns.value.cardVisible.push(col);
|
||||||
if ($props.isEditable && col.disable == null) col.disable = false;
|
if ($props.isEditable && col.disable == null) col.disable = false;
|
||||||
if ($props.useModel && col.columnFilter !== false)
|
if ($props.useModel && col.columnFilter != false)
|
||||||
col.columnFilter = { inWhere: true, ...col.columnFilter };
|
col.columnFilter = { ...col.columnFilter, inWhere: true };
|
||||||
splittedColumns.value.columns.push(col);
|
splittedColumns.value.columns.push(col);
|
||||||
}
|
}
|
||||||
// Status column
|
// Status column
|
||||||
|
@ -314,37 +297,12 @@ defineExpose({
|
||||||
redirect: redirectFn,
|
redirect: redirectFn,
|
||||||
selected,
|
selected,
|
||||||
CrudModelRef,
|
CrudModelRef,
|
||||||
params,
|
|
||||||
tableRef,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleOnDataSaved(_) {
|
function handleOnDataSaved(_, res) {
|
||||||
if (_.onDataSaved) _.onDataSaved({ CrudModelRef: CrudModelRef.value });
|
if (_.onDataSaved) _.onDataSaved({ CrudModelRef: CrudModelRef.value });
|
||||||
else $props.create.onDataSaved(_);
|
else $props.create.onDataSaved(_);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleScroll() {
|
|
||||||
if ($props.crudModel.disableInfiniteScroll) return;
|
|
||||||
|
|
||||||
const tMiddle = tableRef.value.$el.querySelector('.q-table__middle');
|
|
||||||
const { scrollHeight, scrollTop, clientHeight } = tMiddle;
|
|
||||||
const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) <= 40;
|
|
||||||
if (isAtBottom) CrudModelRef.value.vnPaginateRef.paginate();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
|
||||||
if (evt?.shiftKey && added) {
|
|
||||||
const rowIndex = selectedRows[0].$index;
|
|
||||||
const selectedIndexes = new Set(selected.value.map((row) => row.$index));
|
|
||||||
for (const row of rows) {
|
|
||||||
if (row.$index == rowIndex) break;
|
|
||||||
if (!selectedIndexes.has(row.$index)) {
|
|
||||||
selected.value.push(row);
|
|
||||||
selectedIndexes.add(row.$index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QDrawer
|
<QDrawer
|
||||||
|
@ -362,13 +320,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
:search-url="searchUrl"
|
:search-url="searchUrl"
|
||||||
:redirect="!!redirect"
|
:redirect="!!redirect"
|
||||||
@set-user-params="setUserParams"
|
@set-user-params="setUserParams"
|
||||||
:disable-submit-event="true"
|
|
||||||
@remove="
|
|
||||||
(key) =>
|
|
||||||
tableFilterRef
|
|
||||||
.find((f) => f.props?.column.name == key)
|
|
||||||
?.addFilter()
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div
|
<div
|
||||||
|
@ -378,8 +329,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
)"
|
)"
|
||||||
:key="col.id"
|
:key="col.id"
|
||||||
>
|
>
|
||||||
<VnFilter
|
<VnTableFilter
|
||||||
ref="tableFilterRef"
|
|
||||||
:column="col"
|
:column="col"
|
||||||
:data-key="$attrs['data-key']"
|
:data-key="$attrs['data-key']"
|
||||||
v-model="params[columnName(col)]"
|
v-model="params[columnName(col)]"
|
||||||
|
@ -394,7 +344,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
:name="col.orderBy ?? col.name"
|
:name="col.orderBy ?? col.name"
|
||||||
:data-key="$attrs['data-key']"
|
:data-key="$attrs['data-key']"
|
||||||
:search-url="searchUrl"
|
:search-url="searchUrl"
|
||||||
:vertical="false"
|
:vertical="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<slot
|
<slot
|
||||||
|
@ -403,25 +353,21 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
:columns="splittedColumns.columns"
|
:columns="splittedColumns.columns"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #tags="{ tag, formatFn }" v-if="chipLocale">
|
|
||||||
<div class="q-gutter-x-xs">
|
|
||||||
<strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
|
|
||||||
<span>{{ formatFn(tag.value) }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VnFilterPanel>
|
</VnFilterPanel>
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
|
<!-- class in div to fix warn-->
|
||||||
|
|
||||||
<CrudModel
|
<CrudModel
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:class="$attrs['class'] ?? 'q-px-md'"
|
:class="$attrs['class'] ?? 'q-px-md'"
|
||||||
:limit="$attrs['limit'] ?? 20"
|
:limit="20"
|
||||||
ref="CrudModelRef"
|
ref="CrudModelRef"
|
||||||
@on-fetch="(...args) => emit('onFetch', ...args)"
|
@on-fetch="(...args) => emit('onFetch', ...args)"
|
||||||
:search-url="searchUrl"
|
:search-url="searchUrl"
|
||||||
:disable-infinite-scroll="isTableMode"
|
:disable-infinite-scroll="isTableMode"
|
||||||
@save-changes="reload"
|
@save-changes="reload"
|
||||||
:has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
|
:has-sub-toolbar="$attrs['hasSubToolbar'] ?? isEditable"
|
||||||
:auto-load="hasParams || $attrs['auto-load']"
|
:auto-load="hasParams || $attrs['auto-load']"
|
||||||
>
|
>
|
||||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
||||||
|
@ -429,10 +375,8 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<QTable
|
<QTable
|
||||||
ref="tableRef"
|
|
||||||
v-bind="table"
|
v-bind="table"
|
||||||
class="vnTable"
|
class="vnTable"
|
||||||
:class="{ 'last-row-sticky': $props.footer }"
|
|
||||||
:columns="splittedColumns.columns"
|
:columns="splittedColumns.columns"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
|
@ -441,11 +385,15 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
card-container-class="grid-three"
|
card-container-class="grid-three"
|
||||||
flat
|
flat
|
||||||
:style="isTableMode && `max-height: ${tableHeight}`"
|
:style="isTableMode && `max-height: ${tableHeight}`"
|
||||||
:virtual-scroll="isTableMode"
|
virtual-scroll
|
||||||
@virtual-scroll="handleScroll"
|
@virtual-scroll="
|
||||||
|
(event) =>
|
||||||
|
event.index > rows.length - 2 &&
|
||||||
|
($props.crudModel?.paginate ?? true) &&
|
||||||
|
CrudModelRef.vnPaginateRef.paginate()
|
||||||
|
"
|
||||||
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
|
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
|
||||||
@update:selected="emit('update:selected', $event)"
|
@update:selected="emit('update:selected', $event)"
|
||||||
@selection="(details) => handleSelection(details, rows)"
|
|
||||||
>
|
>
|
||||||
<template #top-left v-if="!$props.withoutHeader">
|
<template #top-left v-if="!$props.withoutHeader">
|
||||||
<slot name="top-left"></slot>
|
<slot name="top-left"></slot>
|
||||||
|
@ -473,11 +421,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #header-cell="{ col }">
|
<template #header-cell="{ col }">
|
||||||
<QTh
|
<QTh v-if="col.visible ?? true">
|
||||||
v-if="col.visible ?? true"
|
|
||||||
:style="col.headerStyle"
|
|
||||||
:class="col.headerClass"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="column self-start q-ml-xs ellipsis"
|
class="column self-start q-ml-xs ellipsis"
|
||||||
:class="`text-${col?.align ?? 'left'}`"
|
:class="`text-${col?.align ?? 'left'}`"
|
||||||
|
@ -493,7 +437,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
:search-url="searchUrl"
|
:search-url="searchUrl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<VnFilter
|
<VnTableFilter
|
||||||
v-if="$props.columnSearch"
|
v-if="$props.columnSearch"
|
||||||
:column="col"
|
:column="col"
|
||||||
:show-title="true"
|
:show-title="true"
|
||||||
|
@ -523,7 +467,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
auto-width
|
auto-width
|
||||||
class="no-margin q-px-xs"
|
class="no-margin q-px-xs"
|
||||||
:class="[getColAlign(col), col.columnClass]"
|
:class="[getColAlign(col), col.columnClass]"
|
||||||
:style="col.style"
|
|
||||||
v-if="col.visible ?? true"
|
v-if="col.visible ?? true"
|
||||||
@click.ctrl="
|
@click.ctrl="
|
||||||
($event) =>
|
($event) =>
|
||||||
|
@ -552,7 +495,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
:class="getColAlign(col)"
|
:class="getColAlign(col)"
|
||||||
class="sticky no-padding"
|
class="sticky no-padding"
|
||||||
@click="stopEventPropagation($event)"
|
@click="stopEventPropagation($event)"
|
||||||
:style="col.style"
|
|
||||||
>
|
>
|
||||||
<QBtn
|
<QBtn
|
||||||
v-for="(btn, index) of col.actions"
|
v-for="(btn, index) of col.actions"
|
||||||
|
@ -573,29 +515,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
/>
|
/>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #bottom v-if="bottom">
|
|
||||||
<slot name="bottom-table">
|
|
||||||
<QBtn
|
|
||||||
@click="
|
|
||||||
() =>
|
|
||||||
createAsDialog
|
|
||||||
? (showForm = !showForm)
|
|
||||||
: handleOnDataSaved(create)
|
|
||||||
"
|
|
||||||
class="cursor-pointer fill-icon"
|
|
||||||
color="primary"
|
|
||||||
icon="add_circle"
|
|
||||||
size="md"
|
|
||||||
round
|
|
||||||
flat
|
|
||||||
shortcut="+"
|
|
||||||
:disabled="!disabledAttr"
|
|
||||||
/>
|
|
||||||
<QTooltip>
|
|
||||||
{{ createForm.title }}
|
|
||||||
</QTooltip>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
<template #item="{ row, colsMap }">
|
<template #item="{ row, colsMap }">
|
||||||
<component
|
<component
|
||||||
:is="$props.redirect ? 'router-link' : 'span'"
|
:is="$props.redirect ? 'router-link' : 'span'"
|
||||||
|
@ -712,18 +631,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
</QCard>
|
</QCard>
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
<template #bottom-row="{ cols }" v-if="$props.footer">
|
|
||||||
<QTr v-if="rows.length" style="height: 30px">
|
|
||||||
<QTh
|
|
||||||
v-for="col of cols.filter((cols) => cols.visible ?? true)"
|
|
||||||
:key="col?.id"
|
|
||||||
class="text-center"
|
|
||||||
:class="getColAlign(col)"
|
|
||||||
>
|
|
||||||
<slot :name="`column-footer-${col.name}`" />
|
|
||||||
</QTh>
|
|
||||||
</QTr>
|
|
||||||
</template>
|
|
||||||
</QTable>
|
</QTable>
|
||||||
</template>
|
</template>
|
||||||
</CrudModel>
|
</CrudModel>
|
||||||
|
@ -737,9 +644,8 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
fab
|
fab
|
||||||
icon="add"
|
icon="add"
|
||||||
shortcut="+"
|
shortcut="+"
|
||||||
data-cy="vnTableCreateBtn"
|
|
||||||
/>
|
/>
|
||||||
<QTooltip self="top right">
|
<QTooltip>
|
||||||
{{ createForm?.title }}
|
{{ createForm?.title }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
|
@ -792,7 +698,7 @@ es:
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-header {
|
.bg-header {
|
||||||
background-color: var(--vn-accent-color);
|
background-color: var(--vn-header-color);
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -800,6 +706,18 @@ es:
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.q-table--dark .q-table__bottom,
|
||||||
|
.q-table--dark thead,
|
||||||
|
.q-table--dark tr,
|
||||||
|
.q-table--dark th,
|
||||||
|
.q-table--dark td {
|
||||||
|
border-color: var(--vn-section-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-table__container > div:first-child {
|
||||||
|
background-color: var(--vn-page-color);
|
||||||
|
}
|
||||||
|
|
||||||
.grid-three {
|
.grid-three {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(400px, max-content));
|
grid-template-columns: repeat(auto-fit, minmax(400px, max-content));
|
||||||
|
@ -838,7 +756,6 @@ es:
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.vnTable {
|
.vnTable {
|
||||||
thead tr th {
|
thead tr th {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
@ -847,10 +764,6 @@ es:
|
||||||
thead tr:first-child th {
|
thead tr:first-child th {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
.q-table__top {
|
|
||||||
top: 0;
|
|
||||||
padding: 12px 0;
|
|
||||||
}
|
|
||||||
tbody {
|
tbody {
|
||||||
.q-checkbox {
|
.q-checkbox {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -874,18 +787,6 @@ es:
|
||||||
background-color: var(--vn-section-color);
|
background-color: var(--vn-section-color);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
table tbody th {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.last-row-sticky {
|
|
||||||
tbody:nth-last-child(1) {
|
|
||||||
@extend .bg-header;
|
|
||||||
position: sticky;
|
|
||||||
z-index: 2;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vn-label-value {
|
.vn-label-value {
|
||||||
|
@ -931,12 +832,4 @@ es:
|
||||||
cursor: text;
|
cursor: text;
|
||||||
user-select: all;
|
user-select: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.q-table__container {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-table__middle.q-virtual-scroll.q-virtual-scroll--vertical.scroll {
|
|
||||||
background-color: var(--vn-section-color);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -58,71 +58,79 @@ const getConfig = async (url, filter) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchViewConfigData = async () => {
|
const fetchViewConfigData = async () => {
|
||||||
const userConfigFilter = {
|
try {
|
||||||
where: { tableCode: $props.tableCode, userFk: user.value.id },
|
const userConfigFilter = {
|
||||||
};
|
where: { tableCode: $props.tableCode, userFk: user.value.id },
|
||||||
const userConfig = await getConfig('UserConfigViews', userConfigFilter);
|
};
|
||||||
|
const userConfig = await getConfig('UserConfigViews', userConfigFilter);
|
||||||
|
|
||||||
if (userConfig) {
|
if (userConfig) {
|
||||||
initialUserConfigViewData.value = userConfig;
|
initialUserConfigViewData.value = userConfig;
|
||||||
setUserConfigViewData(userConfig.configuration);
|
setUserConfigViewData(userConfig.configuration);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultConfigFilter = { where: { tableCode: $props.tableCode } };
|
const defaultConfigFilter = { where: { tableCode: $props.tableCode } };
|
||||||
const defaultConfig = await getConfig('DefaultViewConfigs', defaultConfigFilter);
|
const defaultConfig = await getConfig('DefaultViewConfigs', defaultConfigFilter);
|
||||||
|
|
||||||
if (defaultConfig) {
|
if (defaultConfig) {
|
||||||
// Si el backend devuelve una configuración por defecto la usamos
|
// Si el backend devuelve una configuración por defecto la usamos
|
||||||
setUserConfigViewData(defaultConfig.columns);
|
setUserConfigViewData(defaultConfig.columns);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Si no hay configuración por defecto mostramos todas las columnas
|
// Si no hay configuración por defecto mostramos todas las columnas
|
||||||
const defaultColumns = {};
|
const defaultColumns = {};
|
||||||
$props.allColumns.forEach((col) => (defaultColumns[col] = true));
|
$props.allColumns.forEach((col) => (defaultColumns[col] = true));
|
||||||
setUserConfigViewData(defaultColumns);
|
setUserConfigViewData(defaultColumns);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching config view data', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
const params = {};
|
try {
|
||||||
const configuration = {};
|
const params = {};
|
||||||
|
const configuration = {};
|
||||||
|
|
||||||
formattedCols.value.forEach((col) => {
|
formattedCols.value.forEach((col) => {
|
||||||
const { name, active } = col;
|
const { name, active } = col;
|
||||||
configuration[name] = active;
|
configuration[name] = active;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Si existe una view config del usuario hacemos un update si no la creamos
|
// Si existe una view config del usuario hacemos un update si no la creamos
|
||||||
if (initialUserConfigViewData.value) {
|
if (initialUserConfigViewData.value) {
|
||||||
params.updates = [
|
params.updates = [
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
|
configuration: configuration,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
id: initialUserConfigViewData.value.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
params.creates = [
|
||||||
|
{
|
||||||
|
userFk: user.value.id,
|
||||||
|
tableCode: $props.tableCode,
|
||||||
|
tableConfig: $props.tableCode,
|
||||||
configuration: configuration,
|
configuration: configuration,
|
||||||
},
|
},
|
||||||
where: {
|
];
|
||||||
id: initialUserConfigViewData.value.id,
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
params.creates = [
|
|
||||||
{
|
|
||||||
userFk: user.value.id,
|
|
||||||
tableCode: $props.tableCode,
|
|
||||||
tableConfig: $props.tableCode,
|
|
||||||
configuration: configuration,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post('UserConfigViews/crud', params);
|
const response = await axios.post('UserConfigViews/crud', params);
|
||||||
if (response.data && response.data[0]) {
|
if (response.data && response.data[0]) {
|
||||||
initialUserConfigViewData.value = response.data[0];
|
initialUserConfigViewData.value = response.data[0];
|
||||||
|
}
|
||||||
|
emitSavedConfig();
|
||||||
|
notify('globals.dataSaved', 'positive');
|
||||||
|
popupProxyRef.value.hide();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error saving user view config', err);
|
||||||
}
|
}
|
||||||
emitSavedConfig();
|
|
||||||
notify('globals.dataSaved', 'positive');
|
|
||||||
popupProxyRef.value.hide();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const emitSavedConfig = () => {
|
const emitSavedConfig = () => {
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { nextTick, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { QInput } from 'quasar';
|
import { QInput } from 'quasar';
|
||||||
|
|
||||||
const $props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
insertable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'accountShortToStandard']);
|
const emit = defineEmits(['update:modelValue', 'accountShortToStandard']);
|
||||||
|
|
||||||
let internalValue = ref($props.modelValue);
|
let internalValue = ref(props.modelValue);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => $props.modelValue,
|
() => props.modelValue,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
internalValue.value = newVal;
|
internalValue.value = newVal;
|
||||||
}
|
}
|
||||||
|
@ -32,46 +28,8 @@ watch(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeydown = (e) => {
|
|
||||||
if (e.key === 'Backspace') return;
|
|
||||||
if (e.key === '.') {
|
|
||||||
accountShortToStandard();
|
|
||||||
// TODO: Fix this setTimeout, with nextTick doesn't work
|
|
||||||
setTimeout(() => {
|
|
||||||
setCursorPosition(0, e.target);
|
|
||||||
}, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($props.insertable && e.key.match(/[0-9]/)) {
|
|
||||||
handleInsertMode(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function setCursorPosition(pos, el = vnInputRef.value) {
|
|
||||||
el.focus();
|
|
||||||
el.setSelectionRange(pos, pos);
|
|
||||||
}
|
|
||||||
const vnInputRef = ref(false);
|
|
||||||
const handleInsertMode = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const input = e.target;
|
|
||||||
const cursorPos = input.selectionStart;
|
|
||||||
const { maxlength } = vnInputRef.value;
|
|
||||||
let currentValue = internalValue.value;
|
|
||||||
if (!currentValue) currentValue = e.key;
|
|
||||||
const newValue = e.key;
|
|
||||||
if (newValue && !isNaN(newValue) && cursorPos < maxlength) {
|
|
||||||
internalValue.value =
|
|
||||||
currentValue.substring(0, cursorPos) +
|
|
||||||
newValue +
|
|
||||||
currentValue.substring(cursorPos + 1);
|
|
||||||
}
|
|
||||||
nextTick(() => {
|
|
||||||
input.setSelectionRange(cursorPos + 1, cursorPos + 1);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
function accountShortToStandard() {
|
function accountShortToStandard() {
|
||||||
internalValue.value = internalValue.value?.replace(
|
internalValue.value = internalValue.value.replace(
|
||||||
'.',
|
'.',
|
||||||
'0'.repeat(11 - internalValue.value.length)
|
'0'.repeat(11 - internalValue.value.length)
|
||||||
);
|
);
|
||||||
|
@ -79,5 +37,5 @@ function accountShortToStandard() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QInput @keydown="handleKeydown" ref="vnInputRef" v-model="internalValue" />
|
<q-input v-model="internalValue" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import VnSelect from './VnSelect.vue';
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
selectProps: { type: Object, required: true },
|
|
||||||
promise: { type: Function, default: () => {} },
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<QBtnDropdown v-bind="$attrs" color="primary">
|
|
||||||
<VnSelect
|
|
||||||
v-bind="selectProps"
|
|
||||||
hide-selected
|
|
||||||
hide-dropdown-icon
|
|
||||||
focus-on-mount
|
|
||||||
@update:model-value="promise"
|
|
||||||
/>
|
|
||||||
</QBtnDropdown>
|
|
||||||
</template>
|
|
|
@ -1,136 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import VnRow from '../ui/VnRow.vue';
|
|
||||||
import VnInput from './VnInput.vue';
|
|
||||||
import FetchData from '../FetchData.vue';
|
|
||||||
import useNotify from 'src/composables/useNotify';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
submitFn: { type: Function, default: () => {} },
|
|
||||||
askOldPass: { type: Boolean, default: false },
|
|
||||||
});
|
|
||||||
const emit = defineEmits(['onSubmit']);
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { notify } = useNotify();
|
|
||||||
|
|
||||||
const form = ref();
|
|
||||||
const changePassDialog = ref();
|
|
||||||
const passwords = ref({ newPassword: null, repeatPassword: null });
|
|
||||||
const requirements = ref([]);
|
|
||||||
const isLoading = ref(false);
|
|
||||||
|
|
||||||
const validate = async () => {
|
|
||||||
const { newPassword, repeatPassword, oldPassword } = passwords.value;
|
|
||||||
|
|
||||||
if (!newPassword) {
|
|
||||||
notify(t('You must enter a new password'), 'negative');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (newPassword !== repeatPassword) {
|
|
||||||
notify(t("Passwords don't match"), 'negative');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
isLoading.value = true;
|
|
||||||
await props.submitFn(newPassword, oldPassword);
|
|
||||||
emit('onSubmit');
|
|
||||||
} catch (e) {
|
|
||||||
notify('errors.writeRequest', 'negative');
|
|
||||||
} finally {
|
|
||||||
changePassDialog.value.hide();
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
defineExpose({ show: () => changePassDialog.value.show() });
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<FetchData
|
|
||||||
url="UserPasswords/findOne"
|
|
||||||
auto-load
|
|
||||||
@on-fetch="(data) => (requirements = data)"
|
|
||||||
/>
|
|
||||||
<QDialog ref="changePassDialog">
|
|
||||||
<QCard style="width: 350px">
|
|
||||||
<QCardSection>
|
|
||||||
<slot name="header">
|
|
||||||
<VnRow class="items-center" style="flex-direction: row">
|
|
||||||
<span class="text-h6" v-text="t('globals.changePass')" />
|
|
||||||
<QIcon
|
|
||||||
class="cursor-pointer"
|
|
||||||
name="close"
|
|
||||||
size="xs"
|
|
||||||
style="flex: 0"
|
|
||||||
v-close-popup
|
|
||||||
/>
|
|
||||||
</VnRow>
|
|
||||||
</slot>
|
|
||||||
</QCardSection>
|
|
||||||
<QForm ref="form">
|
|
||||||
<QCardSection>
|
|
||||||
<VnInput
|
|
||||||
v-if="props.askOldPass"
|
|
||||||
:label="t('Old password')"
|
|
||||||
v-model="passwords.oldPassword"
|
|
||||||
type="password"
|
|
||||||
:required="true"
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
<VnInput
|
|
||||||
:label="t('New password')"
|
|
||||||
v-model="passwords.newPassword"
|
|
||||||
type="password"
|
|
||||||
:required="true"
|
|
||||||
:info="
|
|
||||||
t('passwordRequirements', {
|
|
||||||
length: requirements.length,
|
|
||||||
nAlpha: requirements.nAlpha,
|
|
||||||
nUpper: requirements.nUpper,
|
|
||||||
nDigits: requirements.nDigits,
|
|
||||||
nPunct: requirements.nPunct,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
|
|
||||||
<VnInput
|
|
||||||
:label="t('Repeat password')"
|
|
||||||
v-model="passwords.repeatPassword"
|
|
||||||
type="password"
|
|
||||||
/>
|
|
||||||
</QCardSection>
|
|
||||||
</QForm>
|
|
||||||
<QCardActions>
|
|
||||||
<slot name="actions">
|
|
||||||
<QBtn
|
|
||||||
:disabled="isLoading"
|
|
||||||
:loading="isLoading"
|
|
||||||
:label="t('globals.cancel')"
|
|
||||||
class="q-ml-sm"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
type="reset"
|
|
||||||
v-close-popup
|
|
||||||
/>
|
|
||||||
<QBtn
|
|
||||||
:disabled="isLoading"
|
|
||||||
:loading="isLoading"
|
|
||||||
:label="t('globals.confirm')"
|
|
||||||
color="primary"
|
|
||||||
@click="validate"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</QCardActions>
|
|
||||||
</QCard>
|
|
||||||
</QDialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<i18n>
|
|
||||||
es:
|
|
||||||
New password: Nueva contraseña
|
|
||||||
Repeat password: Repetir contraseña
|
|
||||||
You must enter a new password: Debes introducir la nueva contraseña
|
|
||||||
Passwords don't match: Las contraseñas no coinciden
|
|
||||||
</i18n>
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed, defineModel } from 'vue';
|
||||||
|
|
||||||
const model = defineModel(undefined, { required: true });
|
const model = defineModel(undefined, { required: true });
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -17,15 +17,17 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mixed;
|
||||||
const componentArray = computed(() => {
|
const componentArray = computed(() => {
|
||||||
if (typeof $props.prop === 'object') return [$props.prop];
|
if (typeof $props.prop === 'object') return [$props.prop];
|
||||||
return $props.prop;
|
return $props.prop;
|
||||||
});
|
});
|
||||||
|
|
||||||
function mix(toComponent) {
|
function mix(toComponent) {
|
||||||
|
if (mixed) return mixed;
|
||||||
const { component, attrs, event } = toComponent;
|
const { component, attrs, event } = toComponent;
|
||||||
const customComponent = $props.components[component];
|
const customComponent = $props.components[component];
|
||||||
return {
|
mixed = {
|
||||||
component: customComponent?.component ?? component,
|
component: customComponent?.component ?? component,
|
||||||
attrs: {
|
attrs: {
|
||||||
...toValueAttrs(attrs),
|
...toValueAttrs(attrs),
|
||||||
|
@ -35,6 +37,7 @@ function mix(toComponent) {
|
||||||
},
|
},
|
||||||
event: { ...customComponent?.event, ...event },
|
event: { ...customComponent?.event, ...event },
|
||||||
};
|
};
|
||||||
|
return mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toValueAttrs(attrs) {
|
function toValueAttrs(attrs) {
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<script setup>
|
|
||||||
const model = defineModel({ type: [String, Number], required: true });
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<QDate v-model="model" :today-btn="true" :options="$attrs.options" />
|
|
||||||
</template>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.q-date {
|
|
||||||
width: 245px;
|
|
||||||
min-width: unset;
|
|
||||||
|
|
||||||
:deep(.q-date__calendar) {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
:deep(.q-date__view) {
|
|
||||||
min-height: 245px;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
:deep(.q-date__calendar-days-container) {
|
|
||||||
min-height: 160px;
|
|
||||||
height: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.q-date__header) {
|
|
||||||
padding: 2px 2px 5px 12px;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -31,10 +31,6 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const warehouses = ref();
|
const warehouses = ref();
|
||||||
|
@ -47,8 +43,7 @@ const dms = ref({});
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
defaultData();
|
defaultData();
|
||||||
if (!$props.formInitialData)
|
if (!$props.formInitialData)
|
||||||
dms.value.description =
|
dms.value.description = t($props.model + 'Description', dms.value);
|
||||||
$props.description ?? t($props.model + 'Description', dms.value);
|
|
||||||
});
|
});
|
||||||
function onFileChange(files) {
|
function onFileChange(files) {
|
||||||
dms.value.hasFileAttached = !!files;
|
dms.value.hasFileAttached = !!files;
|
||||||
|
@ -59,6 +54,7 @@ function mapperDms(data) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
const { files } = data;
|
const { files } = data;
|
||||||
if (files) formData.append(files?.name, files);
|
if (files) formData.append(files?.name, files);
|
||||||
|
delete data.files;
|
||||||
|
|
||||||
const dms = {
|
const dms = {
|
||||||
hasFile: !!data.hasFile,
|
hasFile: !!data.hasFile,
|
||||||
|
@ -82,7 +78,6 @@ async function save() {
|
||||||
const body = mapperDms(dms.value);
|
const body = mapperDms(dms.value);
|
||||||
const response = await axios.post(getUrl(), body[0], body[1]);
|
const response = await axios.post(getUrl(), body[0], body[1]);
|
||||||
emit('onDataSaved', body[1].params, response);
|
emit('onDataSaved', body[1].params, response);
|
||||||
delete dms.value.files;
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,14 +158,13 @@ function addDefaultData(data) {
|
||||||
/>
|
/>
|
||||||
<QFile
|
<QFile
|
||||||
ref="inputFileRef"
|
ref="inputFileRef"
|
||||||
:label="t('globals.file')"
|
:label="t('entry.buys.file')"
|
||||||
v-model="dms.files"
|
v-model="dms.files"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:accept="allowedContentTypes"
|
:accept="allowedContentTypes"
|
||||||
@update:model-value="onFileChange(dms.files)"
|
@update:model-value="onFileChange(dms.files)"
|
||||||
class="required"
|
class="required"
|
||||||
:display-value="dms.file"
|
:display-value="dms.file"
|
||||||
data-cy="VnDms_inputFile"
|
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
|
|
|
@ -5,14 +5,12 @@ import { useRoute } from 'vue-router';
|
||||||
import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
|
import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import VnUserLink from '../ui/VnUserLink.vue';
|
|
||||||
import { downloadFile } from 'src/composables/downloadFile';
|
|
||||||
import VnImg from 'components/ui/VnImg.vue';
|
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import VnDms from 'src/components/common/VnDms.vue';
|
import VnDms from 'src/components/common/VnDms.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import VnUserLink from '../ui/VnUserLink.vue';
|
||||||
|
import { downloadFile } from 'src/composables/downloadFile';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
@ -20,7 +18,6 @@ const { t } = useI18n();
|
||||||
const rows = ref();
|
const rows = ref();
|
||||||
const dmsRef = ref();
|
const dmsRef = ref();
|
||||||
const formDialog = ref({});
|
const formDialog = ref({});
|
||||||
const token = useSession().getTokenMultimedia();
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
model: {
|
model: {
|
||||||
|
@ -92,23 +89,6 @@ const dmsFilter = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
|
||||||
label: '',
|
|
||||||
name: 'file',
|
|
||||||
align: 'left',
|
|
||||||
component: VnImg,
|
|
||||||
props: (prop) => {
|
|
||||||
return {
|
|
||||||
storage: 'dms',
|
|
||||||
collection: null,
|
|
||||||
resolution: null,
|
|
||||||
id: prop.row.file.split('.')[0],
|
|
||||||
token: token,
|
|
||||||
class: 'rounded',
|
|
||||||
ratio: 1,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
field: 'id',
|
field: 'id',
|
||||||
|
@ -162,6 +142,13 @@ const columns = computed(() => [
|
||||||
'model-value': Boolean(prop.value),
|
'model-value': Boolean(prop.value),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'file',
|
||||||
|
label: t('globals.file'),
|
||||||
|
name: 'file',
|
||||||
|
component: 'span',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
field: 'worker',
|
field: 'worker',
|
||||||
|
@ -400,14 +387,7 @@ defineExpose({
|
||||||
/>
|
/>
|
||||||
</QDialog>
|
</QDialog>
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
<QBtn
|
<QBtn fab color="primary" icon="add" @click="showFormDialog()" class="fill-icon">
|
||||||
fab
|
|
||||||
color="primary"
|
|
||||||
icon="add"
|
|
||||||
shortcut="+"
|
|
||||||
@click="showFormDialog()"
|
|
||||||
class="fill-icon"
|
|
||||||
>
|
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('Upload file') }}
|
{{ t('Upload file') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
|
@ -415,6 +395,10 @@ defineExpose({
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.q-gutter-y-ms {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 20px;
|
||||||
|
}
|
||||||
.labelColor {
|
.labelColor {
|
||||||
color: var(--vn-label-color);
|
color: var(--vn-label-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, useAttrs, nextTick } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRequired } from 'src/composables/useRequired';
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
|
|
||||||
const $attrs = useAttrs();
|
|
||||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
||||||
const { t } = useI18n();
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
'update:modelValue',
|
'update:modelValue',
|
||||||
'update:options',
|
'update:options',
|
||||||
|
@ -30,19 +27,11 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
emptyToNull: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
insertable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
maxlength: {
|
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
const { validations } = useValidator();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const requiredFieldRule = (val) => validations().required($attrs.required, val);
|
||||||
|
|
||||||
const vnInputRef = ref(null);
|
const vnInputRef = ref(null);
|
||||||
const value = computed({
|
const value = computed({
|
||||||
|
@ -50,7 +39,6 @@ const value = computed({
|
||||||
return $props.modelValue;
|
return $props.modelValue;
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
if ($props.emptyToNull && value === '') value = null;
|
|
||||||
emit('update:modelValue', value);
|
emit('update:modelValue', value);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -72,50 +60,18 @@ const focus = () => {
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus,
|
focus,
|
||||||
});
|
});
|
||||||
|
import { useAttrs } from 'vue';
|
||||||
|
const $attrs = useAttrs();
|
||||||
|
|
||||||
const mixinRules = [
|
const mixinRules = [
|
||||||
requiredFieldRule,
|
requiredFieldRule,
|
||||||
...($attrs.rules ?? []),
|
...($attrs.rules ?? []),
|
||||||
(val) => {
|
(val) => {
|
||||||
const { maxlength } = vnInputRef.value;
|
const { min } = vnInputRef.value.$attrs;
|
||||||
if (maxlength && +val.length > maxlength)
|
|
||||||
return t(`maxLength`, { value: maxlength });
|
|
||||||
const { min, max } = vnInputRef.value.$attrs;
|
|
||||||
if (!min) return null;
|
if (!min) return null;
|
||||||
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
|
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
|
||||||
if (!max) return null;
|
|
||||||
if (max > 0) {
|
|
||||||
if (Math.floor(val) > max) return t('inputMax', { value: max });
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleKeydown = (e) => {
|
|
||||||
if (e.key === 'Backspace') return;
|
|
||||||
|
|
||||||
if ($props.insertable && e.key.match(/[0-9]/)) {
|
|
||||||
handleInsertMode(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInsertMode = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const input = e.target;
|
|
||||||
const cursorPos = input.selectionStart;
|
|
||||||
const { maxlength } = vnInputRef.value;
|
|
||||||
let currentValue = value.value;
|
|
||||||
if (!currentValue) currentValue = e.key;
|
|
||||||
const newValue = e.key;
|
|
||||||
if (newValue && !isNaN(newValue) && cursorPos < maxlength) {
|
|
||||||
value.value =
|
|
||||||
currentValue.substring(0, cursorPos) +
|
|
||||||
newValue +
|
|
||||||
currentValue.substring(cursorPos + 1);
|
|
||||||
}
|
|
||||||
nextTick(() => {
|
|
||||||
input.setSelectionRange(cursorPos + 1, cursorPos + 1);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -125,14 +81,12 @@ const handleInsertMode = (e) => {
|
||||||
v-model="value"
|
v-model="value"
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
:type="$attrs.type"
|
:type="$attrs.type"
|
||||||
:class="{ required: isRequired }"
|
:class="{ required: $attrs.required }"
|
||||||
@keyup.enter="emit('keyup.enter')"
|
@keyup.enter="emit('keyup.enter')"
|
||||||
@keydown="handleKeydown"
|
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:rules="mixinRules"
|
:rules="mixinRules"
|
||||||
:lazy-rules="true"
|
:lazy-rules="true"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
:data-cy="$attrs.dataCy ?? $attrs.label + '_input'"
|
|
||||||
>
|
>
|
||||||
<template v-if="$slots.prepend" #prepend>
|
<template v-if="$slots.prepend" #prepend>
|
||||||
<slot name="prepend" />
|
<slot name="prepend" />
|
||||||
|
@ -141,17 +95,10 @@ const handleInsertMode = (e) => {
|
||||||
<QIcon
|
<QIcon
|
||||||
name="close"
|
name="close"
|
||||||
size="xs"
|
size="xs"
|
||||||
v-if="
|
v-if="hover && value && !$attrs.disabled && $props.clearable"
|
||||||
hover &&
|
|
||||||
value &&
|
|
||||||
!$attrs.disabled &&
|
|
||||||
!$attrs.readonly &&
|
|
||||||
$props.clearable
|
|
||||||
"
|
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
value = null;
|
value = null;
|
||||||
vnInputRef.focus();
|
|
||||||
emit('remove');
|
emit('remove');
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
@ -169,12 +116,8 @@ const handleInsertMode = (e) => {
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
inputMin: Must be more than {value}
|
inputMin: Must be more than {value}
|
||||||
maxLength: The value exceeds {value} characters
|
|
||||||
inputMax: Must be less than {value}
|
|
||||||
es:
|
es:
|
||||||
inputMin: Debe ser mayor a {value}
|
inputMin: Debe ser mayor a {value}
|
||||||
maxLength: El valor excede los {value} carácteres
|
|
||||||
inputMax: Debe ser menor a {value}
|
|
||||||
</i18n>
|
</i18n>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.q-field__append {
|
.q-field__append {
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, watch, computed, ref, useAttrs } from 'vue';
|
import { onMounted, watch, computed, ref } from 'vue';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import VnDate from './VnDate.vue';
|
|
||||||
import { useRequired } from 'src/composables/useRequired';
|
|
||||||
|
|
||||||
const $attrs = useAttrs();
|
|
||||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
||||||
const model = defineModel({ type: [String, Date] });
|
const model = defineModel({ type: [String, Date] });
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
isOutlined: {
|
isOutlined: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -21,14 +15,28 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const vnInputDateRef = ref(null);
|
const { t } = useI18n();
|
||||||
|
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
||||||
|
|
||||||
const dateFormat = 'DD/MM/YYYY';
|
const dateFormat = 'DD/MM/YYYY';
|
||||||
const isPopupOpen = ref();
|
const isPopupOpen = ref();
|
||||||
const hover = ref();
|
const hover = ref();
|
||||||
const mask = ref();
|
const mask = ref();
|
||||||
|
|
||||||
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
|
onMounted(() => {
|
||||||
|
// fix quasar bug
|
||||||
|
mask.value = '##/##/####';
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleAttrs = computed(() => {
|
||||||
|
return $props.isOutlined
|
||||||
|
? {
|
||||||
|
dense: true,
|
||||||
|
outlined: true,
|
||||||
|
rounded: true,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
});
|
||||||
|
|
||||||
const formattedDate = computed({
|
const formattedDate = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -40,12 +48,15 @@ const formattedDate = computed({
|
||||||
let newDate;
|
let newDate;
|
||||||
if (value) {
|
if (value) {
|
||||||
// parse input
|
// parse input
|
||||||
if (value.includes('/') && value.length >= 10) {
|
if (value.includes('/')) {
|
||||||
if (value.at(2) == '/') value = value.split('/').reverse().join('/');
|
if (value.length == 6) value = value + new Date().getFullYear();
|
||||||
value = date.formatDate(
|
if (value.length >= 10) {
|
||||||
new Date(value).toISOString(),
|
if (value.at(2) == '/') value = value.split('/').reverse().join('/');
|
||||||
'YYYY-MM-DDTHH:mm:ss.SSSZ'
|
value = date.formatDate(
|
||||||
);
|
new Date(value).toISOString(),
|
||||||
|
'YYYY-MM-DDTHH:mm:ss.SSSZ'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const [year, month, day] = value.split('-').map((e) => parseInt(e));
|
const [year, month, day] = value.split('-').map((e) => parseInt(e));
|
||||||
newDate = new Date(year, month - 1, day);
|
newDate = new Date(year, month - 1, day);
|
||||||
|
@ -68,46 +79,26 @@ const formattedDate = computed({
|
||||||
const popupDate = computed(() =>
|
const popupDate = computed(() =>
|
||||||
model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value
|
model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value
|
||||||
);
|
);
|
||||||
onMounted(() => {
|
|
||||||
// fix quasar bug
|
|
||||||
mask.value = '##/##/####';
|
|
||||||
});
|
|
||||||
watch(
|
watch(
|
||||||
() => model.value,
|
() => model.value,
|
||||||
(val) => (formattedDate.value = val),
|
(val) => (formattedDate.value = val),
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const styleAttrs = computed(() => {
|
|
||||||
return $props.isOutlined
|
|
||||||
? {
|
|
||||||
dense: true,
|
|
||||||
outlined: true,
|
|
||||||
rounded: true,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
});
|
|
||||||
|
|
||||||
const manageDate = (date) => {
|
|
||||||
formattedDate.value = date;
|
|
||||||
isPopupOpen.value = false;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div @mouseover="hover = true" @mouseleave="hover = false">
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||||
<QInput
|
<QInput
|
||||||
ref="vnInputDateRef"
|
|
||||||
v-model="formattedDate"
|
v-model="formattedDate"
|
||||||
class="vn-input-date"
|
class="vn-input-date"
|
||||||
:mask="mask"
|
:mask="mask"
|
||||||
placeholder="dd/mm/aaaa"
|
placeholder="dd/mm/aaaa"
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
:class="{ required: isRequired }"
|
:class="{ required: $attrs.required }"
|
||||||
:rules="mixinRules"
|
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
@click="isPopupOpen = true"
|
@click="isPopupOpen = true"
|
||||||
hide-bottom-space
|
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
|
@ -120,7 +111,6 @@ const manageDate = (date) => {
|
||||||
!$attrs.disable
|
!$attrs.disable
|
||||||
"
|
"
|
||||||
@click="
|
@click="
|
||||||
vnInputDateRef.focus();
|
|
||||||
model = null;
|
model = null;
|
||||||
isPopupOpen = false;
|
isPopupOpen = false;
|
||||||
"
|
"
|
||||||
|
@ -134,7 +124,6 @@ const manageDate = (date) => {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<QMenu
|
<QMenu
|
||||||
v-if="$q.screen.gt.xs"
|
|
||||||
transition-show="scale"
|
transition-show="scale"
|
||||||
transition-hide="scale"
|
transition-hide="scale"
|
||||||
v-model="isPopupOpen"
|
v-model="isPopupOpen"
|
||||||
|
@ -143,11 +132,19 @@ const manageDate = (date) => {
|
||||||
:no-focus="true"
|
:no-focus="true"
|
||||||
:no-parent-event="true"
|
:no-parent-event="true"
|
||||||
>
|
>
|
||||||
<VnDate v-model="popupDate" @update:model-value="manageDate" />
|
<QDate
|
||||||
|
v-model="popupDate"
|
||||||
|
:landscape="true"
|
||||||
|
:today-btn="true"
|
||||||
|
:options="$attrs.options"
|
||||||
|
@update:model-value="
|
||||||
|
(date) => {
|
||||||
|
formattedDate = date;
|
||||||
|
isPopupOpen = false;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
</QMenu>
|
</QMenu>
|
||||||
<QDialog v-else v-model="isPopupOpen">
|
|
||||||
<VnDate v-model="popupDate" @update:model-value="manageDate" />
|
|
||||||
</QDialog>
|
|
||||||
</QInput>
|
</QInput>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useAttrs } from 'vue';
|
|
||||||
|
|
||||||
const model = defineModel({ type: [Number, String] });
|
const model = defineModel({ type: [Number, String] });
|
||||||
const $attrs = useAttrs();
|
|
||||||
const step = ref($attrs.step || 0.01);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnInput v-bind="$attrs" v-model.number="model" type="number" :step="step" />
|
<VnInput v-bind="$attrs" v-model.number="model" type="number" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, useAttrs } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import VnTime from './VnTime.vue';
|
|
||||||
import { useRequired } from 'src/composables/useRequired';
|
|
||||||
|
|
||||||
const $attrs = useAttrs();
|
|
||||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
||||||
const { t } = useI18n();
|
|
||||||
const model = defineModel({ type: String });
|
const model = defineModel({ type: String });
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
timeOnly: {
|
timeOnly: {
|
||||||
|
@ -19,9 +14,10 @@ const props = defineProps({
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const vnInputTimeRef = ref(null);
|
|
||||||
const initialDate = ref(model.value ?? Date.vnNew());
|
const initialDate = ref(model.value ?? Date.vnNew());
|
||||||
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
|
const { t } = useI18n();
|
||||||
|
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
||||||
|
|
||||||
const dateFormat = 'HH:mm';
|
const dateFormat = 'HH:mm';
|
||||||
const isPopupOpen = ref();
|
const isPopupOpen = ref();
|
||||||
const hover = ref();
|
const hover = ref();
|
||||||
|
@ -71,18 +67,16 @@ function dateToTime(newDate) {
|
||||||
<template>
|
<template>
|
||||||
<div @mouseover="hover = true" @mouseleave="hover = false">
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||||
<QInput
|
<QInput
|
||||||
ref="vnInputTimeRef"
|
|
||||||
class="vn-input-time"
|
class="vn-input-time"
|
||||||
mask="##:##"
|
mask="##:##"
|
||||||
placeholder="--:--"
|
placeholder="--:--"
|
||||||
v-model="formattedTime"
|
v-model="formattedTime"
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
:class="{ required: isRequired }"
|
:class="{ required: $attrs.required }"
|
||||||
style="min-width: 100px"
|
style="min-width: 100px"
|
||||||
:rules="mixinRules"
|
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||||
@click="isPopupOpen = false"
|
@click="isPopupOpen = false"
|
||||||
type="time"
|
type="time"
|
||||||
hide-bottom-space
|
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
|
@ -95,7 +89,6 @@ function dateToTime(newDate) {
|
||||||
!$attrs.disable
|
!$attrs.disable
|
||||||
"
|
"
|
||||||
@click="
|
@click="
|
||||||
vnInputTimeRef.focus();
|
|
||||||
model = null;
|
model = null;
|
||||||
isPopupOpen = false;
|
isPopupOpen = false;
|
||||||
"
|
"
|
||||||
|
@ -108,7 +101,6 @@ function dateToTime(newDate) {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<QMenu
|
<QMenu
|
||||||
v-if="$q.screen.gt.xs"
|
|
||||||
transition-show="scale"
|
transition-show="scale"
|
||||||
transition-hide="scale"
|
transition-hide="scale"
|
||||||
v-model="isPopupOpen"
|
v-model="isPopupOpen"
|
||||||
|
@ -117,11 +109,8 @@ function dateToTime(newDate) {
|
||||||
:no-focus="true"
|
:no-focus="true"
|
||||||
:no-parent-event="true"
|
:no-parent-event="true"
|
||||||
>
|
>
|
||||||
<VnTime v-model="formattedTime" />
|
<QTime v-model="formattedTime" mask="HH:mm" landscape now-btn />
|
||||||
</QMenu>
|
</QMenu>
|
||||||
<QDialog v-else v-model="isPopupOpen">
|
|
||||||
<VnTime v-model="formattedTime" />
|
|
||||||
</QDialog>
|
|
||||||
</QInput>
|
</QInput>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -2,94 +2,32 @@
|
||||||
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
import VnSelectDialog from 'components/common/VnSelectDialog.vue';
|
import VnSelectDialog from 'components/common/VnSelectDialog.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useAttrs } from 'vue';
|
|
||||||
import { useRequired } from 'src/composables/useRequired';
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const emit = defineEmits(['update:model-value', 'update:options']);
|
const value = defineModel({ type: [String, Number, Object] });
|
||||||
const $attrs = useAttrs();
|
|
||||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
||||||
const props = defineProps({
|
|
||||||
location: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const mixinRules = [requiredFieldRule];
|
|
||||||
const locationProperties = [
|
|
||||||
'postcode',
|
|
||||||
(obj) =>
|
|
||||||
obj.city
|
|
||||||
? `${obj.city}${obj.province?.name ? `(${obj.province.name})` : ''}`
|
|
||||||
: null,
|
|
||||||
(obj) => obj.country?.name,
|
|
||||||
];
|
|
||||||
|
|
||||||
const formatLocation = (obj, properties) => {
|
|
||||||
const parts = properties.map((prop) => {
|
|
||||||
if (typeof prop === 'string') {
|
|
||||||
return obj[prop];
|
|
||||||
} else if (typeof prop === 'function') {
|
|
||||||
return prop(obj);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const filteredParts = parts.filter(
|
|
||||||
(part) => part !== null && part !== undefined && part !== ''
|
|
||||||
);
|
|
||||||
|
|
||||||
return filteredParts.join(', ');
|
|
||||||
};
|
|
||||||
|
|
||||||
const modelValue = ref(
|
|
||||||
props.location ? formatLocation(props.location, locationProperties) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
function showLabel(data) {
|
function showLabel(data) {
|
||||||
const dataProperties = [
|
return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
|
||||||
'code',
|
|
||||||
(obj) => (obj.town ? `${obj.town}(${obj.province})` : null),
|
|
||||||
'country',
|
|
||||||
];
|
|
||||||
return formatLocation(data, dataProperties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleModelValue = (data) => {
|
|
||||||
emit('update:model-value', data);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VnSelectDialog
|
<VnSelectDialog
|
||||||
v-model="modelValue"
|
v-model="value"
|
||||||
|
option-value="code"
|
||||||
option-filter-value="search"
|
option-filter-value="search"
|
||||||
:option-label="
|
:option-label="(opt) => showLabel(opt)"
|
||||||
(opt) => (typeof modelValue === 'string' ? modelValue : showLabel(opt))
|
|
||||||
"
|
|
||||||
url="Postcodes/filter"
|
url="Postcodes/filter"
|
||||||
@update:model-value="handleModelValue"
|
|
||||||
:use-like="false"
|
:use-like="false"
|
||||||
:label="t('Location')"
|
:label="t('Location')"
|
||||||
:placeholder="t('search_by_postalcode')"
|
:placeholder="t('search_by_postalcode')"
|
||||||
:input-debounce="300"
|
:input-debounce="300"
|
||||||
:class="{ required: isRequired }"
|
:class="{ required: $attrs.required }"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
clearable
|
clearable
|
||||||
:emit-value="false"
|
:emit-value="false"
|
||||||
:tooltip="t('Create new location')"
|
|
||||||
:rules="mixinRules"
|
|
||||||
:lazy-rules="true"
|
|
||||||
>
|
>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewPostcode
|
<CreateNewPostcode @on-data-saved="(newValue) => (value = newValue)" />
|
||||||
@on-data-saved="
|
|
||||||
(newValue) => {
|
|
||||||
modelValue = newValue;
|
|
||||||
emit('update:model-value', newValue);
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #option="{ itemProps, opt }">
|
<template #option="{ itemProps, opt }">
|
||||||
<QItem v-bind="itemProps">
|
<QItem v-bind="itemProps">
|
||||||
|
@ -112,9 +50,7 @@ const handleModelValue = (data) => {
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
search_by_postalcode: Search by postalcode, town, province or country
|
search_by_postalcode: Search by postalcode, town, province or country
|
||||||
Create new location: Create new location
|
|
||||||
es:
|
es:
|
||||||
Location: Ubicación
|
Location: Ubicación
|
||||||
Create new location: Crear nueva ubicación
|
|
||||||
search_by_postalcode: Buscar por código postal, ciudad o país
|
search_by_postalcode: Buscar por código postal, ciudad o país
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -9,6 +9,10 @@ const $props = defineProps({
|
||||||
type: Number, //Progress value (1.0 > x > 0.0)
|
type: Number, //Progress value (1.0 > x > 0.0)
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
showDialog: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
cancelled: {
|
cancelled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -20,22 +24,30 @@ const emit = defineEmits(['cancel', 'close']);
|
||||||
|
|
||||||
const dialogRef = ref(null);
|
const dialogRef = ref(null);
|
||||||
|
|
||||||
const showDialog = defineModel('showDialog', {
|
const _showDialog = computed({
|
||||||
type: Boolean,
|
get: () => $props.showDialog,
|
||||||
default: false,
|
set: (value) => {
|
||||||
|
if (value) dialogRef.value.show();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _progress = computed(() => $props.progress);
|
const _progress = computed(() => $props.progress);
|
||||||
|
|
||||||
const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`);
|
const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`);
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
dialogRef.value.hide();
|
||||||
|
emit('cancel');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QDialog ref="dialogRef" v-model="showDialog" @hide="emit('close')">
|
<QDialog ref="dialogRef" v-model="_showDialog" @hide="onDialogHide">
|
||||||
<QCard class="full-width dialog">
|
<QCard class="full-width dialog">
|
||||||
<QCardSection class="row">
|
<QCardSection class="row">
|
||||||
<span class="text-h6">{{ t('Progress') }}</span>
|
<span class="text-h6">{{ t('Progress') }}</span>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<QBtn icon="close" flat round dense v-close-popup />
|
<QBtn icon="close" flat round dense @click="emit('close')" />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection>
|
<QCardSection>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
@ -68,7 +80,7 @@ const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`);
|
||||||
type="button"
|
type="button"
|
||||||
flat
|
flat
|
||||||
class="text-primary"
|
class="text-primary"
|
||||||
v-close-popup
|
@click="cancel()"
|
||||||
>
|
>
|
||||||
{{ t('globals.cancel') }}
|
{{ t('globals.cancel') }}
|
||||||
</QBtn>
|
</QBtn>
|
||||||
|
|
|
@ -2,12 +2,5 @@
|
||||||
const model = defineModel({ type: Boolean, required: true });
|
const model = defineModel({ type: Boolean, required: true });
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QRadio
|
<QRadio v-model="model" v-bind="$attrs" dense :dark="true" class="q-mr-sm" />
|
||||||
v-model="model"
|
|
||||||
v-bind="$attrs"
|
|
||||||
dense
|
|
||||||
:dark="true"
|
|
||||||
class="q-mr-sm"
|
|
||||||
size="xs"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { useQuasar } from 'quasar';
|
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -11,9 +10,7 @@ const $props = defineProps({
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
onMounted(
|
onMounted(() => (stateStore.leftDrawer = $props.leftDrawer));
|
||||||
() => (stateStore.leftDrawer = useQuasar().screen.gt.xs ? $props.leftDrawer : false)
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue';
|
import { ref, toRefs, computed, watch, onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useArrayData } from 'src/composables/useArrayData';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import { useRequired } from 'src/composables/useRequired';
|
|
||||||
import dataByOrder from 'src/utils/dataByOrder';
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
|
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
|
||||||
const $attrs = useAttrs();
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [String, Number, Object],
|
type: [String, Number, Object],
|
||||||
|
@ -42,10 +37,6 @@ const $props = defineProps({
|
||||||
type: [Array],
|
type: [Array],
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
exprBuilder: {
|
|
||||||
type: Function,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
isClearable: {
|
isClearable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -90,26 +81,24 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
dataKey: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
|
const { t } = useI18n();
|
||||||
|
const requiredFieldRule = (val) => val ?? t('globals.fieldRequired');
|
||||||
|
|
||||||
const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } =
|
const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } =
|
||||||
toRefs($props);
|
toRefs($props);
|
||||||
const myOptions = ref([]);
|
const myOptions = ref([]);
|
||||||
const myOptionsOriginal = ref([]);
|
const myOptionsOriginal = ref([]);
|
||||||
const vnSelectRef = ref();
|
const vnSelectRef = ref();
|
||||||
|
const dataRef = ref();
|
||||||
const lastVal = ref();
|
const lastVal = ref();
|
||||||
const noOneText = t('globals.noOne');
|
const noOneText = t('globals.noOne');
|
||||||
const noOneOpt = ref({
|
const noOneOpt = ref({
|
||||||
[optionValue.value]: false,
|
[optionValue.value]: false,
|
||||||
[optionLabel.value]: noOneText,
|
[optionLabel.value]: noOneText,
|
||||||
});
|
});
|
||||||
const isLoading = ref(false);
|
|
||||||
const useURL = computed(() => $props.url);
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get() {
|
get() {
|
||||||
return $props.modelValue;
|
return $props.modelValue;
|
||||||
|
@ -125,7 +114,7 @@ watch(options, (newValue) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(modelValue, async (newValue) => {
|
watch(modelValue, async (newValue) => {
|
||||||
if (!myOptions?.value?.some((option) => option[optionValue.value] == newValue))
|
if (!myOptions.value.some((option) => option[optionValue.value] == newValue))
|
||||||
await fetchFilter(newValue);
|
await fetchFilter(newValue);
|
||||||
|
|
||||||
if ($props.noOne) myOptions.value.unshift(noOneOpt.value);
|
if ($props.noOne) myOptions.value.unshift(noOneOpt.value);
|
||||||
|
@ -133,26 +122,19 @@ watch(modelValue, async (newValue) => {
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setOptions(options.value);
|
setOptions(options.value);
|
||||||
if (useURL.value && $props.modelValue && !findKeyInOptions())
|
if ($props.url && $props.modelValue && !findKeyInOptions())
|
||||||
fetchFilter($props.modelValue);
|
fetchFilter($props.modelValue);
|
||||||
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
|
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
const arrayDataKey =
|
|
||||||
$props.dataKey ?? ($props.url?.length > 0 ? $props.url : $attrs.name ?? $attrs.label);
|
|
||||||
|
|
||||||
const arrayData = useArrayData(arrayDataKey, { url: $props.url, searchUrl: false });
|
|
||||||
|
|
||||||
function findKeyInOptions() {
|
function findKeyInOptions() {
|
||||||
if (!$props.options) return;
|
if (!$props.options) return;
|
||||||
return filter($props.modelValue, $props.options)?.length;
|
return filter($props.modelValue, $props.options)?.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOptions(data) {
|
function setOptions(data) {
|
||||||
data = dataByOrder(data, $props.sortBy);
|
|
||||||
myOptions.value = JSON.parse(JSON.stringify(data));
|
myOptions.value = JSON.parse(JSON.stringify(data));
|
||||||
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
||||||
emit('update:options', data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function filter(val, options) {
|
function filter(val, options) {
|
||||||
|
@ -177,7 +159,7 @@ function filter(val, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFilter(val) {
|
async function fetchFilter(val) {
|
||||||
if (!$props.url) return;
|
if (!$props.url || !dataRef.value) return;
|
||||||
|
|
||||||
const { fields, include, sortBy, limit } = $props;
|
const { fields, include, sortBy, limit } = $props;
|
||||||
const key =
|
const key =
|
||||||
|
@ -195,15 +177,11 @@ async function fetchFilter(val) {
|
||||||
}, {});
|
}, {});
|
||||||
} else defaultWhere = { [key]: getVal(val) };
|
} else defaultWhere = { [key]: getVal(val) };
|
||||||
const where = { ...(val ? defaultWhere : {}), ...$props.where };
|
const where = { ...(val ? defaultWhere : {}), ...$props.where };
|
||||||
$props.exprBuilder && Object.assign(where, $props.exprBuilder(key, val));
|
|
||||||
const fetchOptions = { where, include, limit };
|
const fetchOptions = { where, include, limit };
|
||||||
if (fields) fetchOptions.fields = fields;
|
if (fields) fetchOptions.fields = fields;
|
||||||
if (sortBy) fetchOptions.order = sortBy;
|
if (sortBy) fetchOptions.order = sortBy;
|
||||||
arrayData.reset(['skip', 'filter.skip', 'page']);
|
|
||||||
|
|
||||||
const { data } = await arrayData.applyFilter({ filter: fetchOptions });
|
return dataRef.value.fetch(fetchOptions);
|
||||||
setOptions(data);
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function filterHandler(val, update) {
|
async function filterHandler(val, update) {
|
||||||
|
@ -242,48 +220,19 @@ function nullishToTrue(value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
|
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
|
||||||
|
|
||||||
async function onScroll({ to, direction, from, index }) {
|
|
||||||
const lastIndex = myOptions.value.length - 1;
|
|
||||||
|
|
||||||
if (from === 0 && index === 0) return;
|
|
||||||
if (!useURL.value && !$props.fetchRef) return;
|
|
||||||
if (direction === 'decrease') return;
|
|
||||||
if (to === lastIndex && arrayData.store.hasMoreData && !isLoading.value) {
|
|
||||||
isLoading.value = true;
|
|
||||||
await arrayData.loadMore();
|
|
||||||
setOptions(arrayData.store.data);
|
|
||||||
vnSelectRef.value.scrollTo(lastIndex);
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ opts: myOptions });
|
|
||||||
|
|
||||||
function handleKeyDown(event) {
|
|
||||||
if (event.key === 'Tab') {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const inputValue = vnSelectRef.value?.inputValue;
|
|
||||||
|
|
||||||
if (inputValue) {
|
|
||||||
const matchingOption = myOptions.value.find(
|
|
||||||
(option) =>
|
|
||||||
option[optionLabel.value].toLowerCase() === inputValue.toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matchingOption) {
|
|
||||||
emit('update:modelValue', matchingOption[optionValue.value]);
|
|
||||||
} else {
|
|
||||||
emit('update:modelValue', inputValue);
|
|
||||||
}
|
|
||||||
vnSelectRef.value?.hidePopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="dataRef"
|
||||||
|
:url="$props.url"
|
||||||
|
@on-fetch="(data) => setOptions(data)"
|
||||||
|
:where="where || { [optionValue]: value }"
|
||||||
|
:limit="limit"
|
||||||
|
:sort-by="sortBy"
|
||||||
|
:fields="fields"
|
||||||
|
:params="params"
|
||||||
|
/>
|
||||||
<QSelect
|
<QSelect
|
||||||
v-model="value"
|
v-model="value"
|
||||||
:options="myOptions"
|
:options="myOptions"
|
||||||
|
@ -291,7 +240,6 @@ function handleKeyDown(event) {
|
||||||
:option-value="optionValue"
|
:option-value="optionValue"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@filter="filterHandler"
|
@filter="filterHandler"
|
||||||
@keydown="handleKeyDown"
|
|
||||||
:emit-value="nullishToTrue($attrs['emit-value'])"
|
:emit-value="nullishToTrue($attrs['emit-value'])"
|
||||||
:map-options="nullishToTrue($attrs['map-options'])"
|
:map-options="nullishToTrue($attrs['map-options'])"
|
||||||
:use-input="nullishToTrue($attrs['use-input'])"
|
:use-input="nullishToTrue($attrs['use-input'])"
|
||||||
|
@ -299,18 +247,13 @@ function handleKeyDown(event) {
|
||||||
:fill-input="nullishToTrue($attrs['fill-input'])"
|
:fill-input="nullishToTrue($attrs['fill-input'])"
|
||||||
ref="vnSelectRef"
|
ref="vnSelectRef"
|
||||||
lazy-rules
|
lazy-rules
|
||||||
:class="{ required: isRequired }"
|
:class="{ required: $attrs.required }"
|
||||||
:rules="mixinRules"
|
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||||
virtual-scroll-slice-size="options.length"
|
virtual-scroll-slice-size="options.length"
|
||||||
hide-bottom-space
|
|
||||||
:input-debounce="useURL ? '300' : '0'"
|
|
||||||
:loading="isLoading"
|
|
||||||
@virtual-scroll="onScroll"
|
|
||||||
:data-cy="$attrs.dataCy ?? $attrs.label + '_select'"
|
|
||||||
>
|
>
|
||||||
<template #append>
|
<template v-if="isClearable" #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
v-show="isClearable && value"
|
v-show="value"
|
||||||
name="close"
|
name="close"
|
||||||
@click.stop="
|
@click.stop="
|
||||||
() => {
|
() => {
|
||||||
|
@ -323,22 +266,7 @@ function handleKeyDown(event) {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
||||||
<div v-if="slotName == 'append'">
|
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
|
||||||
<QIcon
|
|
||||||
v-show="isClearable && value"
|
|
||||||
name="close"
|
|
||||||
@click.stop="
|
|
||||||
() => {
|
|
||||||
value = null;
|
|
||||||
emit('remove');
|
|
||||||
}
|
|
||||||
"
|
|
||||||
class="cursor-pointer"
|
|
||||||
size="xs"
|
|
||||||
/>
|
|
||||||
<slot name="append" v-if="$slots.append" v-bind="slotData ?? {}" />
|
|
||||||
</div>
|
|
||||||
<slot v-else :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
|
|
||||||
</template>
|
</template>
|
||||||
</QSelect>
|
</QSelect>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRole } from 'src/composables/useRole';
|
import { useRole } from 'src/composables/useRole';
|
||||||
import { useAcl } from 'src/composables/useAcl';
|
|
||||||
|
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
@ -12,10 +11,6 @@ const $props = defineProps({
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => ['developer'],
|
default: () => ['developer'],
|
||||||
},
|
},
|
||||||
acls: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
actionIcon: {
|
actionIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'add',
|
default: 'add',
|
||||||
|
@ -27,12 +22,15 @@ const $props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const role = useRole();
|
const role = useRole();
|
||||||
const acl = useAcl();
|
const showForm = ref(false);
|
||||||
|
|
||||||
const isAllowedToCreate = computed(() => {
|
const isAllowedToCreate = computed(() => {
|
||||||
if ($props.acls.length) return acl.hasAny($props.acls);
|
|
||||||
return role.hasAny($props.rolesAllowedToCreate);
|
return role.hasAny($props.rolesAllowedToCreate);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const toggleForm = () => {
|
||||||
|
showForm.value = !showForm.value;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -43,7 +41,7 @@ const isAllowedToCreate = computed(() => {
|
||||||
>
|
>
|
||||||
<template v-if="isAllowedToCreate" #append>
|
<template v-if="isAllowedToCreate" #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
@click.stop.prevent="$refs.dialog.show()"
|
@click.stop.prevent="toggleForm()"
|
||||||
:name="actionIcon"
|
:name="actionIcon"
|
||||||
:size="actionIcon === 'add' ? 'xs' : 'sm'"
|
:size="actionIcon === 'add' ? 'xs' : 'sm'"
|
||||||
:class="['default-icon', { '--add-icon': actionIcon === 'add' }]"
|
:class="['default-icon', { '--add-icon': actionIcon === 'add' }]"
|
||||||
|
@ -53,7 +51,7 @@ const isAllowedToCreate = computed(() => {
|
||||||
>
|
>
|
||||||
<QTooltip v-if="tooltip">{{ tooltip }}</QTooltip>
|
<QTooltip v-if="tooltip">{{ tooltip }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<QDialog ref="dialog" transition-show="scale" transition-hide="scale">
|
<QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
|
||||||
<slot name="form" />
|
<slot name="form" />
|
||||||
</QDialog>
|
</QDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { onBeforeMount, ref, useAttrs } from 'vue';
|
|
||||||
import axios from 'axios';
|
|
||||||
import VnSelect from 'components/common/VnSelect.vue';
|
|
||||||
|
|
||||||
const { schema, table, column, translation, defaultOptions } = defineProps({
|
|
||||||
schema: {
|
|
||||||
type: String,
|
|
||||||
default: 'vn',
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
column: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
translation: {
|
|
||||||
type: Function,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
defaultOptions: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const $attrs = useAttrs();
|
|
||||||
const options = ref([]);
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
options.value = [].concat(defaultOptions);
|
|
||||||
const { data } = await axios.get(`Applications/get-enum-values`, {
|
|
||||||
params: { schema, table, column },
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const value of data)
|
|
||||||
options.value.push({
|
|
||||||
[$attrs['option-value'] ?? 'id']: value,
|
|
||||||
[$attrs['option-label'] ?? 'name']: translation ? translation(value) : value,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<VnSelect
|
|
||||||
v-bind="$attrs"
|
|
||||||
:options="options"
|
|
||||||
:key="options.length"
|
|
||||||
:input-debounce="0"
|
|
||||||
/>
|
|
||||||
</template>
|
|
|
@ -86,7 +86,7 @@ async function send() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QDialog ref="dialogRef" data-cy="vnSmsDialog">
|
<QDialog ref="dialogRef">
|
||||||
<QCard class="q-pa-sm">
|
<QCard class="q-pa-sm">
|
||||||
<QCardSection class="row items-center q-pb-none">
|
<QCardSection class="row items-center q-pb-none">
|
||||||
<span class="text-h6 text-grey">
|
<span class="text-h6 text-grey">
|
||||||
|
@ -161,7 +161,6 @@ async function send() {
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
color="primary"
|
color="primary"
|
||||||
unelevated
|
unelevated
|
||||||
data-cy="sendSmsBtn"
|
|
||||||
/>
|
/>
|
||||||
</QCardActions>
|
</QCardActions>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<script setup>
|
|
||||||
const model = defineModel({ type: [String, Number], required: true });
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<QTime v-model="model" now-btn mask="HH:mm" />
|
|
||||||
</template>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.q-time {
|
|
||||||
width: 230px;
|
|
||||||
min-width: unset;
|
|
||||||
:deep(.q-time__header) {
|
|
||||||
min-height: unset;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -8,7 +8,7 @@ defineProps({
|
||||||
<template>
|
<template>
|
||||||
<div :class="$q.screen.gt.md ? 'q-pb-lg' : 'q-pb-md'">
|
<div :class="$q.screen.gt.md ? 'q-pb-lg' : 'q-pb-md'">
|
||||||
<div class="header-link" :style="{ cursor: url ? 'pointer' : 'default' }">
|
<div class="header-link" :style="{ cursor: url ? 'pointer' : 'default' }">
|
||||||
<a :href="url" :class="url ? 'link' : 'color-vn-text'" v-bind="$attrs">
|
<a :href="url" :class="url ? 'link' : 'color-vn-text'">
|
||||||
{{ text }}
|
{{ text }}
|
||||||
<QIcon v-if="url" :name="icon" />
|
<QIcon v-if="url" :name="icon" />
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -47,7 +47,6 @@ let store;
|
||||||
let entity;
|
let entity;
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
|
const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
|
||||||
const menuRef = ref();
|
|
||||||
defineExpose({ getData });
|
defineExpose({ getData });
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
|
@ -167,12 +166,11 @@ const toModule = computed(() =>
|
||||||
icon="more_vert"
|
icon="more_vert"
|
||||||
round
|
round
|
||||||
size="md"
|
size="md"
|
||||||
data-cy="descriptor-more-opts"
|
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('components.cardDescriptor.moreOptions') }}
|
{{ t('components.cardDescriptor.moreOptions') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
<QMenu :ref="menuRef">
|
<QMenu ref="menuRef">
|
||||||
<QList>
|
<QList>
|
||||||
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
|
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
|
||||||
</QList>
|
</QList>
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { useRoute } from 'vue-router';
|
||||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import { useArrayData } from 'src/composables/useArrayData';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
import { isDialogOpened } from 'src/filters';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
|
@ -59,6 +58,22 @@ async function fetch() {
|
||||||
emit('onFetch', Array.isArray(data) ? data[0] : data);
|
emit('onFetch', Array.isArray(data) ? data[0] : data);
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showRedirectToSummaryIcon = computed(() => {
|
||||||
|
const exist = existSummary(route.matched);
|
||||||
|
return !isSummary.value && route.meta.moduleName && exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
function existSummary(routes) {
|
||||||
|
const hasSummary = routes.some((r) => r.name === `${route.meta.moduleName}Summary`);
|
||||||
|
if (hasSummary) return hasSummary;
|
||||||
|
for (const current of routes) {
|
||||||
|
if (current.path != '/' && current.children) {
|
||||||
|
const exist = existSummary(current.children);
|
||||||
|
if (exist) return exist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -69,7 +84,7 @@ async function fetch() {
|
||||||
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
||||||
<slot name="header-left">
|
<slot name="header-left">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="isDialogOpened()"
|
v-if="showRedirectToSummaryIcon"
|
||||||
class="header link"
|
class="header link"
|
||||||
:to="{
|
:to="{
|
||||||
name: `${moduleName ?? route.meta.moduleName}Summary`,
|
name: `${moduleName ?? route.meta.moduleName}Summary`,
|
||||||
|
@ -83,7 +98,7 @@ async function fetch() {
|
||||||
<slot name="header" :entity="entity" dense>
|
<slot name="header" :entity="entity" dense>
|
||||||
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
||||||
</slot>
|
</slot>
|
||||||
<slot name="header-right" :entity="entity">
|
<slot name="header-right">
|
||||||
<span></span>
|
<span></span>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,7 +118,6 @@ async function fetch() {
|
||||||
|
|
||||||
.cardSummary {
|
.cardSummary {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 70vh;
|
|
||||||
.summaryHeader {
|
.summaryHeader {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
|
@ -31,7 +31,7 @@ const dialog = ref(null);
|
||||||
<div class="container order-catalog-item overflow-hidden">
|
<div class="container order-catalog-item overflow-hidden">
|
||||||
<QCard class="card shadow-6">
|
<QCard class="card shadow-6">
|
||||||
<div class="img-wrapper">
|
<div class="img-wrapper">
|
||||||
<VnImg :id="item.id" class="image" zoom-resolution="1600x900" />
|
<VnImg :id="item.id" class="image" />
|
||||||
<div v-if="item.hex && isCatalog" class="item-color-container">
|
<div v-if="item.hex && isCatalog" class="item-color-container">
|
||||||
<div
|
<div
|
||||||
class="item-color"
|
class="item-color"
|
||||||
|
@ -127,6 +127,11 @@ const dialog = ref(null);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
||||||
|
.subName {
|
||||||
|
color: var(--vn-label-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,9 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]);
|
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||||
defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
|
|
||||||
|
|
||||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||||
useDialogPluginComponent();
|
|
||||||
|
|
||||||
const title = props.title || t('Confirm');
|
const title = props.title || t('Confirm');
|
||||||
const message =
|
const message =
|
||||||
|
@ -54,14 +52,9 @@ async function confirm() {
|
||||||
}
|
}
|
||||||
onDialogOK(props.data);
|
onDialogOK(props.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
|
||||||
emit('cancel');
|
|
||||||
onDialogCancel();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QDialog ref="dialogRef" @hide="onDialogHide">
|
<QDialog ref="dialogRef">
|
||||||
<QCard class="q-pa-sm">
|
<QCard class="q-pa-sm">
|
||||||
<QCardSection class="row items-center q-pb-none">
|
<QCardSection class="row items-center q-pb-none">
|
||||||
<QAvatar
|
<QAvatar
|
||||||
|
@ -73,19 +66,10 @@ function cancel() {
|
||||||
/>
|
/>
|
||||||
<span class="text-h6">{{ title }}</span>
|
<span class="text-h6">{{ title }}</span>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<QBtn
|
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||||
icon="close"
|
|
||||||
:disable="isLoading"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
dense
|
|
||||||
@click="cancel()"
|
|
||||||
/>
|
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pb-none">
|
<QCardSection class="row items-center">
|
||||||
<span v-if="message !== false" v-html="message" />
|
<span v-if="message !== false" v-html="message" />
|
||||||
</QCardSection>
|
|
||||||
<QCardSection class="row items-center q-pt-none">
|
|
||||||
<slot name="customHTML"></slot>
|
<slot name="customHTML"></slot>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardActions align="right">
|
<QCardActions align="right">
|
||||||
|
@ -94,7 +78,7 @@ function cancel() {
|
||||||
color="primary"
|
color="primary"
|
||||||
:disable="isLoading"
|
:disable="isLoading"
|
||||||
flat
|
flat
|
||||||
@click="cancel()"
|
v-close-popup
|
||||||
/>
|
/>
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('globals.confirm')"
|
:label="t('globals.confirm')"
|
||||||
|
@ -103,7 +87,6 @@ function cancel() {
|
||||||
@click="confirm()"
|
@click="confirm()"
|
||||||
unelevated
|
unelevated
|
||||||
autofocus
|
autofocus
|
||||||
data-cy="VnConfirm_confirm"
|
|
||||||
/>
|
/>
|
||||||
</QCardActions>
|
</QCardActions>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
|
@ -37,7 +37,7 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
hiddenTags: {
|
hiddenTags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => ['filter', 'or', 'and'],
|
default: () => ['filter', 'search', 'or', 'and'],
|
||||||
},
|
},
|
||||||
customTags: {
|
customTags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -49,7 +49,7 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
searchUrl: {
|
searchUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'table',
|
default: 'params',
|
||||||
},
|
},
|
||||||
redirect: {
|
redirect: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -57,10 +57,12 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineExpose({ search, sanitizer });
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
'update:modelValue',
|
'update:modelValue',
|
||||||
'refresh',
|
'refresh',
|
||||||
'clear',
|
'clear',
|
||||||
|
'search',
|
||||||
'init',
|
'init',
|
||||||
'remove',
|
'remove',
|
||||||
'setUserParams',
|
'setUserParams',
|
||||||
|
@ -74,9 +76,6 @@ const arrayData = useArrayData($props.dataKey, {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const store = arrayData.store;
|
const store = arrayData.store;
|
||||||
const userParams = ref({});
|
const userParams = ref({});
|
||||||
|
|
||||||
defineExpose({ search, sanitizer, params: userParams });
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
userParams.value = $props.modelValue ?? {};
|
userParams.value = $props.modelValue ?? {};
|
||||||
emit('init', { params: userParams.value });
|
emit('init', { params: userParams.value });
|
||||||
|
@ -171,36 +170,16 @@ const tagsList = computed(() => {
|
||||||
return tagList;
|
return tagList;
|
||||||
});
|
});
|
||||||
|
|
||||||
const formatTags = (tags) => {
|
|
||||||
const formattedTags = [];
|
|
||||||
tags.forEach((tag) => {
|
|
||||||
if (tag.label === 'and') {
|
|
||||||
tag.value.forEach((item) => {
|
|
||||||
for (const key in item) {
|
|
||||||
formattedTags.push({ label: key, value: item[key] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
formattedTags.push(tag);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return formattedTags;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tags = computed(() => {
|
const tags = computed(() => {
|
||||||
const filteredTags = tagsList.value.filter(
|
return tagsList.value.filter((tag) => !($props.customTags || []).includes(tag.label));
|
||||||
(tag) => !($props.customTags || []).includes(tag.label)
|
|
||||||
);
|
|
||||||
return formatTags(filteredTags);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const customTags = computed(() =>
|
const customTags = computed(() =>
|
||||||
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label))
|
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label))
|
||||||
);
|
);
|
||||||
|
|
||||||
async function remove(key) {
|
async function remove(key) {
|
||||||
userParams.value[key] = undefined;
|
userParams.value[key] = undefined;
|
||||||
await search();
|
search();
|
||||||
emit('remove', key);
|
emit('remove', key);
|
||||||
emit('update:modelValue', userParams.value);
|
emit('update:modelValue', userParams.value);
|
||||||
}
|
}
|
||||||
|
@ -214,12 +193,7 @@ function formatValue(value) {
|
||||||
|
|
||||||
function sanitizer(params) {
|
function sanitizer(params) {
|
||||||
for (const [key, value] of Object.entries(params)) {
|
for (const [key, value] of Object.entries(params)) {
|
||||||
if (key === 'and' && Array.isArray(value)) {
|
if (value && typeof value === 'object') {
|
||||||
value.forEach((item) => {
|
|
||||||
Object.assign(params, item);
|
|
||||||
});
|
|
||||||
delete params[key];
|
|
||||||
} else if (value && typeof value === 'object') {
|
|
||||||
const param = Object.values(value)[0];
|
const param = Object.values(value)[0];
|
||||||
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
||||||
}
|
}
|
||||||
|
@ -237,7 +211,7 @@ function sanitizer(params) {
|
||||||
icon="search"
|
icon="search"
|
||||||
@click="search()"
|
@click="search()"
|
||||||
></QBtn>
|
></QBtn>
|
||||||
<QForm @submit="search" id="filterPanelForm" @keyup.enter="search()">
|
<QForm @submit="search" id="filterPanelForm">
|
||||||
<QList dense>
|
<QList dense>
|
||||||
<QItem class="q-mt-xs">
|
<QItem class="q-mt-xs">
|
||||||
<QItemSection top>
|
<QItemSection top>
|
||||||
|
@ -273,7 +247,6 @@ function sanitizer(params) {
|
||||||
:key="chip.label"
|
:key="chip.label"
|
||||||
:removable="!unremovableParams?.includes(chip.label)"
|
:removable="!unremovableParams?.includes(chip.label)"
|
||||||
@remove="remove(chip.label)"
|
@remove="remove(chip.label)"
|
||||||
data-cy="vnFilterPanelChip"
|
|
||||||
>
|
>
|
||||||
<slot name="tags" :tag="chip" :format-fn="formatValue">
|
<slot name="tags" :tag="chip" :format-fn="formatValue">
|
||||||
<div class="q-gutter-x-xs">
|
<div class="q-gutter-x-xs">
|
||||||
|
|
|
@ -39,8 +39,6 @@ const getUrl = (zoom = false) => {
|
||||||
const curResolution = zoom
|
const curResolution = zoom
|
||||||
? $props.zoomResolution || $props.resolution
|
? $props.zoomResolution || $props.resolution
|
||||||
: $props.resolution;
|
: $props.resolution;
|
||||||
if ($props.storage === 'dms')
|
|
||||||
return `/api/${$props.storage}/${$props.id}/downloadFile?access_token=${token}`;
|
|
||||||
return isEmployee
|
return isEmployee
|
||||||
? `/api/${$props.storage}/${$props.collection}/${curResolution}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
|
? `/api/${$props.storage}/${$props.collection}/${curResolution}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
|
||||||
: noImage;
|
: noImage;
|
||||||
|
@ -54,21 +52,18 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QImg
|
<QImg
|
||||||
:draggable="true"
|
|
||||||
:class="{ zoomIn: zoom }"
|
:class="{ zoomIn: zoom }"
|
||||||
:src="getUrl()"
|
:src="getUrl()"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@click.stop="show = $props.zoom"
|
@click.stop="show = $props.zoom ? true : false"
|
||||||
spinner-color="primary"
|
spinner-color="primary"
|
||||||
/>
|
/>
|
||||||
<QDialog v-if="$props.zoom" v-model="show">
|
<QDialog v-if="$props.zoom" v-model="show">
|
||||||
<QImg
|
<QImg
|
||||||
:draggable="true"
|
|
||||||
:src="getUrl(true)"
|
:src="getUrl(true)"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
spinner-color="primary"
|
spinner-color="primary"
|
||||||
class="img_zoom"
|
class="img_zoom"
|
||||||
:ratio="0"
|
|
||||||
/>
|
/>
|
||||||
</QDialog>
|
</QDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<script setup>
|
|
||||||
defineProps({ email: { type: [String], default: null } });
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<QBtn
|
|
||||||
v-if="email"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
icon="email"
|
|
||||||
size="sm"
|
|
||||||
color="primary"
|
|
||||||
padding="none"
|
|
||||||
:href="`mailto:${email}`"
|
|
||||||
@click.stop
|
|
||||||
/>
|
|
||||||
</template>
|
|
|
@ -1,50 +1,16 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, useAttrs, onBeforeMount, capitalize } from 'vue';
|
defineProps({ phoneNumber: { type: [String, Number], default: null } });
|
||||||
import axios from 'axios';
|
|
||||||
import { parsePhone } from 'src/filters';
|
|
||||||
const props = defineProps({
|
|
||||||
phoneNumber: { type: [String, Number], default: null },
|
|
||||||
channel: { type: Number, default: null },
|
|
||||||
});
|
|
||||||
|
|
||||||
const config = reactive({
|
|
||||||
sip: { icon: 'phone', href: `sip:${props.phoneNumber}` },
|
|
||||||
'say-simple': {
|
|
||||||
icon: 'vn:saysimple',
|
|
||||||
href: null,
|
|
||||||
channel: props.channel,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const type = Object.keys(config).find((key) => key in useAttrs()) || 'sip';
|
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
let { channel } = config[type];
|
|
||||||
|
|
||||||
if (type === 'say-simple') {
|
|
||||||
const { url, defaultChannel } = (await axios.get('SaySimpleConfigs/findOne'))
|
|
||||||
.data;
|
|
||||||
if (!channel) channel = defaultChannel;
|
|
||||||
|
|
||||||
config[type].href = `${url}?customerIdentity=%2B${parsePhone(
|
|
||||||
props.phoneNumber
|
|
||||||
)}&channelId=${channel}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QBtn
|
<QBtn
|
||||||
v-if="phoneNumber"
|
v-if="phoneNumber"
|
||||||
flat
|
flat
|
||||||
round
|
round
|
||||||
:icon="config[type].icon"
|
icon="phone"
|
||||||
size="sm"
|
size="sm"
|
||||||
color="primary"
|
color="primary"
|
||||||
padding="none"
|
padding="none"
|
||||||
:href="config[type].href"
|
:href="`sip:${phoneNumber}`"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
/>
|
||||||
<QTooltip>
|
|
||||||
{{ capitalize(type).replace('-', '') }}
|
|
||||||
</QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { ref, reactive } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { onBeforeRouteLeave } from 'vue-router';
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
@ -12,40 +12,36 @@ import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import VnUserLink from 'components/ui/VnUserLink.vue';
|
import VnUserLink from 'components/ui/VnUserLink.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
import VnAvatar from 'components/ui/VnAvatar.vue';
|
import VnAvatar from 'components/ui/VnAvatar.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
|
||||||
import VnSelect from 'components/common/VnSelect.vue';
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
url: { type: String, default: null },
|
url: { type: String, default: null },
|
||||||
filter: { type: Object, default: () => {} },
|
filter: { type: Object, default: () => {} },
|
||||||
body: { type: Object, default: () => {} },
|
body: { type: Object, default: () => {} },
|
||||||
addNote: { type: Boolean, default: false },
|
addNote: { type: Boolean, default: false },
|
||||||
selectType: { type: Boolean, default: false },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const currentUser = ref(state.getUser());
|
const currentUser = ref(state.getUser());
|
||||||
const newNote = reactive({ text: null, observationTypeFk: null });
|
const newNote = ref('');
|
||||||
const observationTypes = ref([]);
|
|
||||||
const vnPaginateRef = ref();
|
const vnPaginateRef = ref();
|
||||||
|
function handleKeyUp(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!event.shiftKey) insert();
|
||||||
|
}
|
||||||
|
}
|
||||||
async function insert() {
|
async function insert() {
|
||||||
if (!newNote.text || ($props.selectType && !newNote.observationTypeFk)) return;
|
|
||||||
|
|
||||||
const body = $props.body;
|
const body = $props.body;
|
||||||
const newBody = {
|
const newBody = { ...body, ...{ text: newNote.value } };
|
||||||
...body,
|
|
||||||
...{ text: newNote.text, observationTypeFk: newNote.observationTypeFk },
|
|
||||||
};
|
|
||||||
await axios.post($props.url, newBody);
|
await axios.post($props.url, newBody);
|
||||||
await vnPaginateRef.value.fetch();
|
await vnPaginateRef.value.fetch();
|
||||||
|
newNote.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (newNote.text)
|
if (newNote.value)
|
||||||
quasar.dialog({
|
quasar.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
@ -58,54 +54,37 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<QCard class="q-pa-xs q-mb-xl full-width" v-if="$props.addNote">
|
||||||
v-if="selectType"
|
|
||||||
url="ObservationTypes"
|
|
||||||
:filter="{ fields: ['id', 'description'] }"
|
|
||||||
auto-load
|
|
||||||
@on-fetch="(data) => (observationTypes = data)"
|
|
||||||
/>
|
|
||||||
<QCard class="q-pa-xs q-mb-lg full-width" v-if="$props.addNote">
|
|
||||||
<QCardSection horizontal>
|
<QCardSection horizontal>
|
||||||
{{ t('New note') }}
|
<VnAvatar :worker-id="currentUser.id" size="md" />
|
||||||
|
<div class="full-width row justify-between q-pa-xs">
|
||||||
|
<VnUserLink :name="t('New note')" :worker-id="currentUser.id" />
|
||||||
|
{{ t('globals.now') }}
|
||||||
|
</div>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-px-xs q-my-none q-py-none">
|
<QCardSection class="q-pa-xs q-my-none q-py-none" horizontal>
|
||||||
<VnRow class="full-width">
|
<QInput
|
||||||
<VnSelect
|
v-model="newNote"
|
||||||
:label="t('Observation type')"
|
class="full-width"
|
||||||
v-if="selectType"
|
type="textarea"
|
||||||
url="ObservationTypes"
|
:label="t('Add note here...')"
|
||||||
v-model="newNote.observationTypeFk"
|
filled
|
||||||
option-label="description"
|
size="lg"
|
||||||
style="flex: 0.15"
|
autogrow
|
||||||
:required="true"
|
autofocus
|
||||||
@keyup.enter.stop="insert"
|
@keyup="handleKeyUp"
|
||||||
/>
|
clearable
|
||||||
<VnInput
|
>
|
||||||
v-model.trim="newNote.text"
|
<template #append>
|
||||||
type="textarea"
|
<QBtn
|
||||||
:label="t('Add note here...')"
|
:title="t('Save (Enter)')"
|
||||||
filled
|
icon="save"
|
||||||
size="lg"
|
color="primary"
|
||||||
autogrow
|
flat
|
||||||
@keyup.enter.stop="insert"
|
@click="insert"
|
||||||
clearable
|
/>
|
||||||
:required="true"
|
</template>
|
||||||
>
|
</QInput>
|
||||||
<template #append>
|
|
||||||
<QBtn
|
|
||||||
:title="t('Save (Enter)')"
|
|
||||||
icon="save"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
@click="insert"
|
|
||||||
class="q-mb-xs"
|
|
||||||
dense
|
|
||||||
data-cy="saveNote"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</VnInput>
|
|
||||||
</VnRow>
|
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
</QCard>
|
</QCard>
|
||||||
<VnPaginate
|
<VnPaginate
|
||||||
|
@ -119,10 +98,6 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
class="show"
|
class="show"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
search-url="notes"
|
search-url="notes"
|
||||||
@on-fetch="
|
|
||||||
newNote.text = '';
|
|
||||||
newNote.observationTypeFk = null;
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<TransitionGroup name="list" tag="div" class="column items-center full-width">
|
<TransitionGroup name="list" tag="div" class="column items-center full-width">
|
||||||
|
@ -136,28 +111,13 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
:descriptor="false"
|
:descriptor="false"
|
||||||
:worker-id="note.workerFk"
|
:worker-id="note.workerFk"
|
||||||
size="md"
|
size="md"
|
||||||
:title="note.worker?.user.nickname"
|
|
||||||
/>
|
/>
|
||||||
<div class="full-width row justify-between q-pa-xs">
|
<div class="full-width row justify-between q-pa-xs">
|
||||||
<div>
|
<VnUserLink
|
||||||
<VnUserLink
|
:name="`${note.worker.user.nickname}`"
|
||||||
:name="`${note.worker.user.name}`"
|
:worker-id="note.worker.id"
|
||||||
:worker-id="note.worker.id"
|
/>
|
||||||
/>
|
{{ toDateHourMin(note.created) }}
|
||||||
<QBadge
|
|
||||||
class="q-ml-xs"
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
v-if="selectType && note.observationTypeFk"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
observationTypes.find(
|
|
||||||
(ot) => ot.id === note.observationTypeFk
|
|
||||||
)?.description
|
|
||||||
}}
|
|
||||||
</QBadge>
|
|
||||||
</div>
|
|
||||||
<span v-text="toDateHourMin(note.created)" />
|
|
||||||
</div>
|
</div>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pa-xs q-my-none q-py-none">
|
<QCardSection class="q-pa-xs q-my-none q-py-none">
|
||||||
|
@ -171,6 +131,12 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.q-card {
|
.q-card {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
|
@media (max-width: $breakpoint-sm) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&__section {
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.q-dialog .q-card {
|
.q-dialog .q-card {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
|
@ -184,28 +150,11 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vn-row > :nth-child(2) {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $breakpoint-xs) {
|
|
||||||
.vn-row > :deep(*) {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.q-card {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&__section {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Add note here...: Añadir nota aquí...
|
Add note here...: Añadir nota aquí...
|
||||||
New note: Nueva nota
|
New note: Nueva nota
|
||||||
Save (Enter): Guardar (Intro)
|
Save (Enter): Guardar (Intro)
|
||||||
Observation type: Tipo de observación
|
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -44,7 +44,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 20,
|
default: 10,
|
||||||
},
|
},
|
||||||
userParams: {
|
userParams: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -56,7 +56,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
offset: {
|
offset: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: undefined,
|
default: 0,
|
||||||
},
|
},
|
||||||
skeleton: {
|
skeleton: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -100,7 +100,7 @@ const arrayData = useArrayData(props.dataKey, {
|
||||||
const store = arrayData.store;
|
const store = arrayData.store;
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (props.autoLoad && !store.data?.length) await fetch();
|
if (props.autoLoad) await fetch();
|
||||||
mounted.value = true;
|
mounted.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -115,41 +115,25 @@ watch(
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => store.data,
|
() => store.data,
|
||||||
(data) => {
|
(data) => emit('onChange', data)
|
||||||
if (!mounted.value) return;
|
|
||||||
emit('onChange', data);
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [props.url, props.filter],
|
() => [props.url, props.filter],
|
||||||
([url, filter]) => mounted.value && fetch({ url, filter })
|
([url, filter]) => mounted.value && fetch({ url, filter })
|
||||||
);
|
);
|
||||||
|
|
||||||
const addFilter = async (filter, params) => {
|
const addFilter = async (filter, params) => {
|
||||||
await arrayData.addFilter({ filter, params });
|
await arrayData.addFilter({ filter, params });
|
||||||
};
|
};
|
||||||
|
|
||||||
async function fetch(params) {
|
async function fetch(params) {
|
||||||
useArrayData(props.dataKey, params);
|
useArrayData(props.dataKey, params);
|
||||||
arrayData.reset(['filter.skip', 'skip', 'page']);
|
arrayData.reset(['filter.skip', 'skip']);
|
||||||
await arrayData.fetch({ append: false, updateRouter: mounted.value });
|
|
||||||
return emitStoreData();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update(params) {
|
|
||||||
useArrayData(props.dataKey, params);
|
|
||||||
const { limit, skip } = store;
|
|
||||||
store.limit = limit + skip;
|
|
||||||
store.skip = 0;
|
|
||||||
await arrayData.fetch({ append: false });
|
await arrayData.fetch({ append: false });
|
||||||
store.limit = limit;
|
if (!store.hasMoreData) {
|
||||||
store.skip = skip;
|
isLoading.value = false;
|
||||||
return emitStoreData();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function emitStoreData() {
|
|
||||||
if (!store.hasMoreData) isLoading.value = false;
|
|
||||||
emit('onFetch', store.data);
|
emit('onFetch', store.data);
|
||||||
return store.data;
|
return store.data;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +179,7 @@ async function onLoad(index, done) {
|
||||||
done(isDone);
|
done(isDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ fetch, update, addFilter, paginate });
|
defineExpose({ fetch, addFilter, paginate });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -240,20 +224,13 @@ defineExpose({ fetch, update, addFilter, paginate });
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
>
|
>
|
||||||
<slot name="body" :rows="store.data"></slot>
|
<slot name="body" :rows="store.data"></slot>
|
||||||
<div v-if="isLoading" class="spinner info-row q-pa-md text-center">
|
<div v-if="isLoading" class="info-row q-pa-md text-center">
|
||||||
<QSpinner color="primary" size="md" />
|
<QSpinner color="primary" size="md" />
|
||||||
</div>
|
</div>
|
||||||
</QInfiniteScroll>
|
</QInfiniteScroll>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.spinner {
|
|
||||||
z-index: 1;
|
|
||||||
align-content: end;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.info-row {
|
.info-row {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
<script setup>
|
||||||
|
defineProps({ wrap: { type: Boolean, default: false } });
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="vn-row q-gutter-md">
|
<div class="vn-row q-gutter-md q-mb-md">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -18,9 +21,6 @@
|
||||||
&:not(.wrap) {
|
&:not(.wrap) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
&[fixed] {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -45,7 +45,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 20,
|
default: 10,
|
||||||
},
|
},
|
||||||
userParams: {
|
userParams: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -108,7 +108,6 @@ async function search() {
|
||||||
...Object.fromEntries(staticParams),
|
...Object.fromEntries(staticParams),
|
||||||
search: searchText.value,
|
search: searchText.value,
|
||||||
},
|
},
|
||||||
...{ filter: props.filter },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (props.whereFilter) {
|
if (props.whereFilter) {
|
||||||
|
@ -130,7 +129,6 @@ async function search() {
|
||||||
dense
|
dense
|
||||||
standout
|
standout
|
||||||
autofocus
|
autofocus
|
||||||
data-cy="vnSearchBar"
|
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<QIcon
|
<QIcon
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
routeName: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
entityId: {
|
|
||||||
type: [String, Number],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const id = props.entityId;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<router-link
|
|
||||||
v-if="route?.name !== routeName"
|
|
||||||
:to="{ name: routeName, params: { id: id } }"
|
|
||||||
class="header link"
|
|
||||||
:href="url"
|
|
||||||
>
|
|
||||||
<QIcon name="open_in_new" color="white" size="sm" />
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
|
@ -1,18 +1,20 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
defineProps({
|
const $props = defineProps({
|
||||||
name: { type: String, default: null },
|
name: { type: String, default: null },
|
||||||
tag: { type: String, default: null },
|
|
||||||
workerId: { type: Number, default: null },
|
workerId: { type: Number, default: null },
|
||||||
defaultName: { type: Boolean, default: false },
|
defaultName: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<slot name="link">
|
<slot name="link">
|
||||||
<span :class="{ link: workerId }">
|
<span :class="{ link: $props.workerId }">
|
||||||
{{ defaultName ? name ?? $t('globals.system') : name }}
|
{{ $props.defaultName ? $props.name ?? t('globals.system') : $props.name }}
|
||||||
</span>
|
</span>
|
||||||
</slot>
|
</slot>
|
||||||
<WorkerDescriptorProxy v-if="workerId" :id="workerId" />
|
<WorkerDescriptorProxy v-if="$props.workerId" :id="$props.workerId" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { defineProps, ref } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const props = defineProps({
|
|
||||||
usesMana: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
manaCode: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
manaVal: {
|
|
||||||
type: String,
|
|
||||||
default: 'mana',
|
|
||||||
},
|
|
||||||
manaLabel: {
|
|
||||||
type: String,
|
|
||||||
default: 'Promotion mana',
|
|
||||||
},
|
|
||||||
manaClaimVal: {
|
|
||||||
type: String,
|
|
||||||
default: 'manaClaim',
|
|
||||||
},
|
|
||||||
claimLabel: {
|
|
||||||
type: String,
|
|
||||||
default: 'Claim mana',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const manaCode = ref(props.manaCode);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="column q-gutter-y-sm q-mt-sm">
|
|
||||||
<QRadio
|
|
||||||
v-model="manaCode"
|
|
||||||
dense
|
|
||||||
:val="manaVal"
|
|
||||||
:label="t(manaLabel)"
|
|
||||||
:dark="true"
|
|
||||||
class="q-mb-sm"
|
|
||||||
/>
|
|
||||||
<QRadio
|
|
||||||
v-model="manaCode"
|
|
||||||
dense
|
|
||||||
:val="manaClaimVal"
|
|
||||||
:label="t(claimLabel)"
|
|
||||||
:dark="true"
|
|
||||||
class="q-mb-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,24 +1,11 @@
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { getUrl } from './getUrl';
|
import { getUrl } from './getUrl';
|
||||||
import axios from 'axios';
|
|
||||||
import { exportFile } from 'quasar';
|
|
||||||
|
|
||||||
const { getTokenMultimedia } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = getTokenMultimedia();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) {
|
export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) {
|
||||||
const appUrl = (await getUrl('', 'lilium')).replace('/#/', '');
|
let appUrl = await getUrl('', 'lilium');
|
||||||
const response = await axios.get(
|
appUrl = appUrl.replace('/#/', '');
|
||||||
url ?? `${appUrl}/api/${model}/${id}${urlPath}?access_token=${token}`,
|
window.open(url ?? `${appUrl}/api/${model}/${id}${urlPath}?access_token=${token}`);
|
||||||
{ responseType: 'blob' }
|
|
||||||
);
|
|
||||||
|
|
||||||
const contentDisposition = response.headers['content-disposition'];
|
|
||||||
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
|
|
||||||
const filename =
|
|
||||||
matches != null && matches[1]
|
|
||||||
? matches[1].replace(/['"]/g, '')
|
|
||||||
: 'downloaded-file';
|
|
||||||
|
|
||||||
exportFile(filename, response.data);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,13 @@ export function useAcl() {
|
||||||
state.setAcls(acls);
|
state.setAcls(acls);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasAny(acls) {
|
function hasAny(model, prop, accessType) {
|
||||||
for (const acl of acls) {
|
const acls = state.getAcls().value[model];
|
||||||
let { model, props, accessType } = acl;
|
if (acls)
|
||||||
const modelAcls = state.getAcls().value[model];
|
return ['*', prop].some((key) => {
|
||||||
Array.isArray(props) || (props = [props]);
|
const acl = acls[key];
|
||||||
if (modelAcls)
|
return acl && (acl['*'] || acl[accessType]);
|
||||||
return ['*', ...props].some((key) => {
|
});
|
||||||
const acl = modelAcls[key];
|
|
||||||
return acl && (acl['*'] || acl[accessType]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import axios from 'axios';
|
|
||||||
import { useRole } from './useRole';
|
|
||||||
|
|
||||||
export async function useAdvancedSummary(model, id, roles = ['hr']) {
|
|
||||||
if (useRole().hasAny(roles)) {
|
|
||||||
const { data } = await axios.get(`${model}/advancedSummary`, {
|
|
||||||
params: { filter: { where: { id } } },
|
|
||||||
});
|
|
||||||
return Array.isArray(data) ? data[0] : data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ import { useRouter, useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
||||||
import { buildFilter } from 'filters/filterPanel';
|
import { buildFilter } from 'filters/filterPanel';
|
||||||
import { isDialogOpened } from 'src/filters';
|
|
||||||
|
|
||||||
const arrayDataStore = useArrayDataStore();
|
const arrayDataStore = useArrayDataStore();
|
||||||
|
|
||||||
|
@ -27,8 +26,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
const params = JSON.parse(query[searchUrl]);
|
const params = JSON.parse(query[searchUrl]);
|
||||||
const filter = params?.filter && JSON.parse(params?.filter ?? '{}');
|
const filter = params?.filter && JSON.parse(params?.filter ?? '{}');
|
||||||
delete params.filter;
|
delete params.filter;
|
||||||
|
store.userParams = { ...params, ...store.userParams };
|
||||||
store.userParams = { ...store.userParams, ...params };
|
|
||||||
store.userFilter = { ...filter, ...store.userFilter };
|
store.userFilter = { ...filter, ...store.userFilter };
|
||||||
if (filter?.order) store.order = filter.order;
|
if (filter?.order) store.order = filter.order;
|
||||||
}
|
}
|
||||||
|
@ -75,10 +73,18 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
limit: store.limit,
|
limit: store.limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let exprFilter;
|
||||||
let userParams = { ...store.userParams };
|
let userParams = { ...store.userParams };
|
||||||
|
if (store?.exprBuilder) {
|
||||||
|
const where = buildFilter(userParams, (param, value) => {
|
||||||
|
const res = store.exprBuilder(param, value);
|
||||||
|
if (res) delete userParams[param];
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
exprFilter = where ? { where } : null;
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(filter, store.userFilter);
|
Object.assign(filter, store.userFilter, exprFilter);
|
||||||
|
|
||||||
let where;
|
let where;
|
||||||
if (filter?.where || store.filter?.where)
|
if (filter?.where || store.filter?.where)
|
||||||
where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
|
where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
|
||||||
|
@ -87,29 +93,12 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
const params = { filter };
|
const params = { filter };
|
||||||
|
|
||||||
Object.assign(params, userParams);
|
Object.assign(params, userParams);
|
||||||
if (params.filter) params.filter.skip = store.skip;
|
params.filter.skip = store.skip;
|
||||||
if (store?.order && typeof store?.order == 'string') store.order = [store.order];
|
if (store.order && store.order.length) params.filter.order = store.order;
|
||||||
if (store.order?.length) params.filter.order = [...store.order];
|
|
||||||
else delete params.filter.order;
|
else delete params.filter.order;
|
||||||
|
|
||||||
store.currentFilter = JSON.parse(JSON.stringify(params));
|
|
||||||
delete store.currentFilter.filter.include;
|
|
||||||
store.currentFilter.filter = JSON.stringify(store.currentFilter.filter);
|
|
||||||
|
|
||||||
let exprFilter;
|
|
||||||
if (store?.exprBuilder) {
|
|
||||||
exprFilter = buildFilter(params, (param, value) => {
|
|
||||||
if (param == 'filter') return;
|
|
||||||
const res = store.exprBuilder(param, value);
|
|
||||||
if (res) delete params[param];
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.filter.where || exprFilter)
|
|
||||||
params.filter.where = { ...params.filter.where, ...exprFilter };
|
|
||||||
params.filter = JSON.stringify(params.filter);
|
params.filter = JSON.stringify(params.filter);
|
||||||
|
store.currentFilter = params;
|
||||||
store.isLoading = true;
|
store.isLoading = true;
|
||||||
const response = await axios.get(store.url, {
|
const response = await axios.get(store.url, {
|
||||||
signal: canceller.signal,
|
signal: canceller.signal,
|
||||||
|
@ -124,7 +113,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
for (const row of response.data) store.data.push(row);
|
for (const row of response.data) store.data.push(row);
|
||||||
} else {
|
} else {
|
||||||
store.data = response.data;
|
store.data = response.data;
|
||||||
if (!isDialogOpened()) updateRouter && updateStateParams();
|
if (!document.querySelectorAll('[role="dialog"]').length)
|
||||||
|
updateRouter && updateStateParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
store.isLoading = false;
|
store.isLoading = false;
|
||||||
|
@ -256,10 +246,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStateParams() {
|
function updateStateParams() {
|
||||||
if (!route?.path) return;
|
|
||||||
const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
|
const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
|
||||||
if (store?.searchUrl)
|
newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
|
||||||
newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
|
|
||||||
|
|
||||||
if (store.navigate) {
|
if (store.navigate) {
|
||||||
const { customRouteRedirectName, searchText } = store.navigate;
|
const { customRouteRedirectName, searchText } = store.navigate;
|
||||||
|
@ -280,7 +268,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
const pushUrl = { path: to };
|
const pushUrl = { path: to };
|
||||||
if (to.endsWith('/list') || to.endsWith('/'))
|
if (to.endsWith('/list') || to.endsWith('/'))
|
||||||
pushUrl.query = newUrl.query;
|
pushUrl.query = newUrl.query;
|
||||||
else destroy();
|
destroy();
|
||||||
return router.push(pushUrl);
|
return router.push(pushUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { openURL } from 'quasar';
|
|
||||||
const defaultWindowFeatures = {
|
|
||||||
noopener: true,
|
|
||||||
noreferrer: true,
|
|
||||||
};
|
|
||||||
export default function (url, windowFeatures = defaultWindowFeatures, fn = undefined) {
|
|
||||||
openURL(url, fn, windowFeatures);
|
|
||||||
}
|
|
|
@ -1,26 +1,22 @@
|
||||||
import { useSession } from './useSession';
|
import { useSession } from './useSession';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
|
|
||||||
export function usePrintService() {
|
export function usePrintService() {
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const { getTokenMultimedia } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
|
|
||||||
function sendEmail(path, params) {
|
function sendEmail(path, params) {
|
||||||
return axios.post(path, params).then(() =>
|
return axios.post(path, params).then(() =>
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: t('globals.notificationSent'),
|
message: 'Notification sent',
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
icon: 'check',
|
icon: 'check',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openReport(path, params, isNewTab = '_self') {
|
function openReport(path, params) {
|
||||||
if (typeof params === 'string') params = JSON.parse(params);
|
|
||||||
params = Object.assign(
|
params = Object.assign(
|
||||||
{
|
{
|
||||||
access_token: getTokenMultimedia(),
|
access_token: getTokenMultimedia(),
|
||||||
|
@ -29,7 +25,8 @@ export function usePrintService() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const query = new URLSearchParams(params).toString();
|
const query = new URLSearchParams(params).toString();
|
||||||
window.open(`api/${path}?${query}`, isNewTab);
|
|
||||||
|
window.open(`api/${path}?${query}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { useValidator } from 'src/composables/useValidator';
|
|
||||||
|
|
||||||
export function useRequired($attrs) {
|
|
||||||
const { validations } = useValidator();
|
|
||||||
|
|
||||||
const isRequired = Object.keys($attrs).includes('required');
|
|
||||||
const requiredFieldRule = (val) => validations().required(isRequired, val);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isRequired,
|
|
||||||
requiredFieldRule,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -37,10 +37,6 @@ a {
|
||||||
.link {
|
.link {
|
||||||
color: $color-link;
|
color: $color-link;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&--white {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tx-color-link {
|
.tx-color-link {
|
||||||
|
@ -241,7 +237,7 @@ input::-webkit-inner-spin-button {
|
||||||
th,
|
th,
|
||||||
td {
|
td {
|
||||||
padding: 1px 10px 1px 10px;
|
padding: 1px 10px 1px 10px;
|
||||||
max-width: 130px;
|
max-width: 100px;
|
||||||
div span {
|
div span {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -268,7 +264,6 @@ input::-webkit-inner-spin-button {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-photo-btn {
|
.edit-photo-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
|
@ -276,19 +271,3 @@ input::-webkit-inner-spin-button {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subName {
|
|
||||||
color: var(--vn-label-color);
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-date {
|
|
||||||
&__today {
|
|
||||||
border: 2px solid $info;
|
|
||||||
color: $info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-visible {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ $color-font-secondary: #777;
|
||||||
.bg-success {
|
.bg-success {
|
||||||
background-color: $positive;
|
background-color: $positive;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-notice {
|
.bg-notice {
|
||||||
background-color: $info;
|
background-color: $info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
export default function getDifferences(obj1, obj2) {
|
|
||||||
let diff = {};
|
|
||||||
delete obj1.$index;
|
|
||||||
delete obj2.$index;
|
|
||||||
|
|
||||||
for (let key in obj1) {
|
|
||||||
if (obj2[key] && JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
|
|
||||||
diff[key] = obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let key in obj2) {
|
|
||||||
if (
|
|
||||||
obj1[key] === undefined ||
|
|
||||||
JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])
|
|
||||||
) {
|
|
||||||
diff[key] = obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return diff;
|
|
||||||
}
|
|
|
@ -9,20 +9,12 @@ function parseJSON(str, fallback) {
|
||||||
}
|
}
|
||||||
export default function (route, param) {
|
export default function (route, param) {
|
||||||
// catch route query params
|
// catch route query params
|
||||||
const params = parseJSON(route?.query?.table, {});
|
const params = parseJSON(route?.query?.params, {});
|
||||||
|
|
||||||
// extract and parse filter from params
|
// extract and parse filter from params
|
||||||
const { filter: filterStr = '{}' } = params;
|
const { filter: filterStr = '{}' } = params;
|
||||||
|
|
||||||
const where = parseJSON(filterStr, {})?.where;
|
const where = parseJSON(filterStr, {})?.where;
|
||||||
|
if (where && where[param] !== undefined) {
|
||||||
if (where && !param) {
|
|
||||||
return where;
|
|
||||||
} else if (where && where.and) {
|
|
||||||
const foundParam = where.and.find((p) => p[param]);
|
|
||||||
if (foundParam) {
|
|
||||||
return foundParam[param];
|
|
||||||
}
|
|
||||||
} else if (where && where[param]) {
|
|
||||||
return where[param];
|
return where[param];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
export default function getUpdatedValues(keys, formData) {
|
|
||||||
return keys.reduce((acc, key) => {
|
|
||||||
acc[key] = formData[key];
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
|
@ -11,17 +11,9 @@ import dashIfEmpty from './dashIfEmpty';
|
||||||
import dateRange from './dateRange';
|
import dateRange from './dateRange';
|
||||||
import toHour from './toHour';
|
import toHour from './toHour';
|
||||||
import dashOrCurrency from './dashOrCurrency';
|
import dashOrCurrency from './dashOrCurrency';
|
||||||
import getDifferences from './getDifferences';
|
|
||||||
import getUpdatedValues from './getUpdatedValues';
|
|
||||||
import getParamWhere from './getParamWhere';
|
import getParamWhere from './getParamWhere';
|
||||||
import parsePhone from './parsePhone';
|
|
||||||
import isDialogOpened from './isDialogOpened';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getUpdatedValues,
|
|
||||||
getDifferences,
|
|
||||||
isDialogOpened,
|
|
||||||
parsePhone,
|
|
||||||
toLowerCase,
|
toLowerCase,
|
||||||
toLowerCamel,
|
toLowerCamel,
|
||||||
toDate,
|
toDate,
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default function isDialogOpened(query = '[role="dialog"]') {
|
|
||||||
return document.querySelectorAll(query).length > 0;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
export default function (phone, prefix = 34) {
|
|
||||||
if (phone.startsWith('+')) {
|
|
||||||
return `${phone.slice(1)}`;
|
|
||||||
}
|
|
||||||
if (phone.startsWith('00')) {
|
|
||||||
return `${phone.slice(2)}`;
|
|
||||||
}
|
|
||||||
if (phone.startsWith(prefix) && phone.length === prefix.length + 9) {
|
|
||||||
return `${prefix}${phone.slice(prefix.length)}`;
|
|
||||||
}
|
|
||||||
return `${prefix}${phone}`;
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ export default function toHour(date) {
|
||||||
if (!isValidDate(date)) {
|
if (!isValidDate(date)) {
|
||||||
return '--:--';
|
return '--:--';
|
||||||
}
|
}
|
||||||
return new Date(date || '').toLocaleTimeString([], {
|
return (new Date(date || '')).toLocaleTimeString([], {
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,44 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import Navbar from 'src/components/NavBar.vue';
|
import Navbar from 'src/components/NavBar.vue';
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import routes from 'src/router/modules';
|
|
||||||
import { onMounted } from 'vue';
|
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let isNotified = false;
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const keyBindingMap = routes
|
|
||||||
.filter((route) => route.meta.keyBinding)
|
|
||||||
.reduce((map, route) => {
|
|
||||||
map['Key' + route.meta.keyBinding.toUpperCase()] = route.path;
|
|
||||||
return map;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const handleKeyDown = (event) => {
|
|
||||||
const { ctrlKey, altKey, code } = event;
|
|
||||||
|
|
||||||
if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) {
|
|
||||||
event.preventDefault();
|
|
||||||
router.push(keyBindingMap[code]);
|
|
||||||
isNotified = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyUp = (event) => {
|
|
||||||
const { ctrlKey, altKey } = event;
|
|
||||||
|
|
||||||
if (!ctrlKey || !altKey) {
|
|
||||||
isNotified = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
|
||||||
window.addEventListener('keyup', handleKeyUp);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -11,13 +11,21 @@ const { t } = useI18n();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
|
||||||
const onSynchronizeAll = async () => {
|
const onSynchronizeAll = async () => {
|
||||||
notify(t('Synchronizing in the background'), 'positive');
|
try {
|
||||||
await axios.patch(`Accounts/syncAll`);
|
notify(t('Synchronizing in the background'), 'positive');
|
||||||
|
await axios.patch(`Accounts/syncAll`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error synchronizing all accounts', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSynchronizeRoles = async () => {
|
const onSynchronizeRoles = async () => {
|
||||||
await axios.patch(`RoleInherits/sync`);
|
try {
|
||||||
notify(t('Roles synchronized!'), 'positive');
|
await axios.patch(`RoleInherits/sync`);
|
||||||
|
notify(t('Roles synchronized!'), 'positive');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error synchronizing roles', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ import { useQuasar } from 'quasar';
|
||||||
import VnTable from 'components/VnTable/VnTable.vue';
|
import VnTable from 'components/VnTable/VnTable.vue';
|
||||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
|
||||||
import { useValidator } from 'src/composables/useValidator';
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -25,18 +23,11 @@ const stateStore = useStateStore();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
const roles = ref();
|
|
||||||
const validationsStore = useValidator();
|
|
||||||
const { models } = validationsStore;
|
|
||||||
const exprBuilder = (param, value) => {
|
const exprBuilder = (param, value) => {
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 'search':
|
case 'search':
|
||||||
return {
|
return { model: { like: `%${value}%` } };
|
||||||
or: [
|
|
||||||
{ model: { like: `%${value}%` } },
|
|
||||||
{ property: { like: `%${value}%` } },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
return { [param]: value };
|
return { [param]: value };
|
||||||
}
|
}
|
||||||
|
@ -56,13 +47,6 @@ const columns = computed(() => [
|
||||||
label: t('model'),
|
label: t('model'),
|
||||||
cardVisible: true,
|
cardVisible: true,
|
||||||
create: true,
|
create: true,
|
||||||
columnCreate: {
|
|
||||||
label: t('model'),
|
|
||||||
component: 'select',
|
|
||||||
attrs: {
|
|
||||||
options: Object.keys(models),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -71,10 +55,9 @@ const columns = computed(() => [
|
||||||
cardVisible: true,
|
cardVisible: true,
|
||||||
component: 'select',
|
component: 'select',
|
||||||
attrs: {
|
attrs: {
|
||||||
options: roles,
|
url: 'VnRoles',
|
||||||
optionLabel: 'name',
|
optionLabel: 'name',
|
||||||
optionValue: 'name',
|
optionValue: 'name',
|
||||||
inputDebounce: 0,
|
|
||||||
},
|
},
|
||||||
create: true,
|
create: true,
|
||||||
},
|
},
|
||||||
|
@ -111,25 +94,29 @@ const columns = computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const deleteAcl = async ({ id }) => {
|
const deleteAcl = async ({ id }) => {
|
||||||
await new Promise((resolve) => {
|
try {
|
||||||
quasar
|
await new Promise((resolve) => {
|
||||||
.dialog({
|
quasar
|
||||||
component: VnConfirm,
|
.dialog({
|
||||||
componentProps: {
|
component: VnConfirm,
|
||||||
title: t('Remove ACL'),
|
componentProps: {
|
||||||
message: t('Do you want to remove this ACL?'),
|
title: t('Remove ACL'),
|
||||||
},
|
message: t('Do you want to remove this ACL?'),
|
||||||
})
|
},
|
||||||
.onOk(() => {
|
})
|
||||||
resolve(true);
|
.onOk(() => {
|
||||||
})
|
resolve(true);
|
||||||
.onCancel(() => {
|
})
|
||||||
resolve(false);
|
.onCancel(() => {
|
||||||
});
|
resolve(false);
|
||||||
});
|
});
|
||||||
await axios.delete(`ACLs/${id}`);
|
});
|
||||||
tableRef.value.reload();
|
await axios.delete(`ACLs/${id}`);
|
||||||
notify('ACL removed', 'positive');
|
tableRef.value.reload();
|
||||||
|
notify('ACL removed', 'positive');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting Acl: ', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -143,11 +130,6 @@ const deleteAcl = async ({ id }) => {
|
||||||
/>
|
/>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<FetchData
|
|
||||||
url="VnRoles?fields=['name']"
|
|
||||||
auto-load
|
|
||||||
@on-fetch="(data) => (roles = data)"
|
|
||||||
/>
|
|
||||||
<VnTable
|
<VnTable
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
data-key="AccountAcls"
|
data-key="AccountAcls"
|
||||||
|
|
|
@ -34,9 +34,13 @@ const refresh = () => paginateRef.value.fetch();
|
||||||
const navigate = (id) => router.push({ name: 'AccountSummary', params: { id } });
|
const navigate = (id) => router.push({ name: 'AccountSummary', params: { id } });
|
||||||
|
|
||||||
const killSession = async ({ userId, created }) => {
|
const killSession = async ({ userId, created }) => {
|
||||||
await axios.post(`${urlPath}/killSession`, { userId, created });
|
try {
|
||||||
paginateRef.value.fetch();
|
await axios.post(`${urlPath}/killSession`, { userId, created });
|
||||||
notify(t('Session killed'), 'positive');
|
paginateRef.value.fetch();
|
||||||
|
notify(t('Session killed'), 'positive');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error killing session', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ const redirectToAccountBasicData = (_, { id }) => {
|
||||||
<div class="column q-gutter-sm">
|
<div class="column q-gutter-sm">
|
||||||
<VnInput
|
<VnInput
|
||||||
v-model="data.name"
|
v-model="data.name"
|
||||||
:label="t('globals.name')"
|
:label="t('account.create.name')"
|
||||||
:rules="validate('VnUser.name')"
|
:rules="validate('VnUser.name')"
|
||||||
/>
|
/>
|
||||||
<VnInput
|
<VnInput
|
||||||
|
@ -47,12 +47,12 @@ const redirectToAccountBasicData = (_, { id }) => {
|
||||||
/>
|
/>
|
||||||
<VnInput
|
<VnInput
|
||||||
v-model="data.email"
|
v-model="data.email"
|
||||||
:label="t('globals.params.email')"
|
:label="t('account.create.email')"
|
||||||
type="email"
|
type="email"
|
||||||
:rules="validate('VnUser.email')"
|
:rules="validate('VnUser.email')"
|
||||||
/>
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('account.card.role')"
|
:label="t('account.create.role')"
|
||||||
v-model="data.roleFk"
|
v-model="data.roleFk"
|
||||||
:options="rolesOptions"
|
:options="rolesOptions"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
|
@ -63,7 +63,7 @@ const redirectToAccountBasicData = (_, { id }) => {
|
||||||
/>
|
/>
|
||||||
<VnInput
|
<VnInput
|
||||||
v-model="data.password"
|
v-model="data.password"
|
||||||
:label="t('ldap.password')"
|
:label="t('account.create.password')"
|
||||||
type="password"
|
type="password"
|
||||||
:rules="validate('VnUser.password')"
|
:rules="validate('VnUser.password')"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -33,7 +33,6 @@ const rolesOptions = ref([]);
|
||||||
:search-button="true"
|
:search-button="true"
|
||||||
:hidden-tags="['search']"
|
:hidden-tags="['search']"
|
||||||
:redirect="false"
|
:redirect="false"
|
||||||
search-url="table"
|
|
||||||
>
|
>
|
||||||
<template #tags="{ tag, formatFn }">
|
<template #tags="{ tag, formatFn }">
|
||||||
<div class="q-gutter-x-xs">
|
<div class="q-gutter-x-xs">
|
||||||
|
@ -45,7 +44,7 @@ const rolesOptions = ref([]);
|
||||||
<QItem class="q-my-sm">
|
<QItem class="q-my-sm">
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnInput
|
<VnInput
|
||||||
:label="t('globals.name')"
|
:label="t('account.card.name')"
|
||||||
v-model="params.name"
|
v-model="params.name"
|
||||||
lazy-rules
|
lazy-rules
|
||||||
is-outlined
|
is-outlined
|
||||||
|
|
|
@ -40,8 +40,12 @@ const formUrlCreate = ref(null);
|
||||||
const formUrlUpdate = ref(null);
|
const formUrlUpdate = ref(null);
|
||||||
const formCustomFn = ref(null);
|
const formCustomFn = ref(null);
|
||||||
const onTestConection = async () => {
|
const onTestConection = async () => {
|
||||||
await axios.get(`LdapConfigs/test`);
|
try {
|
||||||
notify(t('LDAP connection established!'), 'positive');
|
await axios.get(`LdapConfigs/test`);
|
||||||
|
notify(t('LDAP connection established!'), 'positive');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error testing connection', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const getInitialLdapConfig = async () => {
|
const getInitialLdapConfig = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -68,10 +72,14 @@ const getInitialLdapConfig = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const deleteMailForward = async () => {
|
const deleteMailForward = async () => {
|
||||||
await axios.delete(URL_UPDATE);
|
try {
|
||||||
initialData.value = { ...DEFAULT_DATA };
|
await axios.delete(URL_UPDATE);
|
||||||
hasData.value = false;
|
initialData.value = { ...DEFAULT_DATA };
|
||||||
notify(t('globals.dataSaved'), 'positive');
|
hasData.value = false;
|
||||||
|
notify(t('globals.dataSaved'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting mail forward', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => await getInitialLdapConfig());
|
onMounted(async () => await getInitialLdapConfig());
|
||||||
|
@ -94,11 +102,11 @@ onMounted(async () => await getInitialLdapConfig());
|
||||||
<QBtn
|
<QBtn
|
||||||
class="q-ml-none"
|
class="q-ml-none"
|
||||||
color="primary"
|
color="primary"
|
||||||
:label="t('account.card.testConnection')"
|
:label="t('ldap.testConnection')"
|
||||||
@click="onTestConection()"
|
@click="onTestConection()"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('account.card.testConnection') }}
|
{{ t('ldap.testConnection') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</template>
|
</template>
|
||||||
|
@ -106,7 +114,7 @@ onMounted(async () => await getInitialLdapConfig());
|
||||||
<VnRow class="row q-gutter-md">
|
<VnRow class="row q-gutter-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
:label="t('account.card.enableSync')"
|
:label="t('ldap.enableSync')"
|
||||||
v-model="data.hasData"
|
v-model="data.hasData"
|
||||||
@update:model-value="($event) => (hasData = $event)"
|
@update:model-value="($event) => (hasData = $event)"
|
||||||
:toggle-indeterminate="false"
|
:toggle-indeterminate="false"
|
||||||
|
@ -138,7 +146,7 @@ onMounted(async () => await getInitialLdapConfig());
|
||||||
/>
|
/>
|
||||||
<VnInput :label="t('ldap.userDN')" clearable v-model="data.userDn" />
|
<VnInput :label="t('ldap.userDN')" clearable v-model="data.userDn" />
|
||||||
<VnInput
|
<VnInput
|
||||||
:label="t('account.card.groupDN')"
|
:label="t('ldap.groupDN')"
|
||||||
clearable
|
clearable
|
||||||
v-model="data.groupDn"
|
v-model="data.groupDn"
|
||||||
/>
|
/>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue