diff --git a/package-lock.json b/package-lock.json index 5f2a2d66..05acd758 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@popperjs/core": "^2.11.5", "downshift": "^6.1.3", "dropzone": "^4.3.0", "minisearch": "^3.0.2", "prop-types": "^15.7.2", "react": "^17.0.1", - "react-datepicker": "^4.8.0", "react-dom": "^17.0.1", "react-plaid-link": "^3.2.1", + "react-popper": "^2.3.0", "react-prop-types": "^0.4.0", "react-signature-canvas": "^1.0.3", "react-signature-pad": "0.0.6", @@ -583,18 +584,6 @@ "d3-time": "1" } }, - "node_modules/date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, "node_modules/decimal.js-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", @@ -1320,23 +1309,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-datepicker": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.8.0.tgz", - "integrity": "sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==", - "dependencies": { - "@popperjs/core": "^2.9.2", - "classnames": "^2.2.6", - "date-fns": "^2.24.0", - "prop-types": "^15.7.2", - "react-onclickoutside": "^6.12.0", - "react-popper": "^2.2.5" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17 || ^18", - "react-dom": "^16.9.0 || ^17 || ^18" - } - }, "node_modules/react-dom": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", @@ -1365,19 +1337,6 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, - "node_modules/react-onclickoutside": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz", - "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==", - "funding": { - "type": "individual", - "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" - }, - "peerDependencies": { - "react": "^15.5.x || ^16.x || ^17.x || ^18.x", - "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" - } - }, "node_modules/react-plaid-link": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/react-plaid-link/-/react-plaid-link-3.2.1.tgz", @@ -2596,11 +2555,6 @@ "d3-time": "1" } }, - "date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==" - }, "decimal.js-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", @@ -3163,19 +3117,6 @@ "object-assign": "^4.1.1" } }, - "react-datepicker": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.8.0.tgz", - "integrity": "sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==", - "requires": { - "@popperjs/core": "^2.9.2", - "classnames": "^2.2.6", - "date-fns": "^2.24.0", - "prop-types": "^15.7.2", - "react-onclickoutside": "^6.12.0", - "react-popper": "^2.2.5" - } - }, "react-dom": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", @@ -3201,12 +3142,6 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, - "react-onclickoutside": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz", - "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==", - "requires": {} - }, "react-plaid-link": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/react-plaid-link/-/react-plaid-link-3.2.1.tgz", diff --git a/package.json b/package.json index 8ee308ff..eee4530b 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,15 @@ "test": "test" }, "dependencies": { + "@popperjs/core": "^2.11.5", "downshift": "^6.1.3", "dropzone": "^4.3.0", "minisearch": "^3.0.2", "prop-types": "^15.7.2", "react": "^17.0.1", - "react-datepicker": "^4.8.0", "react-dom": "^17.0.1", - "react-plaid-link": "^3.2.1", + "react-plaid-link": "^3.3.2", + "react-popper": "^2.3.0", "react-prop-types": "^0.4.0", "react-signature-canvas": "^1.0.3", "react-signature-pad": "0.0.6", diff --git a/project.clj b/project.clj index f757b55f..c41b2a67 100644 --- a/project.clj +++ b/project.clj @@ -19,6 +19,7 @@ [bidi "2.1.6"] [ring/ring-defaults "0.3.2" :exclusions [ring ring/ring-core]] [mount "0.1.16"] + [metosin/malli "0.8.9"] [tolitius/yang "0.1.23"] [ring "1.8.2" :exclusions [commons-codec commons-io diff --git a/resources/public/css/bulma.min.css b/resources/public/css/bulma.min.css index 38e9c2bd..59e6423c 100644 --- a/resources/public/css/bulma.min.css +++ b/resources/public/css/bulma.min.css @@ -10954,15 +10954,8 @@ span[data-tooltip].has-tooltip-primary-two { } .typeahead-menu { - position: absolute; - display: inline-block; - width: 100%; - top: 100%; - left: 0; z-index: 10000; - overflow: auto; - float: left; - min-width: 160px; + min-width: 200px; padding: 5px 0; margin: 2px 0 0; list-style: none; @@ -10981,6 +10974,12 @@ span[data-tooltip].has-tooltip-primary-two { overflow: auto; } +.typeahead input[disabeld] { + background-color: whitesmoke; + border-color: whitesmoke; + box-shadow: none; +} + .typeahead-suggestion { display: block; overflow: visible; diff --git a/resources/public/css/bulma.min.css.map b/resources/public/css/bulma.min.css.map index be37e1d0..e7de84ee 100644 --- a/resources/public/css/bulma.min.css.map +++ b/resources/public/css/bulma.min.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../sass/bulma.scss","../../bulma-0.9.0/bulma.sass","../../bulma-0.9.0/sass/utilities/animations.sass","../../bulma-0.9.0/sass/utilities/mixins.sass","../../bulma-0.9.0/sass/utilities/initial-variables.sass","../../bulma-0.9.0/sass/utilities/controls.sass","../../bulma-0.9.0/sass/base/minireset.sass","../../bulma-0.9.0/sass/base/generic.sass","../../bulma-0.9.0/sass/elements/box.sass","../../bulma-0.9.0/sass/elements/button.sass","../../bulma-0.9.0/sass/elements/container.sass","../../bulma-0.9.0/sass/elements/content.sass","../../bulma-0.9.0/sass/elements/icon.sass","../../bulma-0.9.0/sass/elements/image.sass","../../bulma-0.9.0/sass/elements/notification.sass","../../bulma-0.9.0/sass/elements/progress.sass","../../bulma-0.9.0/sass/elements/table.sass","../../bulma-0.9.0/sass/utilities/derived-variables.scss","../../bulma-0.9.0/sass/elements/tag.sass","../../bulma-0.9.0/sass/elements/title.sass","../../bulma-0.9.0/sass/elements/other.sass","../../bulma-0.9.0/sass/form/shared.sass","../../bulma-0.9.0/sass/form/input-textarea.sass","../../bulma-0.9.0/sass/form/checkbox-radio.sass","../../bulma-0.9.0/sass/form/select.sass","../../bulma-0.9.0/sass/form/file.sass","../../bulma-0.9.0/sass/form/tools.sass","../../bulma-0.9.0/sass/components/breadcrumb.sass","../../bulma-0.9.0/sass/components/card.sass","../../bulma-0.9.0/sass/components/dropdown.sass","../../bulma-0.9.0/sass/components/level.sass","../../bulma-0.9.0/sass/components/media.sass","../../bulma-0.9.0/sass/components/menu.sass","../../bulma-0.9.0/sass/components/message.sass","../../bulma-0.9.0/sass/components/modal.sass","../../bulma-0.9.0/sass/components/navbar.sass","../../bulma-0.9.0/sass/components/pagination.sass","../../bulma-0.9.0/sass/components/panel.sass","../../bulma-0.9.0/sass/components/tabs.sass","../../bulma-0.9.0/sass/grid/columns.sass","../../bulma-0.9.0/sass/grid/tiles.sass","../../bulma-0.9.0/sass/helpers/color.sass","../../bulma-0.9.0/sass/helpers/float.sass","../../bulma-0.9.0/sass/helpers/other.sass","../../bulma-0.9.0/sass/helpers/overflow.sass","../../bulma-0.9.0/sass/helpers/position.sass","../../bulma-0.9.0/sass/helpers/spacing.sass","../../bulma-0.9.0/sass/helpers/typography.sass","../../bulma-0.9.0/sass/helpers/visibility.sass","../../bulma-0.9.0/sass/layout/hero.sass","../../bulma-0.9.0/sass/layout/section.sass","../../bulma-0.9.0/sass/layout/footer.sass","../../sass/bulma-switch.sass","../../sass/tooltip/index.sass","../../sass/tooltip/_variables.sass","../../sass/tooltip/_position.sass","../../sass/tooltip/_animation.sass","../../sass/tooltip/_responsiveness.sass"],"names":[],"mappings":";AACA;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;AACA;EACE;EACA;EACA;EACA;EACA;;AC9BF;ACDA;EACE;IACE;;EACF;IACE;;;AC+JJ;AAAA;AAAA;AAAA;EANE;EACA;EACA;EACA;EACA;;;AAqBF;EAfE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMA;AAAA;EACE,eC3IY;;;ADkNhB;EAhEE;EACA;EACA;EACA;EACA,eC/He;EDgIf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE,kBCzMW;ED0MX;EACA;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EAEE;;AACF;EACE;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAiBJ;EAXE;EACA;EACA,eCjMe;EDkMf;EACA;EACA;EACA;EACA;EACA;EACA;;;AAYF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EANE,QADgB;EAEhB,MAFgB;EAGhB;EACA,OAJgB;EAKhB,KALgB;;;AE7OlB;AAAA;AAAA;AAAA;AAAA;EA3BE;EACA;EACA;EACA;EACA,eDqDO;ECpDP;EACA;EACA,WDkBO;ECjBP,QAfe;EAgBf;EACA,aAhBoB;EAiBpB,gBAfyB;EAgBzB,cAf2B;EAgB3B,eAhB2B;EAiB3B,aAlByB;EAmBzB;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAIE;;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;;;ACrCJ;AAEA;AAA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;AAAA;AAAA;EAME,OPlCW;EOmCX,aPEc;EODd,aAxC0B;;AAyC5B;EACE;EACA;;AACA;EACE;;AACJ;EACE;EACA;;AACA;EACE;;AACJ;EACE;EACA;;AACA;EACE;;AACJ;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE,kBPvDW;EDmIX,aQ3I6B;EAiE7B,SAhEyB;;AAiE3B;EACE;ERwEA,aQvEwB;EACxB;;AACA;EACE;;AACA;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACN;EACE;ER0DA,aQzDwB;EACxB;;AACA;EACE;EACA;;AACA;EACE;;AACN;ERkDE,aQjDwB;;AAC1B;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACJ;ER9CA;EQgDE;EACA,SAvGkB;EAwGlB;EACA;;AACF;AAAA;EAEE;;AACF;EACE;;AACA;AAAA;EAEE,QA/GsB;EAgHtB,cA/G4B;EAgH5B,SA/GuB;EAgHvB;;AACF;EACE,OPxHS;;AOyHT;EACE;;AAEF;AAAA;EAEE,cAtH+B;EAuH/B,OP/HO;;AOiIT;AAAA;EAEE,cAzH+B;EA0H/B,OPpIO;;AOwIL;AAAA;EAEE;;AAER;EACE;;AAEJ;EACE,WPhHK;;AOiHP;EACE,WPpHK;;AOqHP;EACE,WPvHK;;;AQ9BT;EACE;EACA;EACA;EACA,QATgB;EAUhB,OAVgB;;AAYhB;EACE,QAZoB;EAapB,OAboB;;AActB;EACE,QAdqB;EAerB,OAfqB;;AAgBvB;EACE,QAhBoB;EAiBpB,OAjBoB;;;ACDxB;EACE;EACA;;AACA;EACE;EACA;EACA;;AACA;EACE,eT6DW;;AS5Df;EACE;;AAkBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAGE;EACA;;AACJ;EAEE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AA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AXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAyBjB;EACE,WrBVK;;AqBWP;EACE,WrBdK;;AqBgBH;EACE;;AACN;EACE,WrBpBK;;AqBsBH;EACE;;AAGJ;EACE;EACA;;AACF;EACE;EACA;;AAEA;EACE;;AACF;EACE;;AAEJ;EACE;;AACF;EACE;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;;AACA;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE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A;EACA;EACA;;AACF;EACE;EACA;;AAEF;EnCiEA,cmChE0B;;AAC1B;EnC+DA,amC9D0B;;AAG1B;EACE;;AAEF;EACE;;AAGF;EACE;EAEE;;AAGF;EACE,kBlCtFO;EkCuFP,qBlC1FO;;AkC6FP;EACE,kBlCzFK;EkC0FL,clC/FK;EkCgGL;;AAEN;EACE;EACA;;AAEF;EACE,clCvGS;EkCwGT,cA/F0B;EAgG1B,cA/F0B;EAgG1B;EACA;;AACA;EACE,kBlC1GO;EkC2GP,clC/GO;EkCgHP;;AAEF;EnCqBF,amCpB4B;;AAC1B;EAEI,wBlC1DD;EkC2DC,2BlC3DD;;AkC+DH;EAEI,yBlCjED;EkCkEC,4BlClED;;AkCuED;EACE,kBtCnGH;EsCoGG,ctCpGH;EsCqGG,OrBtEW;EqBuEX;;AACN;EACE;;AAGE;EAEI,2BlChFK;EkCiFL,wBlCjFK;EkCkFL;;AAKJ;EAEI,4BlCzFK;EkC0FL,yBlC1FK;EkC2FL;;AAMV;EACE,WlCnIK;;AkCoIP;EACE,WlCvIK;;AkCwIP;EACE,WlC1IK;;;AmCjCT;EACE;EACA;EACA;EACA;EACA,SAPW;;AAQX;EACE;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AAEA;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;ApCkBJ;EoChBE;IACE;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EAEA;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;;ApCnCN;EoCqCE;IAEE;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EAEA;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;;ApC1GN;EoC4GE;IACE;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EAEA;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;;ApC/JN;EoCiKE;IACE;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EAEA;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;I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BJ;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAKA;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAEF;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AA5BJ;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAKA;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAEF;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AA5BJ;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAKA;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAEF;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAGJ;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AtCjCF;EACE;EACA;EACA;;;AuCHJ;EACE;;;AAEF;EACE;;;ACPF;EACE;;;AAEF;EACE;;;ACJF;EACE;;;ACEF;EACE;;;ACJF;EACE;;;AAEF;EACE;;;AAYI;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;ACxBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;A5C6EJ;E4C9EE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;A5CiFJ;E4ClFE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;A5CyFJ;E4C1FE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;A5C6FJ;E4C9FE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;AAyBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;A5CmDF;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;A5CkCJ;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;A5CkCJ;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;A5CkCJ;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;AAcN;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AACF;EACE;;;AACF;EACE;;;AACF;EACE;;;AACF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AC5FA;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAcN;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;A7CmCA;E6ChCA;IACE;;;A7CmCF;E6ChCA;IACE;;;A7CmCF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EC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;;AAyCR;EACC;;;AChJF;EAGC;EACA;EACA;;AApCD;EACC;EACA,OjDba;EiDcb;EACA,arDKe;EqDJf,WjDOO;EiDNP;EACA;EACA;EACA;EACA;EACA;EACA,SC3BgB;ED4BhB;EACA,ejD4Ba;EiD3Bb;EACA,SCjCgB;EDkChB;EACA;;AEvBD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AFxBD;EACC;EACA,OjDQa;EiDPb;EACA,arD0Be;EqDzBf,WjD4BO;EiD3BP;EACA;EACA;EACA;EACA;EACA;EACA,SCNgB;EDOhB;EACA;EACA,cClBmB;EDmBnB;EACA;;AEbA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAmBD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AASA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACC;EACA;EACA;EACA;EACA;;AASA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACC;EACA;EACA;EACA;EACA;EACA;;AF7DD;EACC;EACA,OC1CkB;ED2ClB,WC3CkB;ED4ClB;EACA;EACA;;AA2BC;EACC;;AAED;EACC;;AAED;EACC;;AAMA;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AE3FjB;EF6GE;EACA;;AG9GF;EAEC;;ArD8EA;EoDxEA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDwDA;EoD5EA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApD4DA;EoDhFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDgEA;EoDpFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDoEA;EoDxFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDoDA;EoDUA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDxBA;EoDMA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDpBA;EoDEA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDhBA;EoDFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDZA;EoDNA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApD5BA;EoD5CA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApD6BA;EoDhDA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDiCA;EoDpDA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDqCA;EoDxDA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDyCA;EoD5DA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDyBA;EoDhBA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDGA;EoDpBA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDOA;EoDxBA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDWA;EoD5BA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDeA;EoDhCA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDDA;EsD3CA;IAEC;IACA;;;AtD4CD;EsDzCA;IAEC;IACA;;;AtD0CD;EsDvCA;IAEC;IACA;;;AtDwCD;EsDrCA;IAEC;IACA;;;AtDsCD;EsDnCA;IAEC;IACA;;;AtDgBD;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtD7CF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtD7CF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AJPH;EACC;;AAIC;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;;AAIF;EACC;;;ArDtEH;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACF;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBI5Ea;EJ6Eb;EACA;EACA;EACA;EACA;AACA;;;AAEE;EACA;;;AAGJ;EACE;EACE;EACF;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;EAIE,OIvGa;EJwGb;EACA;EACA,kBAjFO;EAkFP;;;AAIF;EACI;;;AAIJ;EACI;;;AAGJ;EACI;IACI,kBA3FE;;EA8FN;IACI;;;AAKR;EACI;IACI,kBA5GC;;EA+GL;IAEI;;;AAMR;EACI;EACA;;;AAEJ;EACI;EACA;;;AAEJ;EACI;;;AAEJ;EACI","file":"bulma.min.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../sass/bulma.scss","../../bulma-0.9.0/bulma.sass","../../bulma-0.9.0/sass/utilities/animations.sass","../../bulma-0.9.0/sass/utilities/mixins.sass","../../bulma-0.9.0/sass/utilities/initial-variables.sass","../../bulma-0.9.0/sass/utilities/controls.sass","../../bulma-0.9.0/sass/base/minireset.sass","../../bulma-0.9.0/sass/base/generic.sass","../../bulma-0.9.0/sass/elements/box.sass","../../bulma-0.9.0/sass/elements/button.sass","../../bulma-0.9.0/sass/elements/container.sass","../../bulma-0.9.0/sass/elements/content.sass","../../bulma-0.9.0/sass/elements/icon.sass","../../bulma-0.9.0/sass/elements/image.sass","../../bulma-0.9.0/sass/elements/notification.sass","../../bulma-0.9.0/sass/elements/progress.sass","../../bulma-0.9.0/sass/elements/table.sass","../../bulma-0.9.0/sass/utilities/derived-variables.scss","../../bulma-0.9.0/sass/elements/tag.sass","../../bulma-0.9.0/sass/elements/title.sass","../../bulma-0.9.0/sass/elements/other.sass","../../bulma-0.9.0/sass/form/shared.sass","../../bulma-0.9.0/sass/form/input-textarea.sass","../../bulma-0.9.0/sass/form/checkbox-radio.sass","../../bulma-0.9.0/sass/form/select.sass","../../bulma-0.9.0/sass/form/file.sass","../../bulma-0.9.0/sass/form/tools.sass","../../bulma-0.9.0/sass/components/breadcrumb.sass","../../bulma-0.9.0/sass/components/card.sass","../../bulma-0.9.0/sass/components/dropdown.sass","../../bulma-0.9.0/sass/components/level.sass","../../bulma-0.9.0/sass/components/media.sass","../../bulma-0.9.0/sass/components/menu.sass","../../bulma-0.9.0/sass/components/message.sass","../../bulma-0.9.0/sass/components/modal.sass","../../bulma-0.9.0/sass/components/navbar.sass","../../bulma-0.9.0/sass/components/pagination.sass","../../bulma-0.9.0/sass/components/panel.sass","../../bulma-0.9.0/sass/components/tabs.sass","../../bulma-0.9.0/sass/grid/columns.sass","../../bulma-0.9.0/sass/grid/tiles.sass","../../bulma-0.9.0/sass/helpers/color.sass","../../bulma-0.9.0/sass/helpers/float.sass","../../bulma-0.9.0/sass/helpers/other.sass","../../bulma-0.9.0/sass/helpers/overflow.sass","../../bulma-0.9.0/sass/helpers/position.sass","../../bulma-0.9.0/sass/helpers/spacing.sass","../../bulma-0.9.0/sass/helpers/typography.sass","../../bulma-0.9.0/sass/helpers/visibility.sass","../../bulma-0.9.0/sass/layout/hero.sass","../../bulma-0.9.0/sass/layout/section.sass","../../bulma-0.9.0/sass/layout/footer.sass","../../sass/bulma-switch.sass","../../sass/tooltip/index.sass","../../sass/tooltip/_variables.sass","../../sass/tooltip/_position.sass","../../sass/tooltip/_animation.sass","../../sass/tooltip/_responsiveness.sass"],"names":[],"mappings":";AACA;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;AACA;EACE;EACA;EACA;EACA;EACA;;AC9BF;ACDA;EACE;IACE;;EACF;IACE;;;AC+JJ;AAAA;AAAA;AAAA;EANE;EACA;EACA;EACA;EACA;;;AAqBF;EAfE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMA;AAAA;EACE,eC3IY;;;ADkNhB;EAhEE;EACA;EACA;EACA;EACA,eC/He;EDgIf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE,kBCzMW;ED0MX;EACA;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EAEE;;AACF;EACE;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAiBJ;EAXE;EACA;EACA,eCjMe;EDkMf;EACA;EACA;EACA;EACA;EACA;EACA;;;AAYF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EANE,QADgB;EAEhB,MAFgB;EAGhB;EACA,OAJgB;EAKhB,KALgB;;;AE7OlB;AAAA;AAAA;AAAA;AAAA;EA3BE;EACA;EACA;EACA;EACA,eDqDO;ECpDP;EACA;EACA,WDkBO;ECjBP,QAfe;EAgBf;EACA,aAhBoB;EAiBpB,gBAfyB;EAgBzB,cAf2B;EAgB3B,eAhB2B;EAiB3B,aAlByB;EAmBzB;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAIE;;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;;;ACrCJ;AAEA;AAA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;AAAA;AAAA;EAME,OPlCW;EOmCX,aPEc;EODd,aAxC0B;;AAyC5B;EACE;EACA;;AACA;EACE;;AACJ;EACE;EACA;;AACA;EACE;;AACJ;EACE;EACA;;AACA;EACE;;AACJ;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE,kBPvDW;EDmIX,aQ3I6B;EAiE7B,SAhEyB;;AAiE3B;EACE;ERwEA,aQvEwB;EACxB;;AACA;EACE;;AACA;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACN;EACE;ER0DA,aQzDwB;EACxB;;AACA;EACE;EACA;;AACA;EACE;;AACN;ERkDE,aQjDwB;;AAC1B;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACJ;ER9CA;EQgDE;EACA,SAvGkB;EAwGlB;EACA;;AACF;AAAA;EAEE;;AACF;EACE;;AACA;AAAA;EAEE,QA/GsB;EAgHtB,cA/G4B;EAgH5B,SA/GuB;EAgHvB;;AACF;EACE,OPxHS;;AOyHT;EACE;;AAEF;AAAA;EAEE,cAtH+B;EAuH/B,OP/HO;;AOiIT;AAAA;EAEE,cAzH+B;EA0H/B,OPpIO;;AOwIL;AAAA;EAEE;;AAER;EACE;;AAEJ;EACE,WPhHK;;AOiHP;EACE,WPpHK;;AOqHP;EACE,WPvHK;;;AQ9BT;EACE;EACA;EACA;EACA,QATgB;EAUhB,OAVgB;;AAYhB;EACE,QAZoB;EAapB,OAboB;;AActB;EACE,QAdqB;EAerB,OAfqB;;AAgBvB;EACE,QAhBoB;EAiBpB,OAjBoB;;;ACDxB;EACE;EACA;;AACA;EACE;EACA;EACA;;AACA;EACE,eT6DW;;AS5Df;EACE;;AAkBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAGE;EACA;;AACJ;EAEE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AA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AXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAEb;EACE,kBAJI;EAKJ;EACA,OALW;;AAQX;EACE;EACA;EACA,OAXS;;AAcX;EACE;EACA;EACA,OAjBS;;AAoBX;EACE;EACA;EACA,OAvBS;;AAyBjB;EACE,WrBVK;;AqBWP;EACE,WrBdK;;AqBgBH;EACE;;AACN;EACE,WrBpBK;;AqBsBH;EACE;;AAGJ;EACE;EACA;;AACF;EACE;EACA;;AAEA;EACE;;AACF;EACE;;AAEJ;EACE;;AACF;EACE;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;;AACA;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE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A;EACA;EACA;;AACF;EACE;EACA;;AAEF;EnCiEA,cmChE0B;;AAC1B;EnC+DA,amC9D0B;;AAG1B;EACE;;AAEF;EACE;;AAGF;EACE;EAEE;;AAGF;EACE,kBlCtFO;EkCuFP,qBlC1FO;;AkC6FP;EACE,kBlCzFK;EkC0FL,clC/FK;EkCgGL;;AAEN;EACE;EACA;;AAEF;EACE,clCvGS;EkCwGT,cA/F0B;EAgG1B,cA/F0B;EAgG1B;EACA;;AACA;EACE,kBlC1GO;EkC2GP,clC/GO;EkCgHP;;AAEF;EnCqBF,amCpB4B;;AAC1B;EAEI,wBlC1DD;EkC2DC,2BlC3DD;;AkC+DH;EAEI,yBlCjED;EkCkEC,4BlClED;;AkCuED;EACE,kBtCnGH;EsCoGG,ctCpGH;EsCqGG,OrBtEW;EqBuEX;;AACN;EACE;;AAGE;EAEI,2BlChFK;EkCiFL,wBlCjFK;EkCkFL;;AAKJ;EAEI,4BlCzFK;EkC0FL,yBlC1FK;EkC2FL;;AAMV;EACE,WlCnIK;;AkCoIP;EACE,WlCvIK;;AkCwIP;EACE,WlC1IK;;;AmCjCT;EACE;EACA;EACA;EACA;EACA,SAPW;;AAQX;EACE;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AACF;EACE;;AAEA;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;AAJF;EACE;EACA;;AACF;EACE;;ApCkBJ;EoChBE;IACE;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EAEA;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;;ApCnCN;EoCqCE;IAEE;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;IACA;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EACF;IAEE;;EAEA;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;EANF;IAEE;IACA;;EACF;IAEE;;;ApC1GN;EoC4GE;IACE;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EAEA;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;;ApC/JN;EoCiKE;IACE;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;IACA;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EACF;IACE;;EAEA;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;IACE;IACA;;EACF;IACE;;EAJF;I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BJ;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAKA;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAEF;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AA5BJ;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAKA;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAEF;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AA5BJ;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAKA;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAEF;EACE;;;AAEA;EAEE;;;AACJ;EACE;;;AAGJ;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AAHF;EACE;;;AACF;EACE;;;AtCjCF;EACE;EACA;EACA;;;AuCHJ;EACE;;;AAEF;EACE;;;ACPF;EACE;;;AAEF;EACE;;;ACJF;EACE;;;ACEF;EACE;;;ACJF;EACE;;;AAEF;EACE;;;AAYI;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAXF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;ACxBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;A5C6EJ;E4C9EE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;A5CiFJ;E4ClFE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;A5CyFJ;E4C1FE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;A5C6FJ;E4C9FE;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;EADF;IACE;;;AAyBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;A5CmDF;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;A5CkCJ;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;A5CkCJ;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;A5CkCJ;E4C/CE;IACE;;;A5CkDJ;E4ChDE;IACE;;;A5CmDJ;E4CjDE;IACE;;;A5CoDJ;E4ClDE;IACE;;;A5CqDJ;E4CnDE;IACE;;;AAcN;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AACF;EACE;;;AACF;EACE;;;AACF;EACE;;;AACF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AC5FA;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAhBJ;EACE;;;A7C2EF;E6CzEE;IACE;;;A7C4EJ;E6C1EE;IACE;;;A7C6EJ;E6C3EE;IACE;;;A7C8EJ;E6C5EE;IACE;;;A7C+EJ;E6C7EE;IACE;;;AAcN;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;A7CmCA;E6ChCA;IACE;;;A7CmCF;E6ChCA;IACE;;;A7CmCF;E6ChCA;IACE;;;A7CmCF;E6ChCA;IACE;;;A7CmCF;E6ChCA;IACE;;;AAkBJ;EACE;;;A7CJA;E6COA;IACE;;;A7CJF;E6COA;IACE;;;A7CJF;E6COA;IACE;;;A7CJF;E6COA;IACE;;;A7CJF;E6COA;IACE;;;ACnGJ;EACE;EACA;EACA;EACA;;AACA;EACE;;AAEA;EACE;;AAKF;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAtDV;EACE,kBAHM;EAIN,OAHa;;AAIb;AAAA;EAEE;;AACF;EACE,OARW;;AASb;EACE;;AACA;AAAA;EAEE,OAbS;;A9C0EjB;E8C5DI;IAEI,kBAjBE;;;AAkBN;AAAA;EAEE;;AAGA;AAAA;AAAA;EAEE;EACA,OAzBS;;AA2BX;EACE,OA5BS;EA6BT;;AACA;EACE;;AAEF;EACE;;AAGF;EACE,OAtCO;;AAuCP;EACE;;AAEF;EAEE,kBA5CK;EA6CL,cA7CK;EA8CL,OA/CF;;AAkDJ;EAGE;;A9CUR;E8CRU;IACE;;;AAGV;EACE,SA7EoB;;A9CoFxB;E8CJI;IACE,SAhFmB;;;A9CmFzB;E8CAI;IACE,SAnFkB;;;AAuFtB;EACE;EACA;;AACA;EACE;EACA;;AACN;EACE;;AACF;EACE;;;AAIJ;EAEE;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE;;A9ClCF;E8CsBF;IAeI;;;;AAEJ;EACE;;A9CxCA;E8C2CE;IACE;;EACA;IACE;;;A9C1CN;E8CmCF;IASI;IACA;;EACA;I9CaA,c8CZ0B;;;;AAI9B;AAAA;EAEE;EACA;;;AAEF;EACE;EACA;EACA,SAhJkB;;;ACIpB;EACE,SALgB;;A/CiGhB;E+CxFE;IACE,SATmB;;EAUrB;IACE,SAVkB;;;;ACExB;EACE,kB/CSa;E+CRb,SAJe;;;ACwHjB;EACC;EACA;EACA;EACA;EACA;;AAIE;EAIC,SA/HW;;AAiId;EACC;;AACA;EACC;;AACA;EAEC;;AACD;EAEC;;AACD;EACC;;AAnIH;EACC;EACA;EACA;EACA;EACA,WhDeO;EgDdP,Q/ClBe;E+CmBf,a/ClBoB;E+CmBpB;EACA;EACA;;AAEA;EAEC;EACA;EACA;EACA;EACA,OAvBa;EAwBb,QAzBc;EA0Bd,QApCa;EAqCb,ehD+BM;EgD9BN,YhD9BY;EgD+BZ;;AAED;EAEC;EACA;EACA;EACA,MAzCoB;EA0CpB,OAlCa;EAmCb,QApCc;EAqCd;EACA,ehDkBM;EgDjBN,YhDrCY;EgDsCZ,YA9CwB;EA+CxB;;AAGD;EACC;EACA;;AACA;EAEC;EACA;;AACD;EAEC;EACA,OApDoB;;AAwDrB;EAEC,YpDjCK;;AoDkCN;EACC,MA5DoB;;AA+DpB;EAEC;EACA,OA3EkB;;AA+EpB;EAEC;EACA,chD/EW;;AgDgFZ;EAEC,YhDlFW;;AgDqFX;EAEC;EACA,cpDzDI;;AoD0DL;EAEC,YpD5DI;;AoDgEN;EAEC;EACA;;AACD;EAEC;;AAID;EAEC;;AACD;EAEC;;AAtGH;EACC;EACA;EACA;EACA;EACA,WhDgBO;EgDfP,Q/ClBe;E+CmBf,a/ClBoB;E+CmBpB;EACA;EACA;;AAEA;EAEC;EACA;EACA;EACA;EACA,OAvBa;EAwBb,QAzBc;EA0Bd,QApCa;EAqCb,ehD+BM;EgD9BN,YhD9BY;EgD+BZ;;AAED;EAEC;EACA;EACA;EACA,MAzCoB;EA0CpB,OAlCa;EAmCb,QApCc;EAqCd;EACA,ehDkBM;EgDjBN,YhDrCY;EgDsCZ,YA9CwB;EA+CxB;;AAGD;EACC;EACA;;AACA;EAEC;EACA;;AACD;EAEC;EACA,OApDoB;;AAwDrB;EAEC,YpDjCK;;AoDkCN;EACC,MA5DoB;;AA+DpB;EAEC;EACA,OA3EkB;;AA+EpB;EAEC;EACA,chD/EW;;AgDgFZ;EAEC,YhDlFW;;AgDqFX;EAEC;EACA,cpDzDI;;AoD0DL;EAEC,YpD5DI;;AoDgEN;EAEC;EACA;;AACD;EAEC;;AAID;EAEC;;AACD;EAEC;;AAtGH;EACC;EACA;EACA;EACA;EACA,WhDcO;EgDbP,Q/ClBe;E+CmBf,a/ClBoB;E+CmBpB;EACA;EACA;;AAEA;EAEC;EACA;EACA;EACA;EACA,OAvBa;EAwBb,QAzBc;EA0Bd,QApCa;EAqCb,ehD+BM;EgD9BN,YhD9BY;EgD+BZ;;AAED;EAEC;EACA;EACA;EACA,MAzCoB;EA0CpB,OAlCa;EAmCb,QApCc;EAqCd;EACA,ehDkBM;EgDjBN,YhDrCY;EgDsCZ,YA9CwB;EA+CxB;;AAGD;EACC;EACA;;AACA;EAEC;EACA;;AACD;EAEC;EACA,OApDoB;;AAwDrB;EAEC,YpDjCK;;AoDkCN;EACC,MA5DoB;;AA+DpB;EAEC;EACA,OA3EkB;;AA+EpB;EAEC;EACA,chD/EW;;AgDgFZ;EAEC,YhDlFW;;AgDqFX;EAEC;EACA,cpDzDI;;AoD0DL;EAEC,YpD5DI;;AoDgEN;EAEC;EACA;;AACD;EAEC;;AAID;EAEC;;AACD;EAEC;;AAtGH;EACC;EACA;EACA;EACA;EACA,WhDaO;EgDZP,Q/ClBe;E+CmBf,a/ClBoB;E+CmBpB;EACA;EACA;;AAEA;EAEC;EACA;EACA;EACA;EACA,OAvBa;EAwBb,QAzBc;EA0Bd,QApCa;EAqCb,ehD+BM;EgD9BN,YhD9BY;EgD+BZ;;AAED;EAEC;EACA;EACA;EACA,MAzCoB;EA0CpB,OAlCa;EAmCb,QApCc;EAqCd;EACA,ehDkBM;EgDjBN,YhDrCY;EgDsCZ,YA9CwB;EA+CxB;;AAGD;EACC;EACA;;AACA;EAEC;EACA;;AACD;EAEC;EACA,OApDoB;;AAwDrB;EAEC,YpDjCK;;AoDkCN;EACC,MA5DoB;;AA+DpB;EAEC;EACA,OA3EkB;;AA+EpB;EAEC;EACA,chD/EW;;AgDgFZ;EAEC,YhDlFW;;AgDqFX;EAEC;EACA,cpDzDI;;AoD0DL;EAEC,YpD5DI;;AoDgEN;EAEC;EACA;;AACD;EAEC;;AAID;EAEC;;AACD;EAEC;;AA6CC;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;AAKL;EAEC,YAPI;;AAWJ;EAEC;EACA;;AACD;EAEC,YAjBG;;AAqBJ;EAEC;;AAGH;EAEC,YA5BK;;AA+BL;EAEC;EACA;;AACD;EAEC,YArCI;;;AAyCR;EACC;;;AChJF;EAGC;EACA;EACA;;AApCD;EACC;EACA,OjDba;EiDcb;EACA,arDKe;EqDJf,WjDOO;EiDNP;EACA;EACA;EACA;EACA;EACA;EACA,SC3BgB;ED4BhB;EACA,ejD4Ba;EiD3Bb;EACA,SCjCgB;EDkChB;EACA;;AEvBD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AFxBD;EACC;EACA,OjDQa;EiDPb;EACA,arD0Be;EqDzBf,WjD4BO;EiD3BP;EACA;EACA;EACA;EACA;EACA;EACA,SCNgB;EDOhB;EACA;EACA,cClBmB;EDmBnB;EACA;;AEbA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAmBD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AASA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACC;EACA;EACA;EACA;EACA;;AASA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACC;EACA;EACA;EACA;EACA;EACA;;AF7DD;EACC;EACA,OC1CkB;ED2ClB,WC3CkB;ED4ClB;EACA;EACA;;AA2BC;EACC;;AAED;EACC;;AAED;EACC;;AAMA;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AAEd;EACC;;AAEA;EACC;;AAED;EACC;;AAED;EACC;;AACF;EACC;EACA,OAfa;;AE3FjB;EF6GE;EACA;;AG9GF;EAEC;;ArD8EA;EoDxEA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDwDA;EoD5EA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApD4DA;EoDhFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDgEA;EoDpFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDoEA;EoDxFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDoDA;EoDUA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDxBA;EoDMA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDpBA;EoDEA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDhBA;EoDFA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApDZA;EoDNA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;;;ApD5BA;EoD5CA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApD6BA;EoDhDA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDiCA;EoDpDA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDqCA;EoDxDA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDyCA;EoD5DA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;IACA;IACA;;;ApDyBA;EoDhBA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDGA;EoDpBA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDOA;EoDxBA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDWA;EoD5BA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDeA;EoDhCA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACC;IACA;IACA;IACA;IACA;;;ApDDA;EsD3CA;IAEC;IACA;;;AtD4CD;EsDzCA;IAEC;IACA;;;AtD0CD;EsDvCA;IAEC;IACA;;;AtDwCD;EsDrCA;IAEC;IACA;;;AtDsCD;EsDnCA;IAEC;IACA;;;AtDgBD;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtD7CF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtD7CF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AtDzBF;EsD4BC;IACC;;;AJPH;EACC;;AAIC;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;AADD;EACC;;;AAIF;EACC;;;ArDtEH;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBIrEW;EJsEX;EACA;EACA;EACA;EACA;AACA;;;AAEA;EACA;;;AAGA;EACI;EACA;EACA;;;AAGR;EACE;EACE;EACF;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;EAIE,OItGa;EJuGb;EACA;EACA,kBAhFO;EAiFP;;;AAIF;EACI;;;AAIJ;EACI;;;AAGJ;EACI;IACI,kBA1FE;;EA6FN;IACI;;;AAKR;EACI;IACI,kBA3GC;;EA8GL;IAEI;;;AAMR;EACI;EACA;;;AAEJ;EACI;EACA;;;AAEJ;EACI;;;AAEJ;EACI","file":"bulma.min.css"} \ No newline at end of file diff --git a/resources/public/css/main.css b/resources/public/css/main.css index 67cff5fa..d6d9a2c2 100644 --- a/resources/public/css/main.css +++ b/resources/public/css/main.css @@ -220,7 +220,7 @@ nav.navbar .navbar-item.is-active { margin: 0 -50px; padding-left: 50px; } -.aside .main .icon { +.aside .main .menu-item .icon { font-size: 19px; padding-right: 30px; color: #A0A0A0; @@ -462,3 +462,10 @@ table.balance-sheet th.total { .modal-card-foot { flex-wrap: wrap; } + + +.typeahead input[disabeld] { + background-color: whitesmoke !important; + border-color: whitesmoke !important; + box-shadow: none; +} diff --git a/resources/sass/bulma.scss b/resources/sass/bulma.scss index 103089a1..86f70bd2 100644 --- a/resources/sass/bulma.scss +++ b/resources/sass/bulma.scss @@ -75,32 +75,25 @@ $fullhd-enabled: false; } .typeahead-menu { - position:absolute; - display: inline-block; - width: 100%; - top: 100%; - left: 0; - z-index: 10000; - overflow: auto; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - list-style: none; - font-size: 14px; - text-align: left; - background-color: $white; - border: 1px solid #cccccc; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - /* background-clip: padding-box; */ + z-index: 10000; + min-width: 200px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + text-align: left; + background-color: $white; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + /* background-clip: padding-box; */ } .modal-card { overflow: auto; } - + .typeahead-suggestion { display: block; overflow: visible; @@ -171,3 +164,5 @@ tbody tr.live-added { .button.is-outlined { border-width: 2.5px; } + + diff --git a/src/clj/auto_ap/datomic/transactions.clj b/src/clj/auto_ap/datomic/transactions.clj index 4d2b4e35..f59ea98c 100644 --- a/src/clj/auto_ap/datomic/transactions.clj +++ b/src/clj/auto_ap/datomic/transactions.clj @@ -247,6 +247,7 @@ :transaction/bank-account [:bank-account/name :bank-account/code :bank-account/yodlee-account-id :db/id :bank-account/locations :bank-account/current-balance] :transaction/vendor [:db/id :vendor/name] :transaction/matched-rule [:db/id :transaction-rule/note] + :transaction/forecast-match [:db/id :forecasted-transaction/identifier] :transaction/accounts [:transaction-account/amount :db/id :transaction-account/location diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 35b86f76..0e12350f 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -234,7 +234,7 @@ :user {:fields {:id {:type :id} :name {:type 'String} - :role {:type 'String} + :role {:type :role} :clients {:type '(list :client)}}} :account_client_override @@ -415,7 +415,7 @@ :edit_user {:fields {:id {:type :id} :name {:type 'String} - :role {:type 'String} + :role {:type :role} :clients {:type '(list String)}}} :add_contact @@ -520,6 +520,11 @@ :applicability {:values [{:enum-value :global} {:enum-value :optional} {:enum-value :customized}]} + :role {:values [{:enum-value :none} + {:enum-value :user} + {:enum-value :manager} + {:enum-value :power_user} + {:enum-value :admin}]} :account_type {:values [{:enum-value :dividend} {:enum-value :expense} {:enum-value :asset} diff --git a/src/clj/auto_ap/graphql/plaid.clj b/src/clj/auto_ap/graphql/plaid.clj index 4ec1ef83..763ebe07 100644 --- a/src/clj/auto_ap/graphql/plaid.clj +++ b/src/clj/auto_ap/graphql/plaid.clj @@ -2,25 +2,34 @@ (:require [auto-ap.datomic :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query]] - [auto-ap.graphql.utils :refer [assert-admin assert-present <-graphql ->graphql]] + [auto-ap.graphql.utils + :refer [->graphql + <-graphql + assert-admin + assert-can-see-client + assert-present + limited-clients]] [auto-ap.plaid.core :as p] [clj-time.coerce :as coerce] [clj-time.core :as time] + [clojure.tools.logging :as log] [com.walmartlabs.lacinia.util :refer [attach-resolvers]] [datomic.api :as d])) (defn plaid-link-token [context value args] - (assert-admin (:id context)) + (when-not (:client_id value) + (throw (ex-info "Client ID is required" {:validation-error "Client ID is required"}))) + (assert-can-see-client (:id context) (:client_id value)) (let [client-code (:client/code (d/pull (d/db conn) [:client/code] (:client_id value)))] {:token (p/get-link-token client-code)})) (defn link-plaid [context value args] - (assert-admin (:id context)) (when-not (:client_code value) (throw (ex-info "Client not provided" {:validation-error "Client not provided."}))) (when-not (:public_token value) (throw (ex-info "Public token not provided" {:validation-error "public token not provided"}))) - + (log/info (:id context) (:db/id (d/pull (d/db conn) [:db/id] [:client/code (:client_code value)]))) + (assert-can-see-client (:id context) (:db/id (d/pull (d/db conn) [:db/id] [:client/code (:client_code value)]))) (let [access-token (:access_token (p/exchange-public-token (:public_token value) (:client_code value))) account-result (p/get-accounts access-token ) item {:plaid-item/client [:client/code (:client_code value)] @@ -40,7 +49,8 @@ :plaid-item/_accounts "plaid-item"} balance (assoc :plaid-account/balance balance))))) (into [item]))) - {:message (str "Plaid linked successfully. Access Token: " access-token)})) + (log/info "Access token was " access-token) + {:message (str "Plaid linked successfully.")})) (def default-read '[:db/id @@ -54,7 +64,6 @@ :plaid-account/name]}]) (defn raw-graphql-ids [db args] - (println args) (let [query (cond-> {:query {:find [] :in ['$] :where []} @@ -63,6 +72,11 @@ (:sort args) (add-sorter-fields {"external-id" ['[?e :plaid-item/external-id ?sort-external-id]]} args) + (limited-clients (:id args)) + (merge-query {:query {:in ['[?xx ...]] + :where ['[?e :plaid-item/client ?xx]]} + :args [ (set (map :db/id (limited-clients (:id args))))]}) + (:client-id args) (merge-query {:query {:in '[?client-id] :where ['[?e :plaid-item/client ?client-id]]} @@ -93,7 +107,7 @@ (defn get-plaid-item-page [context args value] - (assert-admin (:id context)) + (let [args (assoc args :id (:id context)) [plaid-items cnt] (get-graphql (<-graphql (assoc args :id (:id context))))] {:plaid_items (->> plaid-items diff --git a/src/clj/auto_ap/graphql/transaction_rules.clj b/src/clj/auto_ap/graphql/transaction_rules.clj index 864bcd92..6f1e95e5 100644 --- a/src/clj/auto_ap/graphql/transaction_rules.clj +++ b/src/clj/auto_ap/graphql/transaction_rules.clj @@ -1,59 +1,49 @@ (ns auto-ap.graphql.transaction-rules - (:require [auto-ap.datomic - :refer - [audit-transact merge-query remove-nils replace-nils-with-retract uri conn]] - [auto-ap.datomic.transaction-rules :as tr] - [auto-ap.datomic.transactions :as d-transactions] - [auto-ap.graphql.utils - :refer - [->graphql - <-graphql - assert-admin - ident->enum-f - limited-clients - result->page - snake->kebab]] - [auto-ap.rule-matching :as rm] - [auto-ap.utils :refer [dollars=]] - [clj-time.coerce :as coerce] - [clojure.set :as set] - [clojure.string :as str] - [clojure.tools.logging :as log] - [datomic.api :as d] - [clj-time.coerce :as c]) - (:import java.time.temporal.ChronoField)) + (:require + [auto-ap.datomic + :refer [audit-transact + conn + merge-query + remove-nils + replace-nils-with-retract]] + [auto-ap.datomic.transaction-rules :as tr] + [auto-ap.datomic.transactions :as d-transactions] + [auto-ap.graphql.utils + :refer [->graphql + <-graphql + assert-admin + ident->enum-f + limited-clients + result->page + snake->kebab]] + [auto-ap.rule-matching :as rm] + [auto-ap.utils :refer [dollars=]] + [clj-time.coerce :as c] + [clojure.set :as set] + [clojure.string :as str] + [datomic.api :as d])) -(defn get-transaction-rule-page [context args value] +(defn get-transaction-rule-page [context args _] (let [args (assoc args :id (:id context)) [journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))] (result->page (->> journal-entries (map (ident->enum-f :transaction-rule/transaction-approval-status))) journal-entries-count :transaction_rules args))) -(defn get-transaction-rule-matches [context args value] +(defn get-transaction-rule-matches [context args _] (if (= "admin" (:user/role (:id context))) (let [all-rules (tr/get-all) - transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date coerce/to-date)] + transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date)] (map ->graphql (rm/get-matching-rules transaction all-rules))) nil)) -(defn deleted-accounts [transaction accounts] - (let [current-accounts (:transaction-rule/accounts transaction) - specified-ids (->> accounts - (map :id) - set) - existing-ids (->> current-accounts - (map :db/id) - set)] - (set/difference existing-ids specified-ids))) - (defn transaction-rule-account->entity [{:keys [id account_id percentage location]}] (remove-nils #:transaction-rule-account {:percentage percentage :db/id id :account account_id :location location})) -(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} value] +(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} _] (assert-admin (:id context)) (let [existing-transaction-rule (tr/get-by-id transaction_rule_id)] (when-not (:transaction-rule/description existing-transaction-rule) @@ -62,10 +52,9 @@ (audit-transact [[:db/retractEntity transaction_rule_id]] (:id context)) transaction_rule_id)) -(defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule :as z} value] +(defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule} _] (assert-admin (:id context)) (let [existing-transaction (tr/get-by-id id) - deleted (deleted-accounts existing-transaction accounts) account-total (reduce + 0 (map (fn [x] (:percentage x)) accounts)) _ (when-not (dollars= 1.0 account-total) (let [error (str "Account total (" account-total ") does not reach 100%")] @@ -75,7 +64,7 @@ (let [error (str "You must provide a description or a yodlee merchant")] (throw (ex-info error {:validation-error error})))) _ (doseq [a accounts - :let [{:keys [:account/location :account/name] :as account} (d/entity (d/db conn) (:account_id a)) + :let [{:keys [:account/location :account/name]} (d/entity (d/db conn) (:account_id a)) client (d/entity (d/db conn) client_id) ]] (when (and location (not= location (:location a))) @@ -120,7 +109,7 @@ (defn tr [z x] (re-find (re-pattern z) x)) -(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/note :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count] +(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count] (->> (d/query (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name] @@ -204,7 +193,7 @@ (map ->graphql)) conj []))) -(defn test-transaction-rule [{:keys [id]} {{:keys [description note client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule :as z} value] +(defn test-transaction-rule [{:keys [id]} {{:keys [description client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule} _] (assert-admin id) (-test-transaction-rule id #:transaction-rule {:description description :client (when client_id {:db/id client_id}) @@ -217,6 +206,6 @@ true 15)) -(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} value] +(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _] (assert-admin id) (-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count)) diff --git a/src/clj/auto_ap/graphql/users.clj b/src/clj/auto_ap/graphql/users.clj index bf2fc5f6..50ff139a 100644 --- a/src/clj/auto_ap/graphql/users.clj +++ b/src/clj/auto_ap/graphql/users.clj @@ -3,11 +3,11 @@ [auto-ap.datomic.users :as d-users] [auto-ap.graphql.utils :refer [->graphql assert-admin]])) -(def role->datomic-role {":none" :user-role/none - ":admin" :user-role/admin - ":power_user" :user-role/power-user - ":manager" :user-role/manager - ":user" :user-role/user}) +(def role->datomic-role {:none :user-role/none + :admin :user-role/admin + :power_user :user-role/power-user + :manager :user-role/manager + :user :user-role/user}) (defn edit-user [context {:keys [edit_user] :as args} value] (assert-admin (:id context)) diff --git a/src/clj/auto_ap/graphql/utils.clj b/src/clj/auto_ap/graphql/utils.clj index 6a5c6616..d28b4a8f 100644 --- a/src/clj/auto_ap/graphql/utils.clj +++ b/src/clj/auto_ap/graphql/utils.clj @@ -128,7 +128,7 @@ (if (str/includes? q "&") (str "\"" q "\"~0.8") (let [parts (-> q - (str/replace #"[\[\]\+\*]" "") + (str/replace #"[\[\]\+\*\-]" "") (str/split #"\s+")) exacts (butlast parts) partial (last parts)] diff --git a/src/clj/auto_ap/server.clj b/src/clj/auto_ap/server.clj index cc12b7ec..c331cb37 100644 --- a/src/clj/auto_ap/server.clj +++ b/src/clj/auto_ap/server.clj @@ -108,7 +108,7 @@ (add-shutdown-hook! shutdown-mount) (start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler)) - (alter-var-root #'nrepl.middleware.print/*print-fn* (constantly clojure.pprint/pprint)) + #_(alter-var-root #'nrepl.middleware.print/*print-fn* (constantly clojure.pprint/pprint)) (apply mount/start-without without))) (comment diff --git a/src/cljc/auto_ap/client_routes.cljc b/src/cljc/auto_ap/client_routes.cljc index f3344447..e54bb53b 100644 --- a/src/cljc/auto_ap/client_routes.cljc +++ b/src/cljc/auto_ap/client_routes.cljc @@ -14,11 +14,9 @@ "rules" :admin-rules "accounts" :admin-accounts "import-batches" :admin-import-batches - "reminders" :admin-reminders "vendors" :admin-vendors "excel-import" :admin-excel-import - "yodlee2" :admin-yodlee2 - "plaid" :admin-plaid} + "yodlee2" :admin-yodlee2} "invoices/" {"" :invoices "import" :import-invoices "unpaid" :unpaid-invoices @@ -33,6 +31,7 @@ "requires-feedback" :requires-feedback-transactions "excluded" :excluded-transactions} "reports/" {"" :reports} + "plaid" :plaid "ledger/" {"" :ledger "profit-and-loss" :profit-and-loss "balance-sheet" :balance-sheet diff --git a/src/cljc/auto_ap/entities/invoice.cljc b/src/cljc/auto_ap/entities/invoice.cljc index 6f9c57b8..60526b0f 100644 --- a/src/cljc/auto_ap/entities/invoice.cljc +++ b/src/cljc/auto_ap/entities/invoice.cljc @@ -2,21 +2,4 @@ (:require [clojure.spec.alpha :as s] [auto-ap.entities.shared :as shared])) -(s/def ::vendor map?) -(s/def ::vendor-name string?) -(s/def ::client map?) -(s/def ::invoice-number ::shared/required-identifier) -(s/def ::date ::shared/date) -(s/def ::due (s/nilable ::shared/date)) -(s/def ::scheduled-payment (s/nilable ::shared/date)) -(s/def ::total ::shared/money) -(s/def ::invoice (s/keys :req-un [::client - ::invoice-number - ::date - ::vendor - ::total] - :opt-un [::vendor-name - ::due - ::scheduled-payment - ])) diff --git a/src/cljc/auto_ap/entities/shared.cljc b/src/cljc/auto_ap/entities/shared.cljc index 80ec036a..3a0adc33 100644 --- a/src/cljc/auto_ap/entities/shared.cljc +++ b/src/cljc/auto_ap/entities/shared.cljc @@ -19,6 +19,9 @@ :ldt? #(instance? org.joda.time.LocalDate %) :str? (s/and string? #(re-matches date-regex %))))) +(s/def ::required some?) +(s/def ::has-id (s/and map? + #(:id %))) (s/def ::required-identifier (s/and string? #(not (str/blank? %)))) diff --git a/src/cljc/auto_ap/ledger/reports.cljc b/src/cljc/auto_ap/ledger/reports.cljc index 4a59fb35..3dad705f 100644 --- a/src/cljc/auto_ap/ledger/reports.cljc +++ b/src/cljc/auto_ap/ledger/reports.cljc @@ -530,8 +530,8 @@ [title a b (and (:value a) (:value b) {:border (:border b) :format :dollar - :value (- (:value a) - (:value b))})])))) + :value (- (or (:value a) 0.0) + (or (:value b) 0.0))})])))) (defn summarize-balance-sheet [pnl-data] (let [pnl-datas (map (fn [p] diff --git a/src/cljs/auto_ap/effects.cljs b/src/cljs/auto_ap/effects.cljs index a2d741b8..3837a89d 100644 --- a/src/cljs/auto_ap/effects.cljs +++ b/src/cljs/auto_ap/effects.cljs @@ -17,7 +17,8 @@ (re-frame/reg-fx :redirect (fn [uri] - (pushy/set-token! p/history uri))) + (pushy/set-token! p/history uri) + (p/dispatch-route (p/parse-url uri)))) (re-frame/reg-fx :set-uri-params diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 92fc9162..ff28338c 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -2,13 +2,13 @@ (:require [auto-ap.db :as db] [auto-ap.routes :as routes] - [auto-ap.subs :as subs] [auto-ap.utils :refer [by]] - [auto-ap.views.utils :refer [with-user]] + [auto-ap.views.utils :refer [with-user parse-jwt]] [bidi.bidi :as bidi] [clojure.string :as str] [goog.crypt.base64 :as b64] - [re-frame.core :as re-frame])) + [re-frame.core :as re-frame] + [goog.crypt.base64 :as base64])) (defn jwt->data [token] (js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\." )))))) @@ -142,15 +142,26 @@ (re-frame/reg-event-fx ::set-active-route (fn [{:keys [db]} [_ handler params route-params]] - + (cond + (and (not= :login handler) (not (:user db))) + {:redirect (bidi/path-for routes/routes :login) + :db (assoc db :active-route :login + :active-page :login + :menu nil + :page-failure nil)} - (if (and (not= :login handler) (not (:user db))) - {:redirect "/login" - :db (assoc db :active-route :login - :page-failure nil)} + (and (not= "admin" (:user/role (parse-jwt (:user db)))) + (str/includes? (name handler) "admin")) + {:redirect (bidi/path-for routes/routes :index) + :db (assoc db :active-route :index + :active-page :index + :menu nil + :page-failure nil)} + :else {:db (-> db (assoc :active-route handler :page-failure nil + :menu nil :query-params params :route-params route-params) (auto-ap.views.pages.data-page/dispose-all))}))) diff --git a/src/cljs/auto_ap/events/admin/reminders.cljs b/src/cljs/auto_ap/events/admin/reminders.cljs deleted file mode 100644 index 33bdefee..00000000 --- a/src/cljs/auto_ap/events/admin/reminders.cljs +++ /dev/null @@ -1,7 +0,0 @@ -(ns auto-ap.events.admin.reminders - (:require [re-frame.core :as re-frame] - [auto-ap.db :as db] - [auto-ap.routes :as routes] - [auto-ap.effects :as effects])) - - diff --git a/src/cljs/auto_ap/forms.cljs b/src/cljs/auto_ap/forms.cljs index 0c1f40c5..638333e1 100644 --- a/src/cljs/auto_ap/forms.cljs +++ b/src/cljs/auto_ap/forms.cljs @@ -2,13 +2,16 @@ (:require [re-frame.core :as re-frame] [re-frame.interceptor :as i] [auto-ap.status :as status] - [auto-ap.views.utils :refer [dispatch-event bind-field]])) + [auto-ap.views.utils :refer [dispatch-event]] + [malli.core :as m])) (re-frame/reg-sub ::form (fn [db [_ x]] - (get (-> db ::forms) x))) + (update (get (-> db ::forms) x) + :visited (fn [v] + (or v #{}))))) (re-frame/reg-sub ::field @@ -28,12 +31,20 @@ ([db form data] (start-form db form data nil)) ([db form data complete-listener] - (assoc-in db [::forms form] {:error nil - :active? true - :id (random-uuid) - :status nil - :data data - :complete-listener complete-listener}))) + (-> db + (assoc-in [::forms form] {:error nil + :active? true + :id (random-uuid) + :visited #{} + :status nil + :data data + :complete-listener complete-listener}) + (assoc-in [::status/status form] nil)))) + +(re-frame/reg-event-db + ::start-form + (fn [db [_ id data]] + (start-form db id data))) (defn triggers-saved [form data-key] (i/->interceptor @@ -75,6 +86,28 @@ db (partition 2 path-pairs)))) +(re-frame/reg-event-db + ::reset + (fn [db [_ form v]] + (assoc-in db [::forms form :data] v))) + +(re-frame/reg-event-db + ::visited + (fn [db [_ form & paths]] + (update-in db [::forms form :visited] (fn [v] + (set (into v paths)))))) + +(re-frame/reg-event-db + ::check-problems + (fn [db [_ form schema]] + (assoc-in db [::forms form :problems] + (when schema (m/explain schema (get-in db [::forms form :data])))))) + +(re-frame/reg-event-db + ::attempted-submit + (fn [db [_ form & paths]] + (assoc-in db [::forms form :attempted-submit?] true))) + (defn change-handler [form customize-fn] (fn [db [_ & path-pairs]] @@ -92,6 +125,7 @@ (re-frame/reg-event-db ::save-error (fn [db [_ form result]] + (println result) (-> db (assoc-in [::forms form :status] :error) (assoc-in [::forms form :error] (or (:message (first result)) @@ -138,68 +172,3 @@ (assoc-in [::forms id :error] nil))) -(defn vertical-form [{:keys [can-submit id change-event submit-event fullwidth?] :or {fullwidth? true}}] - {:form - (fn [{:keys [title] :as params} & children] - (let [{:keys [data active? error]} @(re-frame/subscribe [::form id]) - can-submit @(re-frame/subscribe can-submit)] - - [:form { :on-submit (fn [e] - (when (.-stopPropagation e) - (.stopPropagation e) - (.preventDefault e)) - (when can-submit - (re-frame/dispatch-sync (vec (conj submit-event params)))))} - [:h1.title.is-2 title] - [:<> - children]])) - - :form-inline - (fn [{:keys [title] :as params} children] - (let [{:keys [data active? error]} @(re-frame/subscribe [::form id]) - can-submit @(re-frame/subscribe can-submit)] - - [:form { :on-submit (fn [e] - (when (.-stopPropagation e) - (.stopPropagation e) - (.preventDefault e)) - (when can-submit - (re-frame/dispatch-sync (vec (conj submit-event params)))))} - (when title - [:h1.title.is-2 title]) - children])) - :raw-field (fn [control] - (let [{:keys [data]} @(re-frame/subscribe [::form id])] - [bind-field (-> control - (assoc-in [1 :subscription] data) - (assoc-in [1 :event] change-event))])) - :field-holder (fn [label control] - [:div.field - (when label (if fullwidth? [:p.help label] - [:label.label label])) - [:div.control control]]) - :field ^{:key "field"} - (fn [label control] - (let [{:keys [data]} @(re-frame/subscribe [::form id])] - [:div.field - (when label (if fullwidth? [:p.help label] - [:label.label label])) - [:div.control [bind-field (-> control - (assoc-in [1 :subscription] data) - (assoc-in [1 :event] change-event))]]])) - - :error-notification - (fn [] - (when-let [error (:error @(re-frame/subscribe [::form id]))] - ^{:key error} - [:div.has-text-danger.animated.fadeInUp {} error])) - :submit-button (fn [child] - (let [error (:error @(re-frame/subscribe [::form id])) - status @(re-frame/subscribe [::status/single id]) - can-submit @(re-frame/subscribe can-submit)] - [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) - (not can-submit)) - :class (cond-> (status/class-for status) - fullwidth? (conj "is-fullwidth")) } - child]))}) - diff --git a/src/cljs/auto_ap/forms/builder.cljs b/src/cljs/auto_ap/forms/builder.cljs index 47cf5be3..66bcd1ec 100644 --- a/src/cljs/auto_ap/forms/builder.cljs +++ b/src/cljs/auto_ap/forms/builder.cljs @@ -1,11 +1,12 @@ (ns auto-ap.forms.builder (:require - [auto-ap.views.utils :refer [bind-field]] [re-frame.core :as re-frame] [react :as react] [reagent.core :as r] [auto-ap.forms :as forms] - [auto-ap.status :as status])) + [auto-ap.status :as status] + [malli.core :as m] + [malli.error :as me])) (defonce ^js/React.Context form-context (react/createContext "default")) (def ^js/React.Provider Provider (. form-context -Provider)) @@ -15,104 +16,231 @@ (def ^js/React.Provider FormScopeProvider (. form-scope-context -Provider)) (def ^js/React.Consumer FormScopeConsumer (. form-scope-context -Consumer)) -(defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}] - (let [data-sub (or data-sub [::forms/form id]) - change-event (or change-event [::forms/change id]) - {:keys [data error] form-key :id} @(re-frame/subscribe data-sub) - status @(re-frame/subscribe [::status/single id])] - (r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit) - :change-event change-event - :submit-event submit-event - :error error - :status status - :id id - :data data - :fullwidth? fullwidth?}} +(defn valid-field? [problems field-path] + (not (get-in (me/humanize problems) field-path))) + +(defn spec-error-message [problems field-path error-messages] + (-> (me/humanize problems + {:errors (merge (-> me/default-errors + (assoc ::m/missing-key {:error/message "Required"} + ::m/invalid-type {:error/fn + (fn [a b] + (if (nil? (:value a)) + "Required" + "Invalid"))})) + error-messages)}) + (get-in field-path) + first)) + +(defn consume [consumer-component fields f] + [:> consumer-component {} + (fn [consumed] + (r/as-element + (apply f (for [field fields] + (aget consumed field)))))]) + +(re-frame/reg-event-fx + ::blurred + (fn [_ [_ schema id field]] + {:dispatch-n [[::forms/check-problems id schema] + [::forms/visited id field]]})) + +(defn builder [{:keys [value on-change can-submit data-sub error-messages change-event submit-event id fullwidth? schema validation-error-string]}] + (when (and change-event on-change) + (throw "Either the form is to be managed by ::forms, or it should have value and on-change passed in")) + (let [data-sub (or data-sub [::forms/form id]) + change-event (when-not on-change + (or change-event [::forms/change id])) + {:keys [data visited attempted-submit? problems error] form-key :id} @(re-frame/subscribe data-sub) + data (or value data) + status @(re-frame/subscribe [::status/single id]) + can-submit (if can-submit @(re-frame/subscribe can-submit) + true)] + (r/create-element Provider #js {:value #js {:can-submit can-submit + :error-messages (or error-messages + nil) + :on-change on-change + :change-event change-event + :blur-event [::blurred schema id] + :visited visited + :submit-event submit-event + :problems problems + :attempted-submit? attempted-submit? + :error (or error (-> status :error first :message)) + :status status + :id id + :data data + :fullwidth? fullwidth?}} (r/as-element ^{:key form-key} [:form {:on-submit (fn [e] (when (.-stopPropagation e) (.stopPropagation e) (.preventDefault e)) - (when can-submit - (re-frame/dispatch-sync (vec (conj submit-event {})))))} + (if (and schema (not (m/validate schema data))) + (do + (re-frame/dispatch-sync [::status/dispose-single id]) + (re-frame/dispatch [::status/error id [{:message (or validation-error-string "Please fix the errors and try again.")}]]) + (re-frame/dispatch [::forms/attempted-submit id])) + (when can-submit + (re-frame/dispatch-sync (vec (conj submit-event {}))))))} (into [:fieldset {:disabled (boolean (= :loading (:state status)))}] - (r/children (r/current-component)))] + (r/children (r/current-component)))] )))) +;; TODO make virtual builder operate as a cursor and an input instead of a whole new thing +;; make it inherit the outer form, avoiding creating new forms +(defn virtual-builder [] + (let [starting-key (random-uuid) + key (r/atom starting-key)] + (re-frame/dispatch [::forms/start-form starting-key []]) + (fn [{:keys [value on-change can-submit error-messages fullwidth? schema attempted-submit?]}] + (let [data-sub [::forms/form @key] + {:keys [data error problems visited]} @(re-frame/subscribe data-sub) + data (or value data)] + (r/create-element Provider #js {:value #js {:can-submit can-submit + :error-messages (or error-messages + nil) + ;; wrap to make sure raw form updates too + :on-change (fn [v o] + (re-frame/dispatch-sync [::forms/reset @key v]) + (on-change v o)) + :blur-event [::blurred schema @key ] + :problems problems + :attempted-submit? attempted-submit? + :visited visited + :error error + :id @key + :data data + :fullwidth? fullwidth?}} + (r/as-element + ^{:key @key} + (into [:<>] + (r/children (r/current-component))))))))) -(defn raw-field [] + +(defn change-handler [path re-frame-change-event event-or-value] + (re-frame/dispatch (-> re-frame-change-event + (conj path) + (conj (if-let [target (some-> event-or-value (aget "target"))] + (aget target "value") + event-or-value))))) + +(defn form-change-handler [data path on-change event-or-value] + (on-change (assoc-in data path (if-let [target (some-> event-or-value (aget "target"))] + (aget target "value") + event-or-value)) + data)) + +(defn blur-handler [path re-frame-blur-event original-on-blur e] + (when original-on-blur + (original-on-blur e)) + + (re-frame/dispatch (-> re-frame-blur-event + (conj path)))) + +(defn raw-error-v2 [{:keys [field]}] + (consume Consumer + ["visited" "attempted-submit?" "problems" "error-messages"] + (fn [visited attempted-submit? problems error-messages] + (consume FormScopeConsumer + ["scope"] + (fn [scope] + (let [scope (or scope []) + full-field-path (cond + (sequential? field) + (into scope field) + + field + (conj scope field) + + :else + nil) + visited? (get visited full-field-path)] + (when-let [error-message (and + (or visited? attempted-submit?) + (spec-error-message problems full-field-path error-messages))] + [:div + [:p.help.has-text-danger error-message]]))))))) + +(defn raw-field-v2 [{:keys [field] :as props}] + (when-not field + (throw (ex-info (str "Missing field") (clj->js {:props props})))) (let [[child] (r/children (r/current-component))] - [:> Consumer {} - (fn [consume-form] - (r/as-element - [:> FormScopeConsumer {} - (fn [form-scope] - (r/as-element - [bind-field (-> child - (update-in [1 :field] (fn [f] - (cond - (sequential? f) - (into form-scope f) + (consume Consumer + ["visited" "attempted-submit?" "data" "on-change" "change-event" "blur-event" "problems"] + (fn [visited attempted-submit? data on-change change-event blur-event problems] + (consume FormScopeConsumer + ["scope"] + (fn [scope] + (update child 1 (fn [child-props] + (let [scope (or scope []) + full-field-path (cond + (sequential? field) + (into scope field) - f - (conj form-scope f) + field + (conj scope field) - :else - nil))) - - (assoc-in [1 :subscription] (aget consume-form "data")) - (assoc-in [1 :event] (aget consume-form "change-event")))]))]))])) + :else + nil) + visited? (get visited full-field-path) + value (get-in data full-field-path)] + (-> child-props + (assoc :on-change + (if on-change + (partial form-change-handler data full-field-path on-change) + (partial change-handler full-field-path change-event)) + :on-blur (partial blur-handler full-field-path blur-event (:on-blur child-props)) + :value value) + (update :class (fn [class] + (str class + (cond + (and (not visited?) (not attempted-submit?)) + "" + (not (valid-field? problems full-field-path)) + " is-danger" + value + " is-success" + + :else + "")))))))))))))) (defn with-scope [{:keys [scope]}] - (r/create-element FormScopeProvider #js {:value scope} + (r/create-element FormScopeProvider #js {:value #js {:scope scope}} (r/as-element (into [:<>] (r/children (r/current-component)))))) + (defn vertical-control [{:keys [is-small? required?]}] (let [[label & children] (r/children (r/current-component))] - [:> Consumer {} - (fn [consume] - (r/as-element - [:div.field - (if (aget consume "fullwidth?") - [:p.help label] - [:label.label - (if required? - [:span label [:span.has-text-danger " *"]] - label)]) - (into [:div.control ] children)]))])) + (consume Consumer + ["fullwidth?"] + (fn [fullwidth?] + [:div.field + (if fullwidth? + [:p.help label] + [:label.label + (if required? + [:span label [:span.has-text-danger " *"]] + label)]) + (into [:div.control ] children)])))) -(defn field [] - (let [props (r/props (r/current-component)) +(defn field-v2 [] + (let [props (r/props (r/current-component)) [label child] (r/children (r/current-component))] - [:> Consumer {} - (fn [consume] - (r/as-element - [:div.field - (when label - (if (aget consume "fullwidth?") - [:p.help label] - [:label.label - (if (:required? props) - [:span label [:span.has-text-danger " *"]] - label)])) - [:div.control [raw-field {} child]]]))])) - -(defn horizontal-control [] - (let [[label & children] (r/children (r/current-component))] - [:div.field.is-horizontal - (when label - [:div.field-label [:label.label label]]) - [:div.field-body - (for [[i child] (map vector (range) children)] - ^{:key i} - [:div.field - child])]])) - -(defn horizontal-field [] - (let [[label child] (r/children (r/current-component))] - [horizontal-control - label - [raw-field {} child]])) + (consume Consumer + ["fullwidth?"] + (fn [fullwidth?] + [:div.field + (when label + (if fullwidth? + [:p.help label] + [:label.label + (if (:required? props) + [:span label [:span.has-text-danger " *"]] + label)])) + [:div.control [raw-field-v2 props child]] + [:div + [raw-error-v2 {:field (:field props)}]]])))) (defn section [{:keys [title]}] [:<> @@ -123,36 +251,37 @@ (defn submit-button [{:keys [class]}] (let [[child] (r/children (r/current-component))] - [:> Consumer {} - (fn [consume] - (let [status (aget consume "status") - can-submit (aget consume "can-submit") - fullwidth? (aget consume "fullwidth?")] - (r/as-element - [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) - (not can-submit)) - :class (cond-> (or class []) - (status/class-for status) (conj (status/class-for status)) - fullwidth? (conj "is-fullwidth")) } - child])))])) + (consume + Consumer + ["status" "can-submit" "fullwidth?"] + (fn [status can-submit fullwidth?] + [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) + (not can-submit)) + :class (cond-> (or class []) + (status/class-for status) (into (status/class-for status)) + fullwidth? (conj "is-fullwidth")) } + child])))) (defn hidden-submit-button [] - [:> Consumer {} - (fn [consume] - (let [status (aget consume "status") - can-submit (aget consume "can-submit")] - (r/as-element - [:div {:style {:display "none"}} - [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) - (not can-submit))}]])))]) + (consume Consumer ["status" "can-submit"] + (fn [status can-submit] + [:div {:style {:display "none"}} + [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) + (not can-submit))}]]))) (defn error-notification [] - (let [[child] (r/children (r/current-component))] - [:> Consumer {} - (fn [consume] - (r/as-element - (when-let [error (aget consume "error")] - ^{:key error} - [:div.has-text-danger.animated.fadeInUp {} error])))])) + (consume Consumer ["error" "status"] + (fn [error status] + (println status) + (cond error + ^{:key error} + [:div.has-text-danger.animated.fadeInUp {} error] + (-> status :error first :message) + [:div.has-text-danger.animated.fadeInUp {} (-> status :error first :message)] + (-> status :error) + [:div.has-text-danger.animated.fadeInUp {} (-> status :error str)] + + :else + nil)))) diff --git a/src/cljs/auto_ap/history.cljs b/src/cljs/auto_ap/history.cljs index 483e27ca..63177fd8 100644 --- a/src/cljs/auto_ap/history.cljs +++ b/src/cljs/auto_ap/history.cljs @@ -6,11 +6,11 @@ [cemerick.url :refer [url]] [re-frame.core :as re-frame])) -(defn- parse-url [url] +(defn parse-url [url] (println "parsing url" url) (bidi/match-route routes/routes url)) -(defn- dispatch-route [matched-route] +(defn dispatch-route [matched-route] (println "Matched route" matched-route) (re-frame/dispatch [:auto-ap.events/set-active-route (:handler matched-route) (u/query-params) (:route-params matched-route)])) diff --git a/src/cljs/auto_ap/schema.cljs b/src/cljs/auto_ap/schema.cljs new file mode 100644 index 00000000..b3ae551e --- /dev/null +++ b/src/cljs/auto_ap/schema.cljs @@ -0,0 +1,26 @@ +(ns auto-ap.schema + (:require [malli.core :as m])) + +(def reference (m/schema [:map [:id :string]])) +(def date (m/schema [:fn + (fn [d] + (if-not (or (instance? goog.date.DateTime d) + (instance? goog.date.Date d)) + (throw (ex-info "Invalid Date" {:type ::m/invalid-type})) + true))])) + +(def money (m/schema [float? {:error/message "Invalid money"}])) +(def not-empty-string (m/schema [:re {:error/message "Required"} #"\S+"])) +(def code-string (m/schema [:re #"[A-Z0-9\-]+"])) + +(def positive-integer (m/schema [:int {:min 1}])) +(def integer-code (m/schema [:int {:min 10000 :max 99999}])) + +(def expense-account (m/schema [:map + [:id :string] + [:account reference] + [:location :string] + [:amount money]])) + + +(def approval-status (m/schema [:enum :unapproved :requires-feedback :approved :excluded])) diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index a7ebbca4..a8900709 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -2,6 +2,7 @@ (ns auto-ap.subs (:require [re-frame.core :as re-frame] [auto-ap.utils :refer [by]] + [auto-ap.views.utils :refer [parse-jwt]] [clojure.string :as str] [goog.crypt.base64 :as base64] [minisearch :as ms])) @@ -22,6 +23,12 @@ (when (:user db) (sort-by :name (vals (:clients db)))))) +(re-frame/reg-sub + ::client-refs + :<- [::clients] + (fn [c] + (map #(select-keys % [:id :name]) c))) + (re-frame/reg-sub ::all-accounts (fn [db] @@ -135,8 +142,7 @@ (re-frame/reg-sub ::user (fn [db] - (when (:user db) - (js->clj (.parse js/JSON (base64/decodeString (second (str/split (:user db) #"\.")))) :keywordize-keys true)))) + (parse-jwt (:user db)))) (re-frame/reg-sub ::active-route diff --git a/src/cljs/auto_ap/views/components.cljs b/src/cljs/auto_ap/views/components.cljs new file mode 100644 index 00000000..b5de7fee --- /dev/null +++ b/src/cljs/auto_ap/views/components.cljs @@ -0,0 +1,72 @@ +(ns auto-ap.views.components + (:require [reagent.core :as r] + [clojure.string :as str] + [auto-ap.views.components.multi :as multi] + [auto-ap.views.components.money-field :as money] + [auto-ap.views.components.number :as number] + [auto-ap.views.components.typeahead.vendor :as typeahead] + [auto-ap.views.components.button-radio :as br])) + + +(defn checkbox [{:keys [on-change + value + label] + :as props}] + (into [:label.checkbox + [:input (-> props + (assoc + :type "checkbox" + :on-change (fn [] + (on-change (not value))) + :checked value) + (dissoc :value))] + " " label + ] + (r/children (r/current-component)))) + +(defn select-field [{:keys [options allow-nil? class on-change keywordize?] :as props}] + [:div.select {:class class} + [:select (-> props + (dissoc :allow-nil? :class :options) + (update :value (fn [v] + (cond (str/blank? v) + "" + + keywordize? + (name v) + + :else + v))) + (assoc :on-change + (fn [e] + (println "VALUE IS" (keyword (.. e -target -value))) + (if keywordize? + (on-change (keyword (.. e -target -value))) + (on-change e)))) + (dissoc :keywordize?)) + [:<> + (when allow-nil? + [:option {:value nil}]) + (for [[k v] options] + ^{:key k} [:option {:value k} v])]]]) + +(defn switch-input [{:keys [id label on-change value class]}] + [:<> + [:input.switch {:type "checkbox" + :id id + :on-change (fn [] + (on-change (not value))) + :checked (boolean value) + :class class}] + [:label {:for id} label]]) + +(def multi-field-v2 multi/multi-field-v2) + +(def number-input number/number-input) + +(def money-input money/field) + +(def search-backed-typeahead typeahead/search-backed-typeahead) +(def entity-typeahead typeahead/typeahead-v3) + +(def button-radio-input br/button-radio) diff --git a/src/cljs/auto_ap/views/components/address.cljs b/src/cljs/auto_ap/views/components/address.cljs index f0ebc02a..fa92e600 100644 --- a/src/cljs/auto_ap/views/components/address.cljs +++ b/src/cljs/auto_ap/views/components/address.cljs @@ -1,108 +1,34 @@ (ns auto-ap.views.components.address - (:require [auto-ap.entities.address :as address] - [auto-ap.views.utils :refer [dispatch-value-change dispatch-event bind-field horizontal-field]] - [auto-ap.forms.builder :as form-builder])) + (:require + [auto-ap.entities.address :as address] + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components.level :as level])) -(defn address-field [{:keys [event field subscription]}] - [:span - [horizontal-field - nil - [:div.control - [:p.help "Address"] - [bind-field - [:input.input.is-expanded {:type "text" - :placeholder "1700 Pennsylvania Ave" - :field (conj field :street1) - :spec ::address/street1 - :event event - :subscription subscription}]]]] - - [horizontal-field - nil - [:div.control - [bind-field - [:input.input.is-expanded {:type "text" - :placeholder "Suite 400" - :field (conj field :street2) - :spec ::address/street2 - :event event - :subscription subscription}]]]] - - [horizontal-field - nil - [:div.control - [:p.help "City"] - [bind-field - [:input.input.is-expanded {:type "text" - :placeholder "Cupertino" - :field (conj field :city) - :spec ::address/city - :event event - :subscription subscription}]]] - [:div.control - [:p.help "State"] - [bind-field - [:input.input {:type "text" - :placeholder "CA" - :field (conj field :state) - :spec ::address/state - :size 2 - :max-length "2" - :event event - :subscription subscription}]]] - [:div.control - [:p.help "Zip"] - [bind-field - [:input.input {:type "text" - :field (conj field :zip) - :spec ::address/zip - :event event - :subscription subscription - :placeholder "95014"}]]]]]) - -(defn address2-field [] - [:span - [horizontal-field - nil - [:div.control +(defn address2-field [{:keys [value on-change]}] + [form-builder/virtual-builder {:value (or value {}) + :on-change on-change} + [:div + [form-builder/field-v2 {:field :street1} [:p.help "Street Address"] - [form-builder/raw-field - [:input.input.is-expanded {:type "text" - :placeholder "1700 Pennsylvania Ave" - :field [:street1] - :spec ::address/street1}]]]] - - [horizontal-field - nil - [:div.control - [form-builder/raw-field - [:input.input.is-expanded {:type "text" - :placeholder "Suite 400" - :field [:street2] - :spec ::address/street2}]]]] - - [horizontal-field - nil - [:div.control - [:p.help "City"] - [form-builder/raw-field - [:input.input.is-expanded {:type "text" + [:input.input.is-expanded {:type "text" + :placeholder "1700 Pennsylvania Ave"}]] + [form-builder/raw-field-v2 {:field :street2} + [:input.input.is-expanded {:type "text" + :placeholder "Suite 400"}]] + [level/left-stack + [form-builder/field-v2 {:field :city} + [:p.help "City"] + [:input.input.is-expanded {:type "text" :placeholder "Cupertino" - :field [:city] - :spec ::address/city}]]] - [:div.control - [:p.help "State"] - [form-builder/raw-field - [:input.input {:type "text" - :placeholder "CA" - :field [:state] - :spec ::address/state - :size 2 - :max-length "2"}]]] - [:div.control - [:p.help "Zip"] - [form-builder/raw-field - [:input.input {:type "text" - :field [:zip] - :spec ::address/zip - :placeholder "95014"}]]]]]) + :field [:city] + :spec ::address/city}]] + [form-builder/field-v2 {:field :state} + [:p.help "State"] + [:input.input {:type "text" + :placeholder "CA" + :size 2 + :max-length "2"}]] + [form-builder/field-v2 {:field :zip} + [:p.help "Zip"] + [:input.input {:type "text" + :placeholder "95014"}]]]]]) diff --git a/src/cljs/auto_ap/views/components/admin/side_bar.cljs b/src/cljs/auto_ap/views/components/admin/side_bar.cljs index 4afbdfa3..e239dbef 100644 --- a/src/cljs/auto_ap/views/components/admin/side_bar.cljs +++ b/src/cljs/auto_ap/views/components/admin/side_bar.cljs @@ -1,15 +1,11 @@ (ns auto-ap.views.components.admin.side-bar - (:require [re-frame.core :as re-frame] - [reagent.core :as r] - [clojure.string :as str] - [clojure.spec.alpha :as s] - [cljs-time.core :as c] - [goog.string :as gstring] - [bidi.bidi :as bidi] - [auto-ap.routes :as routes] - [auto-ap.views.utils :refer [active-when dispatch-event bind-field horizontal-field date->str str->date pretty standard]] - [auto-ap.subs :as subs] - [auto-ap.events :as events])) + (:require + [auto-ap.routes :as routes] + [auto-ap.subs :as subs] + [auto-ap.views.utils :refer [active-when]] + [bidi.bidi :as bidi] + [re-frame.core :as re-frame] + [reagent.core :as r])) (defn admin-side-bar [params ] (let [ap @(re-frame/subscribe [::subs/active-page])] @@ -56,20 +52,9 @@ [:span {:class "icon icon-saving-bank-1" :style {:font-size "25px"}}] [:span {:class "name"} "Yodlee 2 Link"]]] - [:li.menu-item - [:a {:href (bidi/path-for routes/routes :admin-plaid), :class (str "item" (active-when ap = :admin-plaid))} - [:span {:class "icon icon-saving-bank-1" :style {:font-size "25px"}}] - [:span {:class "name"} "Plaid Link"]]] + [:ul ]] - [:p.menu-label "History"] - [:ul.menu-list - [:li.menu-item - [:a {:href (bidi/path-for routes/routes :admin-reminders) , :class (str "item" (active-when ap = :admin-reminders))} - [:span {:class "icon"} - [:i {:class "fa fa-star-o"}]] - - [:span {:class "name"} "Reminders"]]]] [:p.menu-label "Import"] [:ul.menu-list [:li.menu-item diff --git a/src/cljs/auto_ap/views/components/bank_account_filter.cljs b/src/cljs/auto_ap/views/components/bank_account_filter.cljs index 146e9aec..6c73ff15 100644 --- a/src/cljs/auto_ap/views/components/bank_account_filter.cljs +++ b/src/cljs/auto_ap/views/components/bank_account_filter.cljs @@ -1,8 +1,6 @@ (ns auto-ap.views.components.bank-account-filter (:require - [clojure.spec.alpha :as s] - [auto-ap.entities.invoice :as invoice] - [auto-ap.views.utils :refer [bind-field ->$]] + [auto-ap.views.utils :refer [->$]] [auto-ap.subs :as subs] [re-frame.core :as re-frame])) diff --git a/src/cljs/auto_ap/views/components/date_range_filter.cljs b/src/cljs/auto_ap/views/components/date_range_filter.cljs index c7eea34e..35890012 100644 --- a/src/cljs/auto_ap/views/components/date_range_filter.cljs +++ b/src/cljs/auto_ap/views/components/date_range_filter.cljs @@ -1,57 +1,51 @@ (ns auto-ap.views.components.date-range-filter (:require - [clojure.spec.alpha :as s] - [auto-ap.entities.invoice :as invoice] - [auto-ap.views.utils :refer [bind-field date-picker-optional date->str local-now standard]] + [auto-ap.views.utils :refer [date-picker date->str local-now standard]] [cljs-time.core :as t] - [re-frame.core :as re-frame])) + [re-frame.core :as re-frame] + [auto-ap.forms.builder :as form-builder])) -(defn dispatch-change [on-change-event start end] - (fn [_] - (re-frame/dispatch (into on-change-event [[:start] start]) ) - (re-frame/dispatch (into on-change-event [[:end] end])))) + +(defn set-value [on-change-event v] + (re-frame/dispatch (conj on-change-event v))) (defn date-range-filter [{:keys [value on-change-event]}] - [:div - [:div.field.has-addons - [:p.control [:a.button.is-small {:on-click - (dispatch-change on-change-event - (date->str (t/minus (local-now) (t/period :days 7)) standard) - (date->str (local-now) standard))} - "Week" ]] - [:p.control [:a.button.is-small {:on-click - (dispatch-change on-change-event - (date->str (t/minus (local-now) (t/period :months 1)) standard) - (date->str (local-now) standard))} - "Month" ]] - [:p.control [:a.button.is-small {:on-click - (dispatch-change on-change-event - (date->str (t/minus (local-now) (t/period :years 1)) standard) - (date->str (local-now) standard))} - "Year"]] - [:p.control [:a.button.is-small {:on-click - (dispatch-change on-change-event - nil - nil)} - "All"]]] - [:div.field.has-addons - [:div.control - [bind-field - [date-picker-optional - {:event on-change-event - :type "date2" - :placeholder "Start" - :class "is-small" - :field [:start] - :subscription value - :output :text}]]] - [:div.control - [bind-field - [date-picker-optional - {:event on-change-event - :type "date2" - :class "is-small" - :placeholder "End" - :field [:end] - :subscription value - :output :text}]]]]]) + [form-builder/virtual-builder {:value (or value {}) + :on-change (fn [v] + (set-value on-change-event v))} + + [:div + [:div.field.has-addons + [:p.control [:a.button.is-small {:on-click + #(set-value on-change-event + {:start (date->str (t/minus (local-now) (t/period :days 7)) standard) + :end (date->str (local-now) standard)})} + "Week" ]] + [:p.control [:a.button.is-small {:on-click + #(set-value on-change-event + {:start (date->str (t/minus (local-now) (t/period :months 1)) standard) + :end (date->str (local-now) standard)})} + "Month" ]] + [:p.control [:a.button.is-small {:on-click + + #(set-value on-change-event + {:start (date->str (t/minus (local-now) (t/period :years 1)) standard) + :end (date->str (local-now) standard)})} + "Year"]] + [:p.control [:a.button.is-small {:on-click + #(set-value on-change-event nil)} + "All"]]] + [:div.field.has-addons + [:div.control + + [form-builder/raw-field-v2 {:field :start} + [date-picker + {:placeholder "Start" + :class "is-small" + :output :text}]]] + [:div.control + [form-builder/raw-field-v2 {:field :end} + [date-picker + {:class "is-small" + :placeholder "End" + :output :text}]]]]]]) diff --git a/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs b/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs index 88d1b3ed..13338f8d 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs @@ -2,7 +2,6 @@ (:require [auto-ap.forms :as forms] [auto-ap.status :as status] - [auto-ap.subs :as subs] [auto-ap.utils :refer [by]] [auto-ap.views.components.modal :as modal] [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] @@ -10,17 +9,14 @@ [auto-ap.views.utils :refer [dispatch-event with-user]] [clojure.string :as str] [goog.string :as gstring] - [re-frame.core :as re-frame])) - -(re-frame/reg-sub - ::can-submit - (fn [db] - true)) + [re-frame.core :as re-frame] + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components :as com])) (re-frame/reg-event-fx ::try-save [(forms/in-form ::form)] - (fn [{:keys [db]} [_ id ]] + (fn [{:keys [db]} _] (let [{{:keys [ total]} :invoice :keys [expense-accounts]} (:data db) expense-accounts (vals expense-accounts) @@ -33,6 +29,7 @@ %)) (reduce + 0)) does-add-up? (< (Math/abs (- expense-accounts-total (js/parseFloat total))) 0.001)] + (if (and does-add-up? (every? :new-amount expense-accounts)) @@ -44,7 +41,7 @@ (re-frame/reg-event-fx ::save [with-user (forms/in-form ::form)] - (fn [{:keys [db user] } [_ id]] + (fn [{:keys [db user] } _] (let [{{:keys [id]} :invoice :keys [expense-accounts]} (:data db) expense-accounts (vals expense-accounts)] @@ -85,16 +82,10 @@ (fn [db [_ x]] (update-in db [:data :expense-accounts] dissoc x))) -(def change-expense-accounts-form (forms/vertical-form {:submit-event [::try-save] - :change-event [::forms/change ::form] - :can-submit [::can-submit] - :id ::form})) - (defn form [] - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) expense-accounts (:expense-accounts data) {:keys [total] :or {total 0} {:keys [locations] :as client} :client} (:invoice data) - {:keys [form-inline horizontal-field field raw-field error-notification submit-button]} change-expense-accounts-form multi-location? (> (count locations) 1) expense-accounts-total (->> expense-accounts vals @@ -108,72 +99,71 @@ [:div [:div [:a.button.is-outlined {:on-click (dispatch-event [::add-split])} "Add split"]] - (form-inline {} - [:table.table - [:thead - [:tr - [:th {:style {:width "500px"}} "Expense Account"] - (when multi-location? - [:th {:style {:width "200px"}} "Location"]) - [:th {:style {:width "200px"}} "Original Amount"] - [:th {:style {:width "300px"}} "Amount"] - [:th {:style {:width "5em"}}]]] - [:tbody - (doall (for [[id expense-account] expense-accounts] - ^{:key id} - [:tr - [:td.expandable [:div.control - (raw-field - [search-backed-typeahead {:search-query (fn [i] - [:search_account - {:query i - :client-id (:id client)} - [:name :id :location]]) - :type "typeahead-v3" - :field [:expense-accounts id :account]}])]] + [form-builder/builder {:submit-event [::try-save] + :id ::form} + [:table.table + [:thead + [:tr + [:th {:style {:width "500px"}} "Expense Account"] + (when multi-location? + [:th {:style {:width "200px"}} "Location"]) + [:th {:style {:width "200px"}} "Original Amount"] + [:th {:style {:width "300px"}} "Amount"] + [:th {:style {:width "5em"}}]]] + [:tbody + (doall (for [[id _] expense-accounts] + ^{:key id} + [:tr + [:td.expandable [:div.control + [form-builder/raw-field-v2 {:field [:expense-accounts id :account]} + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client-id (:id client)} + [:name :id :location]])}]]]] - (when multi-location? - [:td - (if-let [forced-location (get-in expense-accounts [id :account :location])] - [:div.select - [:select {:disabled "disabled" :value forced-location} [:option {:value forced-location} forced-location]]] - [:div.select - (raw-field - [:select {:type "select" - :field [:expense-accounts id :location] - :spec (set locations)} - (map (fn [l] ^{:key l} [:option {:value l} l]) locations)])])]) - - [:td - (str "$" (get-in expense-accounts [id :amount]))] - [:td - [:div.control - [:div.field.has-addons.is-extended - [:p.control [:a.button.is-static "$"]] - [:p.control - (raw-field - [:input.input {:type "number" - :field [:expense-accounts id :new-amount-temp] - :style {:text-align "right"} - :on-blur (dispatch-event [::forms/change ::form [:expense-accounts id :new-amount] (get-in expense-accounts [id :new-amount-temp])]) - :on-key-down (fn [e ] - (if (= 13 (.-keyCode e)) - (do - (re-frame/dispatch [::forms/change ::form [:expense-accounts id :new-amount] (get-in expense-accounts [id :new-amount-temp])]) - true) - false)) - :max (:total data) - :step "0.01"}])]]]] - [:td [:a.button {:on-click (dispatch-event [::remove-expense-account-split id])} [:i.fa.fa-times]]]])) - [:tr - [:td.no-border { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Invoice total: "] - [:td.no-border { :style { :text-align "right"} } (str (gstring/format "$%.2f" total ) )]] - [:tr - [:td { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Account total: "] - [:td { :style { :text-align "right"} } (str (gstring/format "$%.2f" expense-accounts-total ) )]] - [:tr - [:td.no-border { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Difference: "] - [:td.no-border { :style { :text-align "right"} } (str (gstring/format "$%.2f" (- total expense-accounts-total) ) )]]]])])) + (when multi-location? + [:td + (if-let [forced-location (get-in expense-accounts [id :account :location])] + [:div.select + [:select {:disabled "disabled" :value forced-location} [:option {:value forced-location} forced-location]]] + [:div.select + + [form-builder/raw-field-v2 {:field [:expense-accounts id :location]} + [com/select-field {:options (map (fn [l] [l l]) + locations) + :allow-nil? true}]]])]) + + [:td + (str "$" (get-in expense-accounts [id :amount]))] + [:td + [:div.control + [:div.field.has-addons.is-extended + [:p.control [:a.button.is-static "$"]] + [:p.control + [form-builder/raw-field-v2 {:field [:expense-accounts id :new-amount-temp]} + [:input.input {:type "number" + :style {:text-align "right"} + :on-blur (dispatch-event [::forms/change ::form [:expense-accounts id :new-amount] (get-in expense-accounts [id :new-amount-temp])]) + :on-key-down (fn [e ] + (if (= 13 (.-keyCode e)) + (do + (re-frame/dispatch [::forms/change ::form [:expense-accounts id :new-amount] (get-in expense-accounts [id :new-amount-temp])]) + true) + false)) + :max (:total data) + :step "0.01"}]]]]]] + [:td [:a.button {:on-click (dispatch-event [::remove-expense-account-split id])} [:i.fa.fa-times]]]])) + [:tr + [:td.no-border { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Invoice total: "] + [:td.no-border { :style { :text-align "right"} } (str (gstring/format "$%.2f" total ) )]] + [:tr + [:td { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Account total: "] + [:td { :style { :text-align "right"} } (str (gstring/format "$%.2f" expense-accounts-total ) )]] + [:tr + [:td.no-border { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Difference: "] + [:td.no-border { :style { :text-align "right"} } (str (gstring/format "$%.2f" (- total expense-accounts-total) ) )]]]] + [form-builder/hidden-submit-button]]])) (re-frame/reg-event-fx ::show @@ -184,11 +174,10 @@ :status-from [::status/single ::form] :class "is-primary" :on-click (dispatch-event [::try-save]) - :can-submit [::can-submit] :close-event [::status/completed ::form]}}] :db (-> db - (forms/start-form ::form + (forms/start-form ::form - {:expense-accounts (by :id - (:expense-accounts i)) - :invoice i}))})) + {:expense-accounts (by :id + (:expense-accounts i)) + :invoice i}))})) diff --git a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs index 855dc645..f18c4427 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -1,10 +1,18 @@ (ns auto-ap.views.components.expense-accounts-field (:require - [auto-ap.views.utils :refer [->$ bind-field dispatch-event]] - [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] - [clojure.string :as str] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] + [auto-ap.utils :refer [dollars-0?]] + [auto-ap.views.components :as com] + [auto-ap.views.components.button-radio :as button-radio] + [auto-ap.views.components.level :refer [left-stack]] + [auto-ap.views.components.money-field :refer [money-field]] + [auto-ap.views.components.percentage-field :refer [percentage-field]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.utils :refer [->$ appearing-group]] [goog.string :as gstring] - [re-frame.core :as re-frame])) + [malli.core :as m])) (defn can-replace-with-default? [accounts] (and (or (not (seq accounts)) @@ -46,27 +54,6 @@ ;; EVENTS -(re-frame/reg-event-fx - ::add-expense-account - (fn [_ [_ event expense-accounts locations]] - {:dispatch (conj event (conj expense-accounts - {:amount 0 :id (str "new-" (random-uuid)) - :amount-mode "%" - :amount-percentage 0 - :location (if (= 1 (count locations)) - (first locations) - nil)}))})) - -(re-frame/reg-event-fx - ::remove-expense-account - (fn [_ [_ event expense-accounts id]] - {:dispatch (conj event (transduce (filter - (fn [ea] - (not= (:id ea) id)) ) - conj - [] - expense-accounts))})) - (defn recalculate-amounts [expense-accounts total] (mapv (fn [ea] @@ -76,143 +63,116 @@ (* (/ (js/parseFloat (:amount-percentage ea)) 100.0) total))))) expense-accounts)) -(re-frame/reg-event-fx - ::spread-evenly - (fn [_ [_ event expense-accounts max-value]] - {:dispatch (into event [(recalculate-amounts (mapv - (fn [ea] - (assoc ea :amount-percentage (js/parseFloat - (goog.string/format "%.2f" - (* 100 (/ 1 (count expense-accounts))))))) - expense-accounts) - max-value)])})) -(re-frame/reg-event-fx - ::expense-account-changed - (fn [_ [_ event expense-accounts max-value field value]] - (let [updated-accounts (cond-> expense-accounts - true (assoc-in field value) - (= (list :account) (drop 1 field)) (assoc-in [(first field) :location] nil) - - (= (list :amount-percentage) (drop 1 field)) (assoc-in [(first field) :amount] - (js/parseFloat - (goog.string/format "%.2f" - (* (/ (cond-> value - (not (float? value)) (js/parseFloat )) 100.0) - (cond-> max-value - (not (float? max-value)) (js/parseFloat))))))) - updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])] - (assoc-in updated-accounts [(first field) :location] location) - updated-accounts)] - {:dispatch (into event [updated-accounts])}))) - - ;; VIEWS -(defn expense-accounts-field [{expense-accounts :value client :client max-value :max locations :locations event :event descriptor :descriptor disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}] - [:div - [:div.columns - [:div.column - [:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"] + + +(def schema (m/schema [:sequential [:map + [:id :string] + [:account schema/reference] + [:location schema/not-empty-string] + [:amount schema/money]]])) + + + +(defn expense-accounts-field-v2 [{value :value on-change :on-change expense-accounts :value client :client max-value :max locations :locations disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}] + [form-builder/virtual-builder {:value value + :schema schema + :on-change (fn [expense-accounts original-expense-accounts] + (let [updated-expense-accounts + (for [[before-account after-account] (map vector (concat original-expense-accounts + (repeat nil)) expense-accounts)] + (cond-> after-account + (not= (:id (:account before-account)) + (:id (:account after-account))) + (assoc :location nil) + + (not= (:amount-percentage before-account) + (:amount-percentage after-account)) + (assoc :amount (* (/ (:amount-percentage after-account) 100.0) + max-value)) + + (:location (:account after-account)) + (assoc :location (:location (:account after-account)))))] + (on-change (into [] updated-expense-accounts))))} + [:div + [:div.tags + (when max-value + [:div.tag "To Allocate: " (->$ max-value)]) + (when-not percentage-only? - [:p.help "Remaining " (->$ (- max-value (reduce + 0 (map (comp js/parseFloat :amount) expense-accounts))))])] - [:div.column.is-narrow - (when-not disabled - [:p.buttons - [:a.button {:on-click (dispatch-event [::spread-evenly event expense-accounts max-value])} "Spread evenly"] - [:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts locations])} "Add"]])]] + (let [total (reduce + 0 (map (or :amount 0.0) expense-accounts))] + [:<> + [:div.tag "Total: " (->$ total) ] + [:div.tag {:class (if (dollars-0? (- max-value total)) + ["is-primary" "is-light"] + ["is-danger" "is-light"])} + "Remaining: " (->$ (- max-value total))]]))] - (for [[index {:keys [account id location amount amount-percentage amount-mode] :as expense-account}] (map vector (range) expense-accounts)] - ^{:key id} - [:div.box - [:div.columns - [:div.column - [:h1.subtitle.is-6 (cond (and account (not percentage-only?)) - (str (:name account) " - " - location ": " - (gstring/format "$%.2f" (or amount 0) )) - - account - (str (:name account) " - " - location ": %" - amount-percentage) - - :else - [:i "New " descriptor])]] - [:div.column.is-narrow - (when-not disabled - [:a.delete {:on-click (dispatch-event [::remove-expense-account event expense-accounts id])}])]] - - [:div.field - [:div.columns - [:div.column - [:p.help "Account"] - [:div.control.is-fullwidth - [bind-field - ^{:key (:id client)} - [search-backed-typeahead {:search-query (fn [i] - [:search_account - {:query i - :client-id (:id client)} - [:name :id :location]]) - :type "typeahead-v3" - :field [index :account] - - :disabled disabled - :event [::expense-account-changed event expense-accounts max-value] - :subscription expense-accounts}]]]] - [:div.column.is-narrow - [:p.help "Location"] - [:div.control - (if-let [forced-location (:location account)] - [:div.select - [:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]] - [:div.select - [bind-field - [:select {:type "select" - :disabled (boolean (or (:location account) - disabled)) - :style {:width "5em"} - :field [index :location] - :allow-nil? true - :spec (set locations) - :event [::expense-account-changed event expense-accounts max-value] - :subscription expense-accounts} - (map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]]]] - - [:div.field - [:p.help "Amount"] - [:div.control - [:div.field.has-addons.is-extended - [:p.control [:span.select - [bind-field - [:select {:type "select" - :disabled (or disabled percentage-only?) - :field [index :amount-mode] - :allow-nil? false - :event [::expense-account-changed event expense-accounts max-value] - :subscription expense-accounts} - [:option "$"] - [:option "%"]]]]] - [:p.control - (if (= "$" amount-mode) - [bind-field - [:input.input {:type "number" - :field [index :amount] - :style {:text-align "right" :width "7em"} - :event [::expense-account-changed event expense-accounts max-value] - :disabled disabled - :subscription expense-accounts - :precision 2 - :value (get-in expense-account [:amount]) - :max max-value - :step "0.01"}]] - [bind-field - [:input.input {:type "number" - :field [index :amount-percentage] - :style {:text-align "right" :width "7em"} - :disabled disabled - :event [::expense-account-changed event expense-accounts max-value] - :precision 2 - :subscription expense-accounts - :value (get-in expense-account [:amount-percentage]) - :max "100" - :step "0.01"}]])]]]]])]) + (into [appearing-group] + (for [[index {:keys [account id amount amount-mode]}] (map vector (range) expense-accounts)] + ^{:key id} + [:div.card {:style {:margin-bottom "2em"}} + [:div.card-header + [:p.card-header-title "Expense Account"] + (when-not disabled + [:div.card-header-icon {:on-click (fn [] + (on-change (into [] (filter #(not= id (:id %)) expense-accounts))))} + [:a.delete ]])] + [:div.card-content + [:div.field + [:div.columns + [:div.column + [:div.control.is-fullwidth + [form-builder/field-v2 {:required? true + :field [index :account]} + "Account" + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client-id (:id client)} + [:name :id :location]]) + :disabled disabled}]]]] + [:div.column.is-narrow + [form-builder/field-v2 {:required? true + :field [index :location]} + "Location" + [com/select-field {:options (if (:location account) + [[(:location account) (:location account)]] + (map (fn [l] [l l]) + locations)) + :disabled (boolean (:location account)) + :allow-nil? true}]]]]] + (if percentage-only? + [form-builder/raw-field-v2 {:field [index :amount-percentage]} + [percentage-field {}]] + [left-stack + [:div.field.has-addons + [form-builder/raw-field-v2 {:field [index :amount-mode]} + [button-radio/button-radio {:options [["$" "Amount"] + ["%" "Percent"]]}]] + (if (= "$" amount-mode) + [form-builder/raw-field-v2 {:field [index :amount]} + [money-field {}] + ] + [form-builder/raw-field-v2 {:field [index :amount-percentage]} + [percentage-field {}]])] + (when (= "%" amount-mode) + [:div.tag.is-primary.is-light (gstring/format "$%.2f" (or amount 0) )])])]])) + (when-not disabled + [:p.buttons + [:a.button {:on-click (fn [] + (on-change + (recalculate-amounts (mapv + (fn [ea] + (assoc ea :amount-percentage (* 100.0 (/ 1 (count expense-accounts))))) + expense-accounts) + max-value)) + )} "Spread evenly"] + [:a.button {:on-click + (fn [] + (on-change (conj value {:id (str "new-" (random-uuid)) + :amount-mode "%" + :location (if (= 1 (count locations)) + (first locations) + nil)})))} + "Add"]])]]) diff --git a/src/cljs/auto_ap/views/components/invoice_table.cljs b/src/cljs/auto_ap/views/components/invoice_table.cljs index 67b57ad8..ae8ebee6 100644 --- a/src/cljs/auto_ap/views/components/invoice_table.cljs +++ b/src/cljs/auto_ap/views/components/invoice_table.cljs @@ -20,28 +20,31 @@ [auto-ap.views.pages.data-page :as data-page])) (defn data-params->query-params [params] - {:exact-match-id (some-> params :exact-match-id str) - :start (:start params 0) - :sort (:sort params) - :per-page (:per-page params) + (if (:exact-match-id params) + {:exact-match-id (some-> params :exact-match-id str) + :client-id (:id @(re-frame/subscribe [::subs/client]))} + {:exact-match-id (some-> params :exact-match-id str) + :start (:start params 0) + :sort (:sort params) + :per-page (:per-page params) - :vendor-id (:id (:vendor params)) - :date-range (:date-range params) - :due-range (:due-range params) - :amount-gte (:amount-gte (:amount-range params)) - :amount-lte (:amount-lte (:amount-range params)) - :location (:location params) - :unresolved (:unresolved params) - :scheduled-payments (:scheduled-payments params) - :invoice-number-like (:invoice-number-like params) - :client-id (:id @(re-frame/subscribe [::subs/client])) - :import-status (:import-status params) - :status (condp = @(re-frame/subscribe [::subs/active-page]) - :invoices nil - :import-invoices nil - :unpaid-invoices :unpaid - :paid-invoices :paid - :voided-invoices :voided)}) + :vendor-id (:id (:vendor params)) + :date-range (:date-range params) + :due-range (:due-range params) + :amount-gte (:amount-gte (:amount-range params)) + :amount-lte (:amount-lte (:amount-range params)) + :location (:location params) + :unresolved (:unresolved params) + :scheduled-payments (:scheduled-payments params) + :invoice-number-like (:invoice-number-like params) + :client-id (:id @(re-frame/subscribe [::subs/client])) + :import-status (:import-status params) + :status (condp = @(re-frame/subscribe [::subs/active-page]) + :invoices nil + :import-invoices nil + :unpaid-invoices :unpaid + :paid-invoices :paid + :voided-invoices :voided)})) (defn query [params] {:venia/queries [[:invoice_page diff --git a/src/cljs/auto_ap/views/components/layouts.cljs b/src/cljs/auto_ap/views/components/layouts.cljs index 367307de..0ab3265e 100644 --- a/src/cljs/auto_ap/views/components/layouts.cljs +++ b/src/cljs/auto_ap/views/components/layouts.cljs @@ -3,44 +3,53 @@ [auto-ap.events :as events] [auto-ap.routes :as routes] [auto-ap.subs :as subs] - [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]] [auto-ap.views.components.modal :as modal] [auto-ap.views.components.vendor-dialog :as vendor-dialog] [auto-ap.views.utils - :refer [active-when appearing bind-field dispatch-event dispatch-event-with-propagation login-url]] + :refer [active-when + appearing + dispatch-event-with-propagation + login-url]] [bidi.bidi :as bidi] [clojure.string :as str] [re-frame.core :as re-frame] - [reagent.core :as r])) + [reagent.core :as r] + [auto-ap.forms.builder :as form-builder] + [vimsical.re-frame.cofx.inject :as inject] + [auto-ap.forms :as forms])) -(defn navbar-drop-down-contents [{:keys [id]} children ] - (let [toggle-fn (fn [] (re-frame/dispatch [::events/toggle-menu id]))] - (r/create-class {:component-did-mount (fn [] (.addEventListener js/document "click" toggle-fn)) - :component-will-unmount (fn [] (.removeEventListener js/document "click" toggle-fn)) - :reagent-render - (fn [children] - children)}))) - -(defn navbar-drop-down [{:keys [ header id class]} child] - (r/create-class - {:reagent-render (fn [{:keys [header id]} child] - (let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])] - [:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class)} - [:a {:class "navbar-link login" :on-click (fn [e] - (.preventDefault e) - (.stopPropagation e) - (re-frame/dispatch [::events/toggle-menu id]) - true)} header] - [appearing {:visible? menu-active? :enter-class "appear" :exit-class "disappear" :timeout 200} - [:div {:class "navbar-dropdown"} - [navbar-drop-down-contents {:id id} - [:div child]]]]]))})) +(defn navbar-drop-down [{:keys [ class]} _] + (let [!child (r/atom nil)] + (r/create-class + {:reagent-render (fn [{:keys [header id]} child] + (let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])] + [:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class) + :ref (fn [n] + (reset! !child n)) + :tab-index 0 + :onBlur (fn [e] + (js/setTimeout (fn [] + (println @!child) + (println (.-activeElement js/document)) + (when-not (.contains @!child (.-activeElement js/document)) + (re-frame/dispatch [::events/toggle-menu id]))) + 2)) + } + [:a {:class "navbar-link login" :on-click (fn [e] + (.preventDefault e) + (.stopPropagation e) + (re-frame/dispatch [::events/toggle-menu id]) + true)} header] + [appearing {:visible? menu-active? :enter-class "appear" :exit-class "disappear" :timeout 200} + [:div {:class "navbar-dropdown"} + [:div child]]]]))}))) (defn login-dropdown [] (let [user (re-frame/subscribe [::subs/user])] (if @user [navbar-drop-down {:header [:span [:span.icon [:i.fa.fa-user] ] - [:span (:user/name @user)]] :id ::account} + [:span (:user/name @user)]] + :id ::account} [:div [:a {:class "navbar-item" :href (bidi/path-for routes/routes :reports)} "My company"] @@ -60,8 +69,8 @@ (re-frame/reg-sub ::matching-clients :<- [::subs/clients] - :<- [::client-search] - (fn [[clients {client-search :value}]] + :<- [::forms/form ::client-search] + (fn [[clients {{client-search :value} :data}]] (if (empty? client-search) clients (if-let [exact-match (first (filter @@ -76,21 +85,46 @@ (str/includes? (str/lower-case (:name client)) (str/lower-case client-search)))) clients))))) -(re-frame/reg-event-db - ::client-search-changed - [(re-frame/path [::client-search])] - (fn [client-search [_ path value]] - (assoc-in client-search path value))) + +(re-frame/reg-event-fx + ::client-searched + [(re-frame/inject-cofx ::inject/sub [::matching-clients])] + (fn [{::keys [matching-clients]}] + {:dispatch-n [[::events/swap-client (first matching-clients)] + [::events/toggle-menu ::select-client]]})) + +(defn client-dropdown [] + (let [client (re-frame/subscribe [::subs/client]) + matching-clients @(re-frame/subscribe [::matching-clients])] + + [navbar-drop-down {:header (str "Company: " (if @client (:name @client) + "All")) + :id ::select-client} + [:div + [:a {:class "navbar-item" + :on-click (fn [] + (re-frame/dispatch [::events/toggle-menu ::select-client]) + (re-frame/dispatch [::forms/form-closing ::client-search]) + (re-frame/dispatch [::events/swap-client nil]))} "All" ] + [:hr {:class "navbar-divider"}] + [form-builder/builder {:id ::client-search + :submit-event [::client-searched]} + [form-builder/raw-field-v2 {:field :value} + [:input.input.navbar-item {:placeholder "Company name" + :auto-focus true}]]] + (for [{:keys [name id] :as client} (take 8 matching-clients)] + ^{:key id } + [:a {:class "navbar-item" + :on-click (fn [] + (re-frame/dispatch [::events/toggle-menu ::select-client]) + (re-frame/dispatch [::events/swap-client client])) + } name])]])) (defn navbar [ap] (let [navbar-menu-shown? (r/atom false)] (fn [ap] - (let [user (re-frame/subscribe [::subs/user]) - client (re-frame/subscribe [::subs/client]) - clients (re-frame/subscribe [::subs/clients]) - matching-clients @(re-frame/subscribe [::matching-clients]) - menu (re-frame/subscribe [::subs/menu]) - client-search @(re-frame/subscribe [::client-search]) + (let [user (re-frame/subscribe [::subs/user]) + clients (re-frame/subscribe [::subs/clients]) is-initial-loading @(re-frame/subscribe [::subs/is-initial-loading?])] [:nav {:class "navbar has-shadow is-fixed-top is-grey"} @@ -133,33 +167,8 @@ [:div.navbar-end (when (> (count @clients) 1) - [navbar-drop-down {:header (str "Company: " (if @client (:name @client) - "All")) - :id ::select-client} - [:div - [:a {:class "navbar-item" - :on-click (fn [] - (re-frame/dispatch [::events/swap-client nil]))} "All" ] - [:hr {:class "navbar-divider"}] - [bind-field - [:input.input.navbar-item {:placeholder "Company name" - :auto-focus true - :field [:value] - :on-key-up (fn [k] - (when (= 13 (.-which k)) - (do - (re-frame/dispatch [::events/swap-client (first matching-clients)]) - (re-frame/dispatch [::events/toggle-menu ::select-client]) - (re-frame/dispatch [::client-search-changed [:value] nil]))) - ) - :event [::client-search-changed] - :subscription client-search}]] - (for [{:keys [name id] :as client} matching-clients] - ^{:key id } - [:a {:class "navbar-item" - :on-click (fn [] - (re-frame/dispatch [::events/swap-client client])) - } name])]])])] + [client-dropdown] + )])] (when-not is-initial-loading [login-dropdown])] @@ -167,11 +176,7 @@ ])))) -(defn footer [] - [:footer {:style {:padding "1em"}} - [:div {:class "content has-text-centered"} - [:p - [:strong "Integreat"] ]]]) + (defn appearing-side-bar [{:keys [visible?]} ] [appearing {:visible? visible? :enter-class "slide-in-right" :exit-class "slide-out-right" :timeout 500} @@ -179,7 +184,7 @@ (into [:div.sub-main {} ] (r/children (r/current-component)))]]) -(defn side-bar-layout [{:keys [side-bar main ap bottom right-side-bar]}] +(defn side-bar-layout [{:keys [side-bar main bottom right-side-bar]}] (let [ap @(re-frame/subscribe [::subs/active-page]) client @(re-frame/subscribe [::subs/client])] [:div @@ -200,7 +205,6 @@ (when right-side-bar right-side-bar) ] - #_[footer] [:div bottom] [:div#dz-hidden]])) diff --git a/src/cljs/auto_ap/views/components/money_field.cljs b/src/cljs/auto_ap/views/components/money_field.cljs index 954371df..b3079671 100644 --- a/src/cljs/auto_ap/views/components/money_field.cljs +++ b/src/cljs/auto_ap/views/components/money_field.cljs @@ -5,7 +5,7 @@ [react :as react])) (def good-$ #"^\-?[0-9]+(\.[0-9][0-9])?$") -(defn -money-field [{:keys [min max disabled on-change value class style]}] +(defn -money-field [{:keys [min max disabled on-blur on-change value class style placeholder]}] (let [[ parsed-amount set-parsed-amount] (react/useState {:parsed value :raw (cond (str/blank? value) @@ -40,6 +40,7 @@ [:div.control.has-icons-left [:input.input {:type "text" :disabled disabled + :placeholder placeholder :class class :on-change (fn [e] (let [raw (.. e -target -value) @@ -58,7 +59,9 @@ (set-parsed-amount {:raw "" :parsed nil}) - (on-change nil))) + (on-change nil)) + (when on-blur + (on-blur))) :min min :max max :step "0.01" @@ -69,3 +72,6 @@ (defn money-field [] [:f> -money-field (r/props (r/current-component))]) + +(defn field [] + [:f> -money-field (r/props (r/current-component))]) diff --git a/src/cljs/auto_ap/views/components/multi.cljs b/src/cljs/auto_ap/views/components/multi.cljs new file mode 100644 index 00000000..6d45ba95 --- /dev/null +++ b/src/cljs/auto_ap/views/components/multi.cljs @@ -0,0 +1,79 @@ +(ns auto-ap.views.components.multi + (:require + [cemerick.url] + #_{:clj-kondo/ignore [:unused-namespace]} + [reagent.core :as reagent] + [react :as react] + [auto-ap.entities.shared :as shared] + [auto-ap.views.utils :refer [appearing-group]] + [auto-ap.forms.builder :as form-builder])) + + +;; TODO just embrace the fact that it will need to be remounted, and use index based keys +(defn multi-field-v2-internal [{:keys [template key-fn allow-change? disable-new? disable-remove? schema on-change disabled new-text] prop-value :value :as props} ] + (let [prop-value (if (seq prop-value) + (vec prop-value) + [])] + [form-builder/virtual-builder {:value prop-value + :schema schema + :on-change on-change} + [:fieldset {:disabled disabled} + [:div {:style {:margin-bottom "0.25em"}} + (into [(if key-fn + appearing-group + :<>)] + (for [[i override] (map vector (range) prop-value) + :let [extant? (if key-fn + (boolean (key-fn override)) + true) + is-disabled? (boolean (and (= false allow-change?) + extant?)) + key (or (when key-fn (key-fn override)) + (::key override) + i)]] + ^{:key key} + [form-builder/with-scope {:scope [i]} + [:div.level {:style {:margin-bottom "0.25em"}} + [:div.level-left {:style {:padding "0.5em 1em"} + :class (when-not extant? + "has-background-info-light")} + (let [template (if (fn? template) + (template override) + template)] + [:fieldset.level-left {:disabled is-disabled? + :style {:padding "0.5em 1em"} + :class (when-not extant? + "has-background-info-light")} + (for [[idx template] (map vector (range ) template)] + ^{:key idx} + [:div.level-item + template]) + (when-not (and disable-remove? + extant?) + [:div.level-item + [:a.button.level-item + {:disabled is-disabled? + :on-click (fn [] + (on-change (into [] + (for [[idx item] (map vector (range) prop-value) + :when (not= idx i)] + item))))} + [:span.icon [:span.icon-remove]]]])]) + ]]])) + (when-not disable-new? + [:button.button.is-outline + {:type "button" + :on-click (fn [e] + (println "ADDING" prop-value) + (on-change (conj prop-value {::key (random-uuid)})) + (.stopPropagation e) + (.preventDefault e))} + [:span.icon [:i.fa.fa-plus]] + [:span (or new-text "New")]])]]])) + + +(defn multi-field-v2 [] + (into + [:f> multi-field-v2-internal + (reagent/props (reagent/current-component))] + (reagent/children (reagent/current-component)))) diff --git a/src/cljs/auto_ap/views/components/number-filter b/src/cljs/auto_ap/views/components/number-filter deleted file mode 100644 index e9286807..00000000 --- a/src/cljs/auto_ap/views/components/number-filter +++ /dev/null @@ -1,43 +0,0 @@ -(ns auto-ap.views.components.number-filter - (:require - [clojure.spec.alpha :as s] - [auto-ap.entities.invoice :as invoice] - [auto-ap.views.components.typeahead :refer [typeahead]] - [auto-ap.views.utils :refer [bind-field date-picker date->str local-now standard]] - [cljs-time.core :as t] - [re-frame.core :as re-frame])) - -(defn dispatch-change [on-change-event start end] - (fn [_] - (re-frame/dispatch (into on-change-event [[:start] start]) ) - (re-frame/dispatch (into on-change-event [[:end] end])))) - -(defn number-filter [{:keys [value on-change-event]}] - [:div - [:div.field.has-addons - [:div.control - [bind-field - [date-picker {:class-name "input is-fullwidth" - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder-text "Start" - :next-month-button-label "" - :next-month-label "" - :event on-change-event - :type "date" - :field [:start] - :subscription value}]]] - [:div.control - [bind-field - [date-picker {:class-name "input is-fullwidth" - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder-text "End" - :next-month-button-label "" - :event on-change-event - :next-month-label "" - :type "date" - :field [:end] - :subscription value}]]]]]) diff --git a/src/cljs/auto_ap/views/components/number.cljs b/src/cljs/auto_ap/views/components/number.cljs new file mode 100644 index 00000000..bb31e30c --- /dev/null +++ b/src/cljs/auto_ap/views/components/number.cljs @@ -0,0 +1,44 @@ +(ns auto-ap.views.components.number + (:require [react :as react] + [reagent.core :as r])) + + +(defn number-internal [props] + + (let [[text set-text ] (react/useState (some-> props :value str)) + [value set-value ] (react/useState (some-> props :value)) + coerce-value (fn [new-value] + (let [new-value (js/parseInt new-value)] + (cond + (nil? new-value) + nil + + (js/Number.isNaN new-value) + nil + + :else + new-value)))] + (react/useEffect (fn [] + (let [prop-value (:value props)] + (when (not (= prop-value value)) + (set-value prop-value) + (if prop-value + (set-text (str prop-value)) + (set-text "")))))) + [:div.field.has-addons + [:div.control + [:input.input (assoc props + :on-change (fn [e] + ((:on-change props) + (coerce-value (.. e -target -value)))) + :value text + :type "number" + :step "1" + :style (or (:style props) + {:width "5em"}) + :size 3)]]])) + +(defn number-input [] + [:f> number-internal + (r/props (r/current-component))]) + diff --git a/src/cljs/auto_ap/views/components/number_filter.cljs b/src/cljs/auto_ap/views/components/number_filter.cljs index 0e371001..e90ecfb0 100644 --- a/src/cljs/auto_ap/views/components/number_filter.cljs +++ b/src/cljs/auto_ap/views/components/number_filter.cljs @@ -1,34 +1,21 @@ (ns auto-ap.views.components.number-filter (:require - [clojure.spec.alpha :as s] - [auto-ap.entities.invoice :as invoice] - [auto-ap.views.utils :refer [bind-field date-picker date->str local-now standard]] - [cljs-time.core :as t] - [re-frame.core :as re-frame])) - -(defn dispatch-change [on-change-event start end] - (fn [_] - (re-frame/dispatch (into on-change-event [[:start] start]) ) - (re-frame/dispatch (into on-change-event [[:end] end])))) + [re-frame.core :as re-frame] + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components :as com])) (defn number-filter [{:keys [value on-change-event]}] - [:div.field - [:div.control - [:div.columns - [:div.column - [bind-field - [:input.input {:type "number" - :placeholder ">=" - :field [:amount-gte] - :step "0.01" - :event on-change-event - :subscription value}]]] - [:div.column - [bind-field - [:input.input {:type "number" - :placeholder "<=" - :field [:amount-lte] - :event on-change-event - :step "0.01" - :subscription value}]]]]]] + [form-builder/virtual-builder {:value (or value {}) + :on-change (fn [v] + (re-frame/dispatch (conj on-change-event v)))} + + [:div.columns + [:div.column + [:div.control + [form-builder/raw-field-v2 {:field :amount-gte} + [com/money-input {:placeholder ">="}]]]] + [:div.column + [:div.control + [form-builder/raw-field-v2 {:field :amount-lte} + [com/money-input {:placeholder "<="}]]]]]] ) diff --git a/src/cljs/auto_ap/views/components/percentage_field.cljs b/src/cljs/auto_ap/views/components/percentage_field.cljs new file mode 100644 index 00000000..4a43dee6 --- /dev/null +++ b/src/cljs/auto_ap/views/components/percentage_field.cljs @@ -0,0 +1,74 @@ +(ns auto-ap.views.components.percentage-field + (:require [reagent.core :as r] + [auto-ap.views.utils :refer [->short$]] + [clojure.string :as str] + [react :as react])) +(def good-% #"^\d{1,3}$") + +(defn -percentage-field [{:keys [min max disabled on-blur on-change value class style placeholder]}] + (let [[ parsed-amount set-parsed-amount] (react/useState {:parsed value + :raw (cond + (str/blank? value) + "" + + (js/Number.isNaN (js/parseInt value)) + "" + + :else + (str (js/parseInt value)))})] + (react/useEffect (fn [] + ;; allow the controlling field to change the raw representation + ;; when the raw amount is a valid representation, so that 33. + ;; doesn't get unset + (when (or + (and (:raw parsed-amount) + (re-find good-% (:raw parsed-amount))) + (str/blank? (:raw parsed-amount))) + (set-parsed-amount + (assoc parsed-amount + :parsed value + :raw (cond + (str/blank? value) + "" + + (js/Number.isNaN (js/parseInt value)) + "" + + :else + (str (js/parseInt value)))))) + nil)) + [:div.control.has-icons-left + [:input.input {:type "text" + :disabled disabled + :placeholder placeholder + :class class + :on-change (fn [e] + (let [raw (.. e -target -value) + new-value (when (and raw + (not (str/blank? raw)) + (re-find good-% raw)) + (js/parseFloat raw))] + (set-parsed-amount {:raw raw + :parsed new-value}) + (when (not= value new-value) + (on-change new-value)))) + :value (or (:raw parsed-amount) + "") + :on-blur (fn [] + (when-not (re-find good-% (:raw parsed-amount)) + + (set-parsed-amount {:raw "" + :parsed nil}) + (on-change nil)) + (when on-blur + (on-blur))) + :min min + :max max + :step "0.01" + :style (or style + {:width "8em"})}] + [:span.icon.is-left + [:i.fa.fa-percent]]])) + +(defn percentage-field [] + [:f> -percentage-field (r/props (r/current-component))]) diff --git a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs index 3768e867..a3b23064 100644 --- a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs +++ b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs @@ -3,12 +3,34 @@ [downshift :as ds :refer [useCombobox]] [re-frame.core :as re-frame] [auto-ap.views.utils :refer [with-user]] + [react-popper :refer [usePopper] :as react-popper] + [reagent.core :as r] [clojure.string :as str] [react :as react])) (set! *warn-on-infer* true) +(defn popper-test-internal [props children] + (let [[reference-element set-reference-element] (react/useState nil) + [popper-element set-popper-element] (react/useState nil) + ^js/Popper use (usePopper reference-element popper-element + #js {:placement "bottom-start" + :strategy "fixed"}) + popper-props (into {:ref set-popper-element + :style (assoc (js->clj (.. use -styles -popper)) + :z-index "1000") + } + (js->clj (.. use -attributes -popper)))] + [:<> + [:div {:ref set-reference-element}] + [:div popper-props + (into [:div {:class (:class props)}] children)]])) + +(defn popper [] + [:f> popper-test-internal (r/props (r/current-component)) + (r/children (r/current-component))]) + (re-frame/reg-event-fx ::search-completed (fn [_ [_ set-items set-loading-status result]] @@ -61,9 +83,8 @@ :time 250 :key ::input-value-settled}}))) -(defn typeahead-v3-internal [{:keys [class entity->text entities on-input-change style ^js on-change disabled value name auto-focus] :or {disabled false} - prop-value :value - :as i}] +(defn typeahead-v3-internal [{:keys [class entity->text entities on-input-change style ^js on-change disabled value name auto-focus on-blur] :or {disabled false} + prop-value :value}] (let [[items set-items] (react/useState (or entities [])) [focused set-focus] (react/useState (boolean auto-focus)) @@ -113,13 +134,16 @@ focused (conj "is-focused") - )} + ) + } (when selectedItem ^{:key "hidden"} [:div.level-item [:div.control [:div.tags.has-addons - [:span.tag (:name selectedItem)] + [:span.tag (if entity->text + (entity->text selectedItem) + (:name selectedItem))] (when name [:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}]) (when-not disabled @@ -142,22 +166,25 @@ :disabled disabled :onFocus #(set-focus true) - :onBlur #(set-focus false) + :onBlur #(do (set-focus false) + (when on-blur + (on-blur))) :autoFocus (if auto-focus "autoFocus" "")}))]]] - [:div {:class (when (and isOpen (seq items)) - "typeahead-menu")} - [:ul (js->clj (getMenuProps)) - (when (and isOpen (seq items)) - (for [[index item] (map vector (range) (js->clj items :keywordize-keys true))] - ^{:key item} - [:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index})) - :class (when (= index highlightedIndex) - "typeahead-highlighted")) - (if entity->text - (entity->text item) - (:name item))]))]]]])) + [:div (js->clj (getMenuProps)) + (when (and isOpen (seq items)) + [popper {:class "typeahead-menu"} + [:ul + (when (and isOpen (seq items)) + (for [[index item] (map vector (range) (js->clj items :keywordize-keys true))] + ^{:key item} + [:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index})) + :class (when (= index highlightedIndex) + "typeahead-highlighted")) + (if entity->text + (entity->text item) + (:name item))]))]])]]])) (defn search-backed-typeahead [{:keys [search-query] :as props}] [:div @@ -174,11 +201,10 @@ :on-input-change (fn [input set-items] (if entities-by-id - (do - (set-items - (into-array - (->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} ) - (take 10))))) + (set-items + (into-array + (->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} ) + (take 10)))) (set-items (into-array (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "") diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index d4ecd63b..d6e7fbe3 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -1,33 +1,72 @@ (ns auto-ap.views.components.vendor-dialog (:require - [auto-ap.entities.contact :as contact] - [auto-ap.entities.vendors :as entity] [auto-ap.forms :as forms] [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] [auto-ap.status :as status] - [auto-ap.views.components.level :refer [left-stack]] [auto-ap.subs :as subs] + [auto-ap.views.components :as com] [auto-ap.views.components.address :refer [address2-field]] + [auto-ap.views.components.level :refer [left-stack]] [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.multi :refer [multi-field-v2]] + [auto-ap.views.components.number :refer [number-input]] [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] [auto-ap.views.pages.admin.vendors.common :as common] [auto-ap.views.utils - :refer [dispatch-event multi-field str->int with-is-admin? with-user]] - [clojure.spec.alpha :as s] + :refer [dispatch-event str->int with-is-admin? with-user]] + [malli.core :as m] [re-frame.core :as re-frame] [reagent.core :as r])) ;; Remaining cleanup todos: ;; test minification -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::vendor-form] - (fn [form] - (s/valid? ::entity/vendor (:data form)))) +(def terms-override-schema (m/schema [:map + [:client schema/reference] + [:terms :int]])) +(def automatically-paid-schema (m/schema [:map + [:client schema/reference]])) + +(def schedule-payment-dom-schema (m/schema [:map + [:client schema/reference] + [:dom [:int {:max 30}]]])) + +(def account-override-schema (m/schema [:map + [:client schema/reference] + [:account schema/reference]])) + +(def schema (m/schema [:map [:name schema/not-empty-string] + [:print-as {:optional true} + [:maybe :string]] + [:hidden {:optional true} + [:maybe :boolean]] + [:terms {:optional true} + [:maybe :int]] + [:terms-overrides {:optional true} + [:maybe [:sequential terms-override-schema]]] + [:schedule-payment-dom {:optional true} + [:maybe [:sequential schedule-payment-dom-schema]]] + [:default-account schema/reference] + [:account-overrides {:optional true} + [:sequential account-override-schema]] + [:legal-entity-first-name {:optional true} + [:maybe :string]] + [:legal-entity-middle-name {:optional true} + [:maybe :string]] + [:legal-entity-last-name {:optional true} + [:maybe :string]] + [:legal-entity-tin {:optional true} + [:maybe :string]] + [:legal-entity-tin-type {:optional true} + [:or [:maybe :string] + [:maybe keyword?]]] + [:legal-entity-1099-type {:optional true} + [:or [:maybe :string] + [:maybe keyword?]]]])) (re-frame/reg-event-fx ::save-complete @@ -39,13 +78,13 @@ ::save [with-user with-is-admin? (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)] (fn [{:keys [user is-admin?] {{:keys [name hidden print-as terms invoice-reminder-schedule primary-contact automatically-paid-when-due schedule-payment-dom secondary-contact address default-account terms-overrides account-overrides id legal-entity-tin legal-entity-tin-type legal-entity-first-name legal-entity-last-name legal-entity-middle-name legal-entity-1099-type] :as data} :data} :db} _] - (when (s/valid? ::entity/vendor data) + (if (m/validate schema data) (let [query [:upsert-vendor {:vendor (cond-> {:id id :name name :print-as print-as - :terms (or (str->int terms) - nil) + :terms (or terms + nil) :default-account-id (:id default-account) :address address :primary-contact primary-contact @@ -75,12 +114,11 @@ (comp :id :client) automatically-paid-when-due) :legal-entity-first-name legal-entity-first-name - :legal-entity-middle-name legal-entity-middle-name + :legal-entity-middle-name legal-entity-middle-name :legal-entity-last-name legal-entity-last-name :legal-entity-tin legal-entity-tin :legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword) - :legal-entity-1099-type (some-> legal-entity-1099-type clojure.core/name not-empty keyword) - ))} + :legal-entity-1099-type (some-> legal-entity-1099-type clojure.core/name not-empty keyword)))} common/default-read]] { :graphql {:token user @@ -88,7 +126,10 @@ :query-obj {:venia/operation {:operation/type :mutation :operation/name "UpsertVendor"} :venia/queries [{:query/data query}]} - :on-success [::save-complete]}})))) + :on-success [::save-complete]}}) + + {:dispatch-n [[::forms/attempted-submit ::vendor-form] + [::status/error ::vendor-form [{:message "Please fix the errors and try again."}]]]}))) (defn pull-left [] (into [:div {:style {:position "relative" @@ -103,10 +144,8 @@ [form-builder/vertical-control {:is-small? true} "Name" [:div.control.has-icons-left - [form-builder/raw-field - [:input.input.is-expanded {:type "text" - :field :name - :spec ::contact/name}]] + [form-builder/raw-field-v2 {:field :name} + [:input.input.is-expanded {:type "text"}]] [:span.icon.is-small.is-left [:i.fa.fa-user]]]] [form-builder/vertical-control {:is-small? true} @@ -115,201 +154,165 @@ [:div.control.has-icons-left [:span.icon.is-small.is-left [:i.fa.fa-envelope]] - [form-builder/raw-field - [:input.input {:type "email" - :field :email - :spec ::contact/email}]]]] + [form-builder/raw-field-v2 {:field :email} + [:input.input {:type "email"}]]]] [form-builder/vertical-control {:is-small? true} "Phone" [:div.control.has-icons-left - [form-builder/raw-field - [:input.input {:type "phone" - :field :phone - :spec ::contact/phone}]] + [form-builder/raw-field-v2 {:field :phone} + [:input.input {:type "phone"}]] [:span.icon.is-small.is-left [:i.fa.fa-phone]]]]]]]) + (defn form-content [{:keys [data]}] (let [is-admin? @(re-frame/subscribe [::subs/is-admin?]) - clients @(re-frame/subscribe [::subs/clients])] + clients @(re-frame/subscribe [::subs/client-refs])] [form-builder/builder {:submit-event [::save] - :can-submit [::can-submit] - :id ::vendor-form} - [form-builder/field - [:span "Name " [:span.has-text-danger "*"]] - [:input.input {:type "text" - :auto-focus true - :field :name - :spec ::entity/name}]] + :id ::vendor-form + :schema schema} + [form-builder/field-v2 {:field :name + :required true} + "Name" + [:input.input {:auto-focus true}]] - [form-builder/field + [form-builder/field-v2 {:field :print-as} "Print Checks As" - [:input.input {:type "text" - :field :print-as - :spec ::entity/print-as}]] + [:input.input]] (when is-admin? - [:div.field - [:label.checkbox - [form-builder/raw-field - [:input {:type "checkbox" - :field [:hidden] - :spec ::entity/hidden}]] - " Hidden"]]) + [form-builder/raw-field-v2 {:field :hidden} + [com/checkbox {:label "Hidden"}]]) [form-builder/section {:title "Terms"} - [form-builder/field + [form-builder/field-v2 {:field :terms} "Terms" - [:input.input {:type "number" - :step "1" - :style {:width "4em"} - :field [:terms] - :size 3 - :spec ::entity/terms}]] + [number-input ]] (when is-admin? - [form-builder/field + [form-builder/field-v2 {:field [:terms-overrides]} "Overrides" - [multi-field {:type "multi-field" - :field [:terms-overrides] - :template [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "13em"} - :type "typeahead-v3" - :field [:client]}] - [:input.input {:type "number" - :step "1" - :style {:width "4em"} - :field [:terms] - :size 3 - :spec ::entity/terms}]]}]]) - ] + [multi-field-v2 {:template [[form-builder/raw-field-v2 {:field [:client]} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "13em"} + :type "typeahead-v3" + }]] + [form-builder/raw-field-v2 {:field :terms} + [number-input]]] + :schema [:sequential terms-override-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "New Terms Override"}]])] + (when is-admin? [form-builder/section {:title "Schedule payment when due"} - [form-builder/field + [form-builder/field-v2 {:field [:automatically-paid-when-due]} "Client" - [multi-field {:type "multi-field" - :field [:automatically-paid-when-due] - :template [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "13em"} - :type "typeahead-v3" - :field [:client]}]]}]]]) + [multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "13em"}}]]] + :schema [:sequential automatically-paid-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "Schedule another client"}]]]) (when is-admin? [form-builder/section {:title "Schedule payment on day of month"} - [form-builder/field + [form-builder/field-v2 {:field [:schedule-payment-dom]} "Overrides" - [multi-field {:type "multi-field" - :field [:schedule-payment-dom] - :template [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "13em"} - :type "typeahead-v3" - :field [:client]}] - [:input.input {:type "number" - :step "1" - :style {:width "4em"} - :field [:dom] - :size 3 - :spec ::entity/terms}]]}]]]) + [multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "13em"}}]] + [form-builder/raw-field-v2 {:field :dom} + [number-input]]] + :schema [:sequential schedule-payment-dom-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "Schedule another client"}]]]) [form-builder/section {:title "Expense Accounts"} - [form-builder/field - "Default *" + [form-builder/field-v2 {:field :default-account + :required? true} + "Default" [search-backed-typeahead {:search-query (fn [i] [:search_account {:query i} [:name :id]]) - :type "typeahead-v3" - :style {:width "19em"} - :field [:default-account]}]] + :style {:width "19em"}}]] (when is-admin? - [form-builder/field + [form-builder/field-v2 {:field [:account-overrides]} "Overrides" - [multi-field {:type "multi-field" - :field [:account-overrides] - :template (fn [entity] - [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "19em"} - :type "typeahead-v3" - :field [:client]}] - [search-backed-typeahead {:search-query (fn [i] - [:search_account - {:query i - :client_id (:id (:client entity))} - [:name :id]]) - :type "typeahead-v3" - :style {:width "15em"} - :field [:account]}]])}]])] + [multi-field-v2 {:template (fn [entity] + [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "19em"} + }]] + [form-builder/raw-field-v2 {:field :account} + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client_id (:id (:client entity))} + [:name :id]]) + :style {:width "15em"}}]]]) + :schema [:sequential account-override-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "Add override"}]])] - [form-builder/with-scope {:scope [:address ]} - [form-builder/section {:title "Address"} - [:div {:style {:width "30em"}} + [form-builder/section {:title "Address"} + [:div {:style {:width "30em"}} + [form-builder/raw-field-v2 {:field :address} [address2-field]]]] - + [form-builder/section {:title "Contact"} [contact-field {:name "Primary" :field [:primary-contact]}] [contact-field {:name "Secondary" :field [:secondary-contact]}]] - (when is-admin? [form-builder/section {:title "Legal Entity"} [form-builder/vertical-control "Name" - [left-stack + [left-stack [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-first-name} [:input.input {:type "text" - :placeholder "First Name" - :field [:legal-entity-first-name] - :spec ::contact/name}]]] + :placeholder "First Name"}]]] [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-middle-name} [:input.input {:type "text" - :placeholder "Middle Name" - :field [:legal-entity-middle-name] - :spec ::contact/name}]]] + :placeholder "Middle Name"}]]] [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-last-name} [:input.input {:type "text" - :placeholder "Last Name" - :field [:legal-entity-last-name] - :spec ::contact/name}]]]]] + :placeholder "Last Name"}]]]]] [form-builder/vertical-control "TIN" - [left-stack - [form-builder/raw-field + [left-stack + [form-builder/raw-field-v2 {:field :legal-entity-tin} [:input.input {:type "text" :placeholder "SSN or EIN" - :field [:legal-entity-tin] :size "12" - :spec ::contact/name}]] + }]] [:div.control - [:div.select - [form-builder/raw-field - [:select {:type "select" - :field [:legal-entity-tin-type]} - [:option {:value nil} ""] - [:option {:value "ein"} "EIN"] - [:option {:value "ssn"} "SSN"]]]]]]] + [form-builder/raw-field-v2 {:field :legal-entity-tin-type} + [com/select-field {:options [["ein" "EIN"] + ["ssn" "SSN"]] + :allow-nil? true}]]]]] - [form-builder/vertical-control + [form-builder/field-v2 {:field :legal-entity-1099-type} "1099 Type" - [:div.control - [:div.select - [form-builder/raw-field - [:select {:type "select" - :field [:legal-entity-1099-type]} - [:option {:value nil} ""] - [:option {:value "none"} "Don't 1099"] - [:option {:value "misc"} "Misc"] - [:option {:value "landlord"} "Landlord"]]]]]]]) + [com/select-field {:options [["none" "Don't 1099"] + ["misc" "Misc"] + ["landlord" "Landlord"]] + :allow-nil? true}]]]) [form-builder/hidden-submit-button]])) (defn vendor-dialog [ ] @@ -321,36 +324,32 @@ ::vendor-selected [with-user (forms/in-form ::select-vendor-form)] (fn [{{:keys [data]} :db :keys [user]} _] - {:graphql {:token user - :query-obj {:venia/queries [[:vendor-by-id - {:id (:id (:vendor data))} - common/default-read]]} - :owns-state {:single ::select-vendor-form} - :on-success (fn [r] - [::started (:vendor-by-id r)])}})) - -(re-frame/reg-sub - ::can-submit-select-vendor-form - :<- [::forms/field ::select-vendor-form [:vendor]] - (fn [vendor] - (if vendor - true - false))) - + (if (:vendor data) + {:graphql {:token user + :query-obj {:venia/queries [[:vendor-by-id + {:id (:id (:vendor data))} + common/default-read]]} + :owns-state {:single ::select-vendor-form} + :on-success (fn [r] + [::started (:vendor-by-id r)])}} + {:dispatch-n [[::forms/attempted-submit ::select-vendor-form] + [::status/error ::select-vendor-form [{:message "Please select a vendor."}]]]}))) (defn select-vendor-form-content [] [form-builder/builder {:submit-event [::vendor-selected] - :can-submit [::can-submit-select-vendor-form] - :id ::select-vendor-form} - [form-builder/field + :id ::select-vendor-form + :validation-error-string "Please select a vendor." + :schema [:map + [:vendor schema/reference]]} + [form-builder/field-v2 {:field :vendor + :required? true} "Vendor to edit" [search-backed-typeahead {:search-query (fn [i] [:search_vendor {:query i} [:name :id]]) - :type "typeahead-v3" - :auto-focus true - :field [:vendor]}]] + :style {:width "20em"} + :auto-focus true}]] [form-builder/hidden-submit-button]]) @@ -360,6 +359,7 @@ (fn [{:keys [db]} [_ vendor]] {:db (-> db (forms/start-form ::vendor-form (-> vendor (update :automatically-paid-when-due #(mapv (fn [apwd] + apwd {:id (:id apwd) :client apwd}) %)) @@ -370,11 +370,11 @@ {:title "Vendor" :body [vendor-dialog] :class "semi-wide" + :status-from [::status/single ::vendor-form] :confirm {:value "Save Vendor" :status-from [::status/single ::vendor-form] :class "is-primary" :on-click (dispatch-event [::save]) - :can-submit [::can-submit] :close-event [::status/completed ::vendor-form]}}]})) (re-frame/reg-event-fx @@ -388,5 +388,4 @@ :status-from [::status/single ::select-vendor-form] :class "is-primary" :on-click (dispatch-event [::vendor-selected]) - :can-submit [::can-submit-select-vendor-form] :close-event [::status/completed ::select-vendor-form]}}]})) diff --git a/src/cljs/auto_ap/views/components/vendor_filter.cljs b/src/cljs/auto_ap/views/components/vendor_filter.cljs index 67aa7995..03b1f2ab 100644 --- a/src/cljs/auto_ap/views/components/vendor_filter.cljs +++ b/src/cljs/auto_ap/views/components/vendor_filter.cljs @@ -1,7 +1,3 @@ -(ns auto-ap.views.components.vendor-filter - (:require - [clojure.spec.alpha :as s] - [auto-ap.entities.invoice :as invoice] - [auto-ap.views.utils :refer [bind-field]])) +(ns auto-ap.views.components.vendor-filter) diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index f924ea3f..c86355d5 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -28,7 +28,7 @@ [auto-ap.views.pages.admin.users :refer [admin-users-page]] [auto-ap.views.pages.admin.import-batches :refer [import-batches-page]] [auto-ap.views.pages.admin.yodlee2 :as yodlee2] - [auto-ap.views.pages.admin.plaid :as plaid])) + [auto-ap.views.pages.company.plaid :as plaid])) (defmulti page (fn [active-page] active-page)) (defmethod page :unpaid-invoices [_] @@ -112,8 +112,8 @@ (defmethod page :admin-yodlee2 [_] (yodlee2/admin-yodle-provider-accounts-page)) -(defmethod page :admin-plaid [_] - (plaid/admin-plaid-page)) +(defmethod page :plaid [_] + (plaid/plaid-page)) (defmethod page :admin-accounts [_] (admin-accounts-page)) @@ -144,8 +144,9 @@ (let [ap (re-frame/subscribe [::subs/active-page]) current-client @(re-frame/subscribe [::subs/client]) is-loading? @(re-frame/subscribe [::subs/is-initial-loading?])] - (if is-loading? - [loading-layout] - - [:div - ^{:key (str @ap "-" current-client)} [page @ap]]))) + (when @ap + (if is-loading? + [loading-layout] + + [:div + ^{:key (str @ap "-" current-client)} [page @ap]])))) diff --git a/src/cljs/auto_ap/views/pages/admin/accounts.cljs b/src/cljs/auto_ap/views/pages/admin/accounts.cljs index 4bc7af25..45be819d 100644 --- a/src/cljs/auto_ap/views/pages/admin/accounts.cljs +++ b/src/cljs/auto_ap/views/pages/admin/accounts.cljs @@ -73,8 +73,7 @@ [buttons/new-button {:name "Account" :class "is-primary" :event [::account-form/editing - {:type :asset - :account-set "default"}]}]] + {:account-set "default"}]}]] [table/accounts-table {:data-page ::page}]]) (defn admin-accounts-page [] diff --git a/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs b/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs index e83eb8ad..fd81848c 100644 --- a/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs @@ -1,19 +1,21 @@ (ns auto-ap.views.pages.admin.accounts.form - (:require [auto-ap.entities.account :as entity] - [auto-ap.forms :as forms] - [auto-ap.subs :as subs] - [auto-ap.views.components.layouts :refer [side-bar]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.utils :refer [dispatch-event multi-field]] - [clojure.spec.alpha :as s] - [clojure.string :as str] - [re-frame.core :as re-frame])) + (:require + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] + [auto-ap.subs :as subs] + [auto-ap.views.components :as com] + [auto-ap.views.components.layouts :refer [side-bar]] + [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [clojure.string :as str] + [malli.core :as m] + [re-frame.core :as re-frame] + [vimsical.re-frame.cofx.inject :as inject])) (def types [:dividend :expense :asset :liability :equity :revenue]) (def applicabilities [:global :optional :customized]) - - (re-frame/reg-sub ::request :<- [::forms/form ::form] @@ -35,11 +37,7 @@ (:name client-override))}) client-overrides)})) -(re-frame/reg-sub - ::can-submit - :<- [::request] - (fn [request] - (s/valid? ::entity/account request))) + (re-frame/reg-event-db ::editing @@ -51,103 +49,98 @@ (re-frame/reg-event-fx ::edited [(forms/triggers-saved ::form :upsert-account)] - (fn [{:keys [db]} [_ {:keys [upsert-account]}]])) + (fn [_ [_ _]])) -(re-frame/reg-event-db - ::add-client-override - [(forms/in-form ::form)] - (fn [form] - (update form :data (fn [data] - (-> data - (update :client-overrides conj (:new-client-override data)) - (dissoc :new-client-override)))))) (re-frame/reg-event-fx ::saving - (fn [{:keys [db]} _] - (when @(re-frame/subscribe [::can-submit]) - (let [{{:keys [id type name numeric-code account-set]} :data :as data} @(re-frame/subscribe [::forms/form ::form])] - {:db (forms/loading db ::form ) - :graphql - {:token (-> db :user) - :query-obj {:venia/operation {:operation/type :mutation - :operation/name "UpsertAccount"} - :venia/queries [{:query/data [:upsert-account - {:account @(re-frame/subscribe [::request])} - [:id :type :name :account-set :numeric-code :location :applicability [:client-overrides [:name :id [:client [:id :name]]]]]]}]} - :on-success [::edited] - :on-error [::forms/save-error ::form]}})))) + [with-user (re-frame/inject-cofx ::inject/sub [::request]) ] + (fn [{:keys [user] ::keys [request]} _] + (let [_ @(re-frame/subscribe [::forms/form ::form])] + {:graphql + {:owns-state {:single ::form} + :token user + :query-obj {:venia/operation {:operation/type :mutation + :operation/name "UpsertAccount"} + :venia/queries [{:query/data [:upsert-account + {:account request} + [:id :type :name :account-set :numeric-code :location :applicability [:client-overrides [:name :id [:client [:id :name]]]]]]}]} + :on-success [::edited] + :on-error [::forms/save-error ::form]}}))) -(def account-form (forms/vertical-form {:can-submit [::can-submit] - :change-event [::forms/change ::form] - :submit-event [::saving] - :id ::form})) +(def account-customization-schema + (m/schema + [:map [:client schema/reference] + [:name schema/not-empty-string]])) + +(def account-schema + (m/schema + [:map + [:numeric-code schema/integer-code] + [:name schema/not-empty-string] + [:type [:enum :dividend :expense :asset :liability :equity :revenue]] + [:location {:optional true} [:maybe :string]] + [:applicability [:enum :global :optional :customized]] + [:client-overrides {:optional true} + [:maybe [:sequential account-customization-schema]]]])) (defn form [_] - (let [{error :error account :data } @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field field-holder raw-field error-notification submit-button]} account-form] - - ^{:key (:id account)} + (let [{account :data } @(re-frame/subscribe [::forms/form ::form])] [side-bar {:on-close (dispatch-event [::forms/form-closing ::form])} - (form-inline {:title (if (:id account) - "Edit account" - "Add account")} - [:<> + [form-builder/builder {:change-event [::forms/change ::form] + :submit-event [::saving] + :id ::form + :schema account-schema} + [form-builder/section {:title (if (:id account) + "Edit account" + "Add account")} + [form-builder/field-v2 {:field :numeric-code} + "Code" + [com/number-input + {:disabled (boolean (:id account)) + :auto-focus (not (boolean (:id account))) + :style {:width "9em"}}]] - (field "Account Set" - [:input.input {:type "text" - :field :account-set - :disabled (boolean (:id account)) - :spec ::entity/account-set}]) + [form-builder/field-v2 {:field :name} + "Name" + [:input.input {:type "text" + :auto-focus (boolean (:id account))}]] - (field "Code" - [:input.input {:type "text" - :field :numeric-code - :disabled (boolean (:id account)) - :spec ::entity/numeric-code}]) - - (field "Name" - [:input.input {:type "text" - :field :name - :spec ::entity/name}]) - - (field-holder "Account Type" - [:div.select - (raw-field - [:select {:type "select" - :field :type - :spec (set types)} - (map (fn [l] - [:option {:value (name l)} (str/capitalize (name l))]) types)])]) + [form-builder/field-v2 {:field :type} + "Account Type" + [com/select-field {:options (map (fn [l] + [l (str/capitalize (name l))]) + types) + :allow-nil? true + :keywordize? true}]] - (field "Location" - [:input.input.known-field.location {:type "text" - :field :location - :spec ::entity/location}]) + [form-builder/field-v2 {:field :location} + "Location" + [:input.input.known-field.location {:type "text"}]] - [:h2.subtitle "Client"] - (field-holder "Applicability" - [:div.select - (raw-field - [:select {:type "select" - :field :applicability - :spec (set applicabilities)} - (map (fn [l] - [:option {:value (name l)} (str/capitalize (name l))]) applicabilities)])]) - (field "Customizations" - [multi-field {:type "multi-field" - :field [:client-overrides] - :template [[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) - :style {:width "13em"} - :entity->text :name - :type "typeahead-v3" - :field [:client]}] - [:input.input {:type "text" - :style {:width "15em"} - :placeholder "Bubblegum" - :field [:name]}] - ]}]) - (error-notification) + [form-builder/section {:title "Client"} + [:h2.subtitle "Client"] + [form-builder/field-v2 {:field :applicability} + "Applicability" + [com/select-field {:options (map (fn [l] + [l + (str/capitalize (name l))]) + applicabilities) + :allow-nil? true + :keywordize? true}]] + [form-builder/field-v2 {:field :client-overrides} + "Customizations" + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) + :style {:width "13em"} + :entity->text :name}]] + [form-builder/raw-field-v2 {:field :name} + [:input.input {:type "text" + :style {:width "15em"} + :placeholder "Bubblegum"}]]] + :key-fn :id + :schema [:sequential account-customization-schema]}]]] + [form-builder/error-notification] - (submit-button "Save")])])) + [form-builder/submit-button "Save"]]]])) diff --git a/src/cljs/auto_ap/views/pages/admin/clients/form.cljs b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs index ee55c28c..aa0599e4 100644 --- a/src/cljs/auto_ap/views/pages/admin/clients/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs @@ -9,28 +9,58 @@ [auto-ap.views.components.address :refer [address2-field]] [react-signature-canvas] [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.components.level :refer [left-stack]] + [auto-ap.views.components.level :refer [left-stack] :as level] + [auto-ap.views.components :as com] [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] [auto-ap.views.utils - :refer [date->str - date-picker - date-picker-friendly - dispatch-event - horizontal-field - multi-field - standard]] + :refer [date-picker + dispatch-event]] [bidi.bidi :as bidi] [cljs-time.coerce :as coerce] [cljs-time.core :as t] - [clojure.string :as str] [re-frame.core :as re-frame] [reagent.core :as r] [react-signature-canvas] - [vimsical.re-frame.cofx.inject :as inject])) + [vimsical.re-frame.cofx.inject :as inject] + [auto-ap.schema :as schema] + [malli.core :as m])) (def signature-canvas (r/adapt-react-class (.-default react-signature-canvas))) +(def location-schema (m/schema [:map + [:location schema/not-empty-string]])) + +(def square-location-schema (m/schema [:map + [:square-location schema/reference] + [:client-location schema/not-empty-string]])) + +(def ezcater-schema (m/schema [:map + [:caterer schema/reference] + [:client-location schema/not-empty-string]])) + +(def name-match-schema (m/schema [:map + [:match schema/not-empty-string]])) +(def location-match-schema (m/schema [:map + [:match schema/not-empty-string] + [:location schema/not-empty-string]])) +(def email-schema [:map + [:email schema/not-empty-string] + [:description schema/not-empty-string]]) + +(def client-schema [:map + [:name schema/not-empty-string] + [:code schema/code-string] + [:locations [:sequential location-schema]] + [:emails {:optional true} + [:maybe [:sequential email-schema]]] + [:matches {:optional true} + [:maybe [:sequential name-match-schema]]] + [:location-matches {:optional true} + [:maybe [:sequential location-match-schema]]] + [:selected-square-locations {:optional true} + [:maybe [:sequential square-location-schema]]]]) + (defn upload-replacement-button [{:keys [on-change]} text] (let [button (atom nil)] (r/create-class {:display-name "Upload button" @@ -104,14 +134,6 @@ [upload-replacement-button {:on-change on-change} "Upload signature"]]])) ]))) -(re-frame/reg-sub - ::can-submit - :<- [::new-client-request] - (fn [_ _] - true - - #_(s/valid? ::entity/client r))) - (re-frame/reg-sub ::new-client-request :<- [::forms/form ::form] @@ -120,7 +142,8 @@ {:id (:id new-client-data), :name (:name new-client-data) :code (:code new-client-data) ;; TODO add validation can't change - :emails (:emails new-client-data) + :emails (map #(select-keys % [:id :email :description]) + (:emails new-client-data)) :square-auth-token (:square-auth-token new-client-data) :square-locations (map (fn [x] @@ -135,15 +158,7 @@ :location (:location x)}) (:ezcater-locations new-client-data)) - :locked-until (cond (not (:locked-until new-client-data)) - nil - - (instance? goog.date.Date (:locked-until new-client-data)) - (date->str (:locked-until new-client-data) standard) - - :else - (:locked-until new-client-data) - ) + :locked-until (:locked-until new-client-data) :locations (mapv :location (:locations new-client-data)) :matches (mapv :match (:matches new-client-data)) :location-matches (:location-matches new-client-data) @@ -166,28 +181,16 @@ :bank-accounts (map-indexed (fn [i {:keys [number name check-number plaid-account intuit-bank-account include-in-reports type id code numeric-code start-date bank-name routing bank-code new? sort-order visible yodlee-account-id locations yodlee-account use-date-instead-of-post-date]}] {:number number :name name - :check-number (when-not (str/blank? check-number) - (js/parseInt check-number)) - :numeric-code (when-not (str/blank? numeric-code) - (js/parseInt numeric-code)) + :check-number check-number + :numeric-code numeric-code :include-in-reports include-in-reports - :start-date (cond (not start-date) - nil - - (instance? goog.date.Date start-date) - (date->str start-date standard) - - :else - start-date - ) + :start-date start-date :type type :id id :sort-order i :visible visible :locations (mapv :location locations) :use-date-instead-of-post-date use-date-instead-of-post-date - :yodlee-account-id (when-not (str/blank? yodlee-account-id) - (js/parseInt yodlee-account-id)) :yodlee-account (:id yodlee-account) :plaid-account (:id plaid-account) :intuit-bank-account (:id intuit-bank-account) @@ -204,7 +207,7 @@ :<- [::subs/route-params] :<- [::subs/clients-by-id] (fn [[rp clients-by-id]] - (or (clients-by-id (:id rp)) + (or (get clients-by-id (:id rp)) {}))) (re-frame/reg-event-fx @@ -220,13 +223,16 @@ {:id (:id sl) :square-location sl :client-location (:client-location sl)})))) - (update :locations #(mapv (fn [l] {:location l}) %)) - (update :matches #(mapv (fn [l] {:match l}) %)) + (update :locations #(mapv (fn [l] {:location l + :id (random-uuid)}) %)) + (update :matches #(mapv (fn [l] {:match l + :id (random-uuid)}) %)) (update :bank-accounts (fn [bas] (mapv (fn [ba] (update ba :locations (fn [ls] - (map (fn [l] {:location l}) + (map (fn [l] {:location l + :id (random-uuid)}) ls)))) bas))))))})) @@ -375,188 +381,132 @@ (when active? [:div.card-content [:label.label "General"] - [horizontal-field - nil + [level/left-stack [:div.control [:p.help "Account Code"] (if new? - [:div.field.has-addons.is-extended + [:div.field.has-addons [:p.control [:a.button.is-static (:code new-client) "-" ]] [:p.control - [form-builder/raw-field - [:input.input {:type "code" - :field [:code] - :spec ::entity/code}]]]] + [form-builder/raw-field-v2 {:field :code} + [:input.input {:type "text"}]]]] [:div.field [:p.control code]])] - - - [form-builder/field + [form-builder/field-v2 {:field :name} "Nickname" [:input.input {:placeholder "BOA Checking #1" - :type "text" - :field [:name]}]] - [horizontal-field - nil - [form-builder/field - "Numeric Code" - [:input.input {:placeholder "20101" - :type "text" - :field [:numeric-code]}]]] - [form-builder/field + :type "text"}]] + [form-builder/field-v2 {:field :numeric-code} + "Numeric Code" + [com/number-input {:placeholder "20101" + :style {:width "8em"}}]] + [form-builder/field-v2 {:field :start-date} "Start date" - [date-picker {:class-name "input" - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder "mm/dd/yyyy" - :next-month-button-label "" - :next-month-label "" - :type "date" - :field [:start-date]}]]] + [date-picker {:output :cljs-date}]]] + (when (#{:check ":check"} type ) [:div [:label.label "Bank"] - [horizontal-field - nil - [form-builder/field + [level/left-stack + [form-builder/field-v2 {:field :bank-name} "Bank Name" [:input.input {:placeholder "Bank of America" - :type "text" - :field [:bank-name]}]] - [form-builder/field + :type "text"}]] + [form-builder/field-v2 {:field [:routing]} "Routing #" [:input.input {:placeholder "104819123" :style {:width "9em"} - :type "text" - :field [:routing]}]] - [form-builder/field + :type "text"}]] + [form-builder/field-v2 {:field :bank-code} "Bank code" [:input.input {:placeholder "12/10123" - :type "text" - :field [:bank-code]}]]] + :type "text"}]]] - [horizontal-field - nil - [form-builder/field + [level/left-stack + [form-builder/field-v2 {:field :number} "Account #" [:input.input {:placeholder "123456789" :type "text" - :style {:width "20em"} - :field [:number]}]] + :style {:width "20em"}}]] - [form-builder/field + [form-builder/field-v2 {:field :check-number} "Check Number" - [:input.input {:placeholder "10000" - :style {:width "6em"} - :type "text" - :field [:check-number]}]]] - [form-builder/field - "Yodlee Account" - [:input.input {:placeholder "Yodlee Account #" - :type "text" - :field [:yodlee-account-id]}]] - [form-builder/field + [com/number-input {:style {:width "8em"} + :placeholder "10000"}]]] + + [form-builder/field-v2 {:field :yodlee-account} "Yodlee Account (new)" [typeahead-v3 {:entities @(re-frame/subscribe [::yodlee-accounts (:id new-client)]) - :entity->text (fn [m] (str (:name m) " - " (:number m))) - :type "typeahead-v3" - :field [:yodlee-account]}]] - [:div.field - [:label.checkbox - [form-builder/raw-field - [:input {:type "checkbox" - :field [:use-date-instead-of-post-date]}]] - " (Yodlee only) Use 'date' instead of 'postDate'"]] + :entity->text (fn [m] (str (:name m) " - " (:number m)))}]] - [form-builder/field + + [form-builder/raw-field-v2 {:field :use-date-instead-of-post-date} + [com/checkbox {:label " (Yodlee only) Use 'date' instead of 'postDate'"}]] + + [form-builder/field-v2 {:field :intuit-bank-account} "Intuit Bank Account" [typeahead-v3 {:entities @(re-frame/subscribe [::subs/intuit-bank-accounts]) - :entity->text (fn [m] (str (:name m))) - :type "typeahead-v3" - :field [:intuit-bank-account]}]] - [form-builder/field + :entity->text (fn [m] (str (:name m)))}]] + [form-builder/field-v2 {:field :plaid-accounti} "Plaid Account" [typeahead-v3 {:entities @(re-frame/subscribe [::plaid-accounts (:id new-client)]) - :entity->text (fn [m] (str (:name m))) - :type "typeahead-v3" - :field [:plaid-account]}]]]) + :entity->text (fn [m] (str (:name m)))}]]]) (when (#{:credit ":credit"} type ) [:div [:label.label "Account"] - [horizontal-field - nil - [form-builder/field - "Bank Name" - [:input.input {:placeholder "Bank of America" - :type "text" - :field [:bank-name]}]]] - + [form-builder/field-v2 {:field :bank-name} + "Bank Name" + [:input.input {:placeholder "Bank of America" + :type "text"}]] - [horizontal-field - nil - [form-builder/field - "Account #" - [:input.input {:placeholder "123456789" - :type "text" - :field [:number]}]]] - - [form-builder/field - "Yodlee Account" - [:input.input {:placeholder "Yodlee Account #" + [form-builder/field-v2 {:field :number} + "Account #" + [:input.input {:placeholder "123456789" :type "text" - :field [:yodlee-account-id]}]] - [form-builder/field + :style {:width "20em"}}]] + + [form-builder/field-v2 {:field :yodlee-account} "Yodlee Account (new)" [typeahead-v3 {:entities @(re-frame/subscribe [::yodlee-accounts (:id new-client)]) - :entity->text (fn [m] (str (:name m) " - " (:number m))) - :type "typeahead-v3" - :field [:yodlee-account]}]] - [:div.field - [:label.checkbox - [form-builder/raw-field - [:input {:type "checkbox" - :field [:use-date-instead-of-post-date]}]] - " (Yodlee only) Use 'date' instead of 'postDate'"]] - [form-builder/field + :entity->text (fn [m] (str (:name m) " - " (:number m)))}]] + + [form-builder/raw-field-v2 {:field :use-date-instead-of-post-date} + [com/checkbox {:label "(Yodlee only) Use 'date' instead of 'postDate'"}] + [:input {:type "checkbox" + :field [:use-date-instead-of-post-date]}]] + + [form-builder/field-v2 {:field :intuit-bank-account} "Intuit Bank Account" [typeahead-v3 {:entities @(re-frame/subscribe [::subs/intuit-bank-accounts]) - :entity->text (fn [m] (str (:name m))) - :type "typeahead-v3" - :field [:intuit-bank-account]}]] - [form-builder/field + :entity->text (fn [m] (str (:name m)))}]] + + [form-builder/field-v2 {:field :plaid-account} "Plaid Account" [typeahead-v3 {:entities @(re-frame/subscribe [::plaid-accounts (:id new-client)]) - :entity->text (fn [m] (str (:name m))) - :type "typeahead-v3" - :field [:plaid-account]}]]]) + :entity->text (fn [m] (str (:name m)))}]]]) [:div.field [:label.label "Locations"] [:div.control [:p.help "If this account is location-specific, add the valid locations"] - [form-builder/raw-field - [multi-field {:type "multi-field" - :field [:locations] - :template [[:select.select {:type "select" - :style {:width "7em"} - :allow-nil? true - :field [:location] - :spec (set (map :location (get-in new-client [:locations])))} - [:<> - [:option ""] - [:<> (map (fn [l] ^{:key (:location l)} - [:option {:value (:location l)} (:location l)]) - (get-in new-client [:locations]))]]]]}]]]] - [:div.field - [:label.checkbox - [form-builder/raw-field - [:input {:type "checkbox" - :field [:include-in-reports]}]] - " Include in reports"]]]) + [form-builder/raw-field-v2 {:field :locations} + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :location} + [com/select-field {:options (map (fn [l] + [(:location l) (:location l)]) + (get-in new-client [:locations])) + :allow-nil? true + :style {:width "7em"} + }]]] + :schema [:sequential location-schema] + :key-fn :id}]]]] + + [form-builder/raw-field-v2 {:field :include-in-reports} + [com/checkbox {:label "Include in reports"}] + ] + ]) (when active? [:footer.card-footer @@ -568,86 +518,87 @@ (defn general-section [] (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])] [form-builder/section {:title "General"} - [form-builder/field + [form-builder/field-v2 {:field :name} "Name" [:input.input {:type "text" - :style {:width "20em"} - :field [:name] - :spec ::entity/name}]] - [form-builder/field + :style {:width "20em"}}]] + [form-builder/field-v2 {:field :code} "Client code" [:input.input {:type "code" :style {:width "5em"} - :field :code :disabled (boolean (:id new-client)) :spec ::entity/code}]] - [form-builder/field + [form-builder/field-v2 {:field :locations} "Locations" - [multi-field {:type "multi-field" - :field :locations - :allow-change? false - :template [[:input.input {:field [:location] - :max-length 2 - :style { :width "4em"}}]]}]] + [com/multi-field-v2 {:allow-change? false + :template [[form-builder/raw-field-v2 {:field :location} + [:input.input {:max-length 2 + :style { :width "4em"}}]]] + :disable-remove? true + :key-fn :id + :schema [:sequential location-schema] + :next-key (random-uuid)}]] - [:div.field - [:label.label "Signature"] + [form-builder/vertical-control + "Signature" [signature {:signature-file (:signature-file new-client) :signature-data (:signature-data new-client) :on-change (fn [uri] (re-frame/dispatch [::forms/change ::form [:signature-data] uri]))}]] - [form-builder/field + [form-builder/field-v2 {:field :locked-until} "Locked Until" - [date-picker-friendly {:type "date" - :field [:locked-until] - :style {:width "15em"}}]]])) + [date-picker {:output :cljs-date + :style {:width "15em"}}]]])) (defn contacts-section [] [form-builder/section {:title "Contacts"} - [form-builder/field + [form-builder/field-v2 {:field :emails} "Emails (address/description)" - [multi-field {:type "multi-field" - :field :emails - :template [[:input.input {:type "email" - :field [:email] - :placeholder "tom@myspace.com" - :spec ::entity/email}] - [:input.input {:type "text" - :placeholder "Manager" - :field [:description]}]]}]] + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :email} + [:input.input {:type "email" + :placeholder "tom@myspace.com"}]] + [form-builder/raw-field-v2 {:field :description} + [:input.input {:type "text" + :placeholder "Manager"}]]] + :key-fn :id + :schema [:sequential email-schema] + :next-key (random-uuid)}]] - [form-builder/with-scope {:scope [:address ]} - [:div.field - [:label.label "Address"] - [:div.control - [:div {:style {:width "30em"}} - [address2-field]]]]]]) + [form-builder/vertical-control + "Address" + [:div {:style {:width "30em"}} + [form-builder/raw-field-v2 {:field :address} + [address2-field]]]]]) +;; TODO Name matches, locations, bank account locations are all "single field multis", and require weird mounting and +;; unmounting. A new field could sort that out easily (defn matching-section [] [form-builder/section {:title "Matching"} - [form-builder/field + [form-builder/field-v2 {:field :matches} "Name matches" - [multi-field {:type "multi-field" - :field :matches - :template [[:input.input {:field [:match] - :placeholder "Harry's burger joint" - :style { :width "15em"}}]]}]] + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field [:match]} + [:input.input {:placeholder "Harry's burger joint" + :style { :width "15em"}}]]] + :key-fn :id + :next-key (random-uuid) + :schema [:sequential name-match-schema]}]] - [form-builder/field + [form-builder/field-v2 {:field :location-matches} "Location Matches" - [multi-field {:type "multi-field" - :field :location-matches - :template [[:input.input {:field [:match] - :placeholder "Downtown" - :style { :width "15em"}}] - [:input.input {:field [:location] - :placeholder "DT" - :max-length 2 - :style { :width "4em"}}]]}]]]) + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :match} + [:input.input {:placeholder "Downtown" + :style { :width "15em"}}]] + [form-builder/raw-field-v2 {:field :location} + [:input.input {:placeholder "DT" + :max-length 2 + :style { :width "4em"}}]]] + :schema [:sequential location-match-schema] + :next-key (random-uuid) + :key-fn :id}]]]) (defn bank-accounts-section [] (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])] @@ -674,99 +625,82 @@ [form-builder/section {:title "Cash Flow"} [:label.label (str "Week A (" next-week-a ")")] [left-stack - [form-builder/field + [form-builder/field-v2 {:field :week-a-credits} "Regular Credits" - [:input.input {:type "number" - :style {:width "10em"} - :placeholder "500.00" - :field [:week-a-credits] - :step "0.01"}]] - [form-builder/field + [com/money-input]] + [form-builder/field-v2 {:field :week-a-debits} "Regular Debits" - [:input.input {:type "number" - :style {:width "10em"} - :placeholder "150.00" - :field [:week-a-debits] - :step "0.01"}]]] + [com/money-input]]] [:label.label (str "Week B (" next-week-b ")")] [left-stack - [form-builder/field "Regular Credits" - [:input.input {:type "number" - :style {:width "10em"} - :placeholder "1000.00" - :field [:week-b-credits] - :step "0.01"}]] - [form-builder/field "Regular Debits" - [:input.input {:type "number" - :style {:width "10em"} - :placeholder "250.00" - :field [:week-b-debits] - :step "0.01"}]]] - [:div.field - [:label.label "Forecasted transactions"] + [form-builder/field-v2 {:field :week-b-credits} + "Regular Credits" + [com/money-input]] + [form-builder/field-v2 {:field :week-b-debits} + "Regular Debits" + [com/money-input]]] - [:div.control - [form-builder/raw-field - [multi-field {:type "multi-field" - :field :forecasted-transactions - :template [[:input.input {:type "text" - :placeholder "Identifier" - :style {:width "10em"} - :field [ :identifier]}] - [:input.input {:type "number" - :style {:width "8em"} - :placeholder "DOM" - :step "1" - :field [:day-of-month]}] - [:input.input {:type "number" - :placeholder "250.00" - :class "has-text-right" - :style {:width "7em"} - :field [:amount] - :step "0.01"}]]}]]]]])) + [form-builder/field-v2 {:field :forecasted-transactions} + "Forecasted transactions" + + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :identifier} + [:input.input {:type "text" + :placeholder "Identifier" + :style {:width "10em"}}]] + [form-builder/raw-field-v2 {:field :day-of-month} + [com/number-input {:placeholder "DOM"}]] + [form-builder/raw-field-v2 {:field :amount + :placeholder "AMT"} + [com/money-input]]] + :key-fn :id}]]])) (defn square-section [] (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])] [form-builder/section {:title "Square Integration"} - [form-builder/field "Square Authentication Token" + [form-builder/field-v2 {:field :square-auth-token} + "Square Authentication Token" [:input.input {:type "text" - :style {:width "40em"} - :field [:square-auth-token]}]] - [form-builder/field + :style {:width "40em"}}]] + [form-builder/field-v2 {:field :selected-square-locations} "Square Locations" - [multi-field {:type "multi-field" - :field :selected-square-locations - :template [[typeahead-v3 {:entities (:square-locations new-client) - :entity->text :name - :type "typeahead-v3" - :style {:width "15em"} - :field [:square-location]}] - [:input.input {:type "text" - :style {:width "4em"} - :field [:client-location] - :step "0.01"}]] - :disable-remove? true}]]])) + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :square-location} + [typeahead-v3 {:entities (:square-locations new-client) + :entity->text :name + :style {:width "15em"}}]] + [form-builder/raw-field-v2 {:field :client-location} + [com/select-field {:options (map (fn [l] + [(:location l) (:location l)]) + (get-in new-client [:locations])) + :allow-nil? true + :style {:width "7em"} + }]]] + :disable-remove? true + :key-fn :id + :schema [:sequential square-location-schema]}]]])) (defn ezcater-section [] - [form-builder/section {:title "EZCater integration"} + (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])] + [form-builder/section {:title "EZCater integration"} + + [form-builder/field-v2 {:field :ezcater-locations} + "EZCater Locations" + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :caterer} + [search-backed-typeahead {:search-query (fn [i] + [:search_ezcater_caterer + {:query i} + [:name :id]]) + :entity->text :name + :style {:width "20em"}}]] + [form-builder/raw-field-v2 {:field [:location]} + [com/select-field {:options (map (fn [l] + [(:location l) (:location l)]) + (get-in new-client [:locations])) + :allow-nil? true + :style {:width "7em"}}]]] + :key-fn :id + :schema [:sequential ezcater-schema] + :disable-remove? true}]]])) - [form-builder/field - "EZCater Locations" - [multi-field {:type "multi-field" - :field :ezcater-locations - :template [[search-backed-typeahead {:search-query (fn [i] - [:search_ezcater_caterer - {:query i} - [:name :id]]) - :entity->text :name - :type "typeahead-v3" - :field [:caterer] - :style {:width "20em"}}] - [:input.input {:type "text" - :style {:width "4em"} - :field [:location] - :step "0.01"}]] - :disable-remove? true}]]]) (defn form-content [] (let [_ @(re-frame/subscribe [::client]) @@ -774,10 +708,10 @@ ^{:key (or (:id new-client) "new")} - [form-builder/builder {:can-submit [::can-submit] - :submit-event [::save-new-client ] + [form-builder/builder {:submit-event [::save-new-client ] :id ::form - :fullwidth? false} + :fullwidth? false + :schema client-schema} [general-section] [contacts-section] diff --git a/src/cljs/auto_ap/views/pages/admin/excel_import.cljs b/src/cljs/auto_ap/views/pages/admin/excel_import.cljs index 380f067e..4606dfd3 100644 --- a/src/cljs/auto_ap/views/pages/admin/excel_import.cljs +++ b/src/cljs/auto_ap/views/pages/admin/excel_import.cljs @@ -1,168 +1,114 @@ (ns auto-ap.views.pages.admin.excel-import - (:require [auto-ap.events :as all-events] - [auto-ap.forms :as forms] - [auto-ap.subs :as subs] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.utils :refer [bind-field dispatch-event]] - [re-frame.core :as re-frame])) - -(re-frame/reg-sub - ::excel-import - (fn [db] - (::excel-import db))) - -(re-frame/reg-sub - ::expense-accounts - (fn [db] - (::expense-accounts db))) - -(re-frame/reg-event-db - ::change - (fn [db [_ field v]] - (assoc-in db (into [::excel-import] field) v))) - - + (:require + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] + [auto-ap.views.components :as com] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.utils :refer [with-user]] + [malli.core :as m] + [re-frame.core :as re-frame] + [reagent.core :as r])) (re-frame/reg-event-fx ::save - [(forms/in-form ::excel-import)] - (fn [{{excel-import-data :data :as excel-import-form} :db}] - (let [user @(re-frame/subscribe [::subs/token])] - {:db (-> excel-import-form - (assoc :status :loading) - (assoc :error nil)) - :http {:token user - :method :post - :body (pr-str excel-import-data) - :headers {"Content-Type" "application/edn"} - :uri (str "/api/invoices/upload-integreat") - :on-success [::save-complete] - :on-error [::forms/save-error ::excel-import]}}))) + [ with-user (forms/in-form ::form)] + (fn [{:keys [db user]}] + {:http {:token user + :method :post + :body (pr-str {:excel-rows (:excel-rows (:data db))}) + :headers {"Content-Type" "application/edn"} + :uri (str "/api/invoices/upload-integreat") + :owns-state {:single ::form} + :on-success [::save-complete]}})) (re-frame/reg-event-fx ::save-complete (fn [{:keys [db]} [_ rows]] - {:db - (-> db - (forms/save-succeeded ::excel-import) - (assoc-in [::excel-import :rows] rows))})) + (cond-> + {:db (assoc db ::result rows)} + (seq (:vendors-not-found rows)) (assoc :dispatch [::forms/start-form ::create-vendors ])))) + +(re-frame/reg-sub + ::result + (fn [db] + (::result db))) -(re-frame/reg-event-fx - ::save-error - (fn [{:keys [db]}] - (println "ERROR") - {:dispatch [::change [:error] true] - :db (-> db - (assoc-in [::excel-import :rows] nil) - (assoc-in [::excel-import :saving?] false))})) -(re-frame/reg-event-db - ::toggle-vendor - (fn [db [_ data]] - (update-in db [::excel-import :create-vendors] (fn [x] - (let [x (or x #{})] - (if (x data) - (disj x data) - (conj x data))))))) (re-frame/reg-event-fx ::create-vendors - (fn [{:keys [db]}] - (let [excel-import (::excel-import db)] - (println (::expense-accounts db)) - {:graphql {:token (:user db) - :query-obj {:venia/operation {:operation/type :mutation - :operation/name "UpsertVendor"} - - :venia/queries (map (fn [v ] - {:query/data [:upsert-vendor - {:vendor {:name v :default-account-id (-> db ::expense-accounts (get v) :default-account-id :id)}} - [:id :name]]}) - - (get-in db [::excel-import :create-vendors]))} - :on-success [::create-vendor-complete] - :on-error [::create-vendor-error]} - :db (-> db - (assoc-in [::excel-import :saving-vendors?] true))}))) + [with-user (forms/in-form ::create-vendors)] + (fn [{:keys [user db]}] + {:graphql {:token user + :owns-state {:single ::create-vendors} + :query-obj {:venia/operation {:operation/type :mutation + :operation/name "UpsertVendor"} + + :venia/queries + (for [[vendor-name {:keys [default-account]}] (:data db)] + {:query/data [:upsert-vendor + {:vendor {:name vendor-name :default-account-id (:id default-account)}} + [:id :name]]})} + :on-success [::create-vendor-complete]}})) (re-frame/reg-event-db ::create-vendor-complete - (fn [db [_ data]] - (-> db - (update-in [::excel-import :rows :vendors-not-found] - (fn [v] - (reduce disj v (get-in db [::excel-import :create-vendors])))) - (update-in [::excel-import] dissoc :create-vendors)))) + (fn [db _] + (dissoc db ::result ))) + +(def missing-vendor-schema + (m/schema [:map-of :string + [:map + [:default-account schema/reference]]])) + +(defn create-missing-vendors [{:keys [vendors]}] + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::create-vendors]) + vendors-to-create (filter (fn [v] (:checked v)) + (vals data))] + [form-builder/builder {:id ::create-vendors + :submit-event [::create-vendors] + :schema missing-vendor-schema} + [:article.message.is-warning.is-paddingless + [:div.message-header + "Some vendors could not be found"] + + [:div.message-body + [:h2 "Check the vendors you want to create"] + (for [v vendors] + ^{:key v} + [:div.field.is-grouped + [:div.control + [form-builder/raw-field-v2 {:field [v :checked]} + [com/checkbox {:label v}]]] + + [:div.control + [form-builder/raw-field-v2 {:field [v :default-account]} + [com/search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i} + [:name :id :location]])}]]]]) + [form-builder/error-notification] + [form-builder/submit-button {:disabled (when-not (seq vendors-to-create) "disabled")} + (str "Create " (count vendors-to-create) " vendors")] + [:div.is-clearfix]]]])) (defn admin-excel-import-content [] [:div - (let [{{:keys [vendors-not-found already-imported imported]} :rows - :keys [create-vendors] - :or {create-vendors #{}} - :as excel-import-data} @(re-frame/subscribe [::excel-import]) - data @(re-frame/subscribe [::expense-accounts]) - form @(re-frame/subscribe [::forms/form ::excel-import]) - - - chooseable-expense-accounts @(re-frame/subscribe [::subs/all-accounts]) - change-event [::all-events/change-form [::expense-accounts]]] + (let [{:keys [vendors-not-found errors already-imported imported]} @(re-frame/subscribe [::result])] [:div [:h1.title "Import Invoices from Integreat Excel"] (when (seq vendors-not-found) - [:article.message.is-warning.is-paddingless - [:div.message-header - "Some vendors could not be found"] - - [:div.message-body - [:h2 "Check the vendors you want to create"] - [:div.columns - (for [[i vendor-group] (map vector (range) (partition-all (max 1 (/ (count vendors-not-found) 3)) vendors-not-found))] - ^{:key i} - [:div.column - (for [v vendor-group] - ^{:key v} [:div.field.is-grouped - [:p.control - [:label.checkbox - [:input {:value v - :checked (if (create-vendors v) - "checked" - "") - - :type "checkbox" - :on-change (fn [] - (re-frame/dispatch [::toggle-vendor v]))}] - (str " " v)]] - - [:p.control - [bind-field - [typeahead-v3 {:entities chooseable-expense-accounts - :entity->text (fn [x ] (str (:numeric-code x) " - " (:name x))) - :type "typeahead-v3" - :field [v :default-account-id] - :event change-event - :subscription data}]]]])])] - [:div - [:button.button.is-pulled-right - {:on-click (dispatch-event [::create-vendors]) - :disabled (when-not (seq create-vendors) - "disabled") - } - (str "Create " (count create-vendors) " vendors")]] - [:div.is-clearfix]]]) - [bind-field - [:textarea.textarea {:rows "20" - :field :excel-rows - :type "text" - :event [::forms/change ::excel-import] - :subscription (:data form)}]] - - [:button.button.is-large.is-pulled-right.is-primary {:on-click (dispatch-event [::save]) - :class (str @(re-frame/subscribe [::forms/loading-class ::excel-import]) - (when (:error form) " animated shake")) - :disabled (when (= :saving (:status form)) "disabled")} "Import"] - + [create-missing-vendors {:vendors vendors-not-found}] + ) + [form-builder/builder {:id ::form + :submit-event [::save]} + [form-builder/raw-field-v2 {:field :excel-rows} + [:textarea.textarea {:rows "20" + :type "text"}]] + [form-builder/error-notification] + [form-builder/submit-button "Import"]] [:div.is-clearfix] [:div.is-clearfix [:p @@ -171,29 +117,49 @@ [:p (when already-imported (str already-imported " rows already imported."))]] - (when-let [errors (:errors (:rows excel-import-data))] + (when errors [:div [:h3 (str "Import errors (" (min 100 (count errors)) " / " (count errors) " )")] [:table.table.is-fullwidth [:thead - [:th "Date"] - [:th "Invoice #"] - [:th "Client"] - [:th "Vendor"] - [:th "Amount"] - [:th "Errors"]] + [:td "Date"] + [:td "Invoice #"] + [:td "Client"] + [:td "Vendor"] + [:td "Amount"] + [:td "Errors"]] - (for [{:keys [raw-date invoice-number client vendor-name amount] row-errors :errors} (take 100 errors)] - ^{:key (str raw-date invoice-number client vendor-name amount)} - [:tr - [:td raw-date] - [:td invoice-number] - [:td client] - [:td vendor-name] - [:td amount] - [:td (map (fn [{:keys [info]}] ^{:key info} [:p info]) row-errors)]])]])])]) + [:tbody + (for [{:keys [raw-date invoice-number client vendor-name amount] row-errors :errors} (take 100 errors)] + ^{:key (str raw-date invoice-number client vendor-name amount)} + [:tr + [:td raw-date] + [:td invoice-number] + [:td client] + [:td vendor-name] + [:td amount] + [:td (map (fn [{:keys [info]}] ^{:key info} [:p info]) row-errors)]])]]])])]) -(defn admin-excel-import-page [] +(defn admin-excel-import-page-internal [] [side-bar-layout {:side-bar [admin-side-bar {}] :main [admin-excel-import-content]}]) + +(re-frame/reg-event-fx + ::mounted + (fn [_ _] + {:dispatch [::forms/start-form ::form]})) + +(re-frame/reg-event-fx + ::unmounted + (fn [{:keys [db]} _] + {:dispatch-n [[::forms/form-closing ::form] + [::forms/form-closing ::create-vendors]] + :db (dissoc db ::results)})) + +(defn admin-excel-import-page [] + (r/create-class + {:display-name "excel-import-page" + :component-will-unmount #(re-frame/dispatch-sync [::unmounted]) + :component-did-mount #(re-frame/dispatch [::mounted]) + :reagent-render admin-excel-import-page-internal})) diff --git a/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs b/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs index 2dcfcee6..bad6a6f2 100644 --- a/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs +++ b/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs @@ -79,6 +79,7 @@ (defn table [{:keys [status data-page]}] (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page]) params @(re-frame/subscribe [::params]) + is-admin? @(re-frame/subscribe [::subs/is-admin?]) statuses @(re-frame/subscribe [::status/multi ::refresh])] [grid/grid {:data-page data-page :column-count 5} @@ -105,5 +106,6 @@ [:li (:name a) [:div.tag (->$ (:balance a))]])]] [grid/cell {} [:div.buttons - [buttons/fa-icon {:event [::delete-requested (:id c)] - :icon "fa-times"}]]]])]]])) + (when is-admin? + [buttons/fa-icon {:event [::delete-requested (:id c)] + :icon "fa-times"}])]]])]]])) diff --git a/src/cljs/auto_ap/views/pages/admin/reminders.cljs b/src/cljs/auto_ap/views/pages/admin/reminders.cljs deleted file mode 100644 index abbfd966..00000000 --- a/src/cljs/auto_ap/views/pages/admin/reminders.cljs +++ /dev/null @@ -1,193 +0,0 @@ -(ns auto-ap.views.pages.admin.reminders - (:require-macros [cljs.core.async.macros :refer [go]]) - (:require [re-frame.core :as re-frame] - [auto-ap.views.components.paginator :refer [paginator]] - [auto-ap.views.components.sorter :refer [sorted-column]] - [auto-ap.entities.vendors :as entity] - [reagent.core :as reagent] - [auto-ap.subs :as subs] - [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event date-time->str date->str horizontal-field bind-field]] - [cljs.reader :as edn] - - [auto-ap.routes :as routes] - [bidi.bidi :as bidi])) -(re-frame/reg-sub - ::editing-reminder - (fn [db] - (::editing-reminder db))) - -(re-frame/reg-sub - ::reminder-page - (fn [db] - (::reminder-page db))) - -(re-frame/reg-sub - ::reminder-params - (fn [db] - (::reminder-params db))) - -(re-frame/reg-event-db - ::edit - (fn [db [_ which]] - (assoc db ::editing-reminder which))) - -(re-frame/reg-event-db - ::change - (fn [db [_ field v]] - (assoc-in db (into [::editing-reminder] field) v))) - -(re-frame/reg-event-fx - ::mounted - (fn [{:keys [db]} _] - {:db (assoc db ::reminder-params {:start 0}) - :dispatch [::invalidated]})) - -(re-frame/reg-event-fx - ::params-changed - (fn [{:keys [db]} [_ params]] - {:db (update db ::reminder-params merge params) - :dispatch [::invalidated]})) - -(re-frame/reg-event-fx - ::save - (fn [{:keys [db]}] - (let [edited-reminder (::editing-reminder db)] - (println edited-reminder) - {:http {:token (:user db) - :method :put - :body (pr-str (dissoc edited-reminder :sent :scheduled)) - :headers {"Content-Type" "application/edn"} - :uri (str "/api/reminders/" (:id edited-reminder)) - :on-success [::save-complete] - :on-error [::save-error]}}))) - -(re-frame/reg-event-fx - ::save-complete - (fn [{:keys [db]}] - {:dispatch [::edit nil]})) - -(re-frame/reg-event-fx - ::save-error - (fn [{:keys [db]}] - {:dispatch [::change [:error] true]})) - -(re-frame/reg-event-fx - ::invalidated - (fn [{:keys [db]}] - {:graphql {:token (:user db) - :query-obj {:venia/queries [[:reminder_page - (::reminder-params db) - - [[:reminders [:id :email :sent :scheduled :subject :body [:vendor [:name :id]] ]] - :total - :start - :end]]]} - - :on-success [::received]}})) - - -(re-frame/reg-event-db - ::received - (fn [db [_ reminders]] - (assoc db ::reminder-page (first (:reminder-page reminders))))) - -(defn edit-dialog [] - (let [editing-reminder @(re-frame/subscribe [::editing-reminder])] - [:div.modal.is-active - [:div.modal-background {:on-click (fn [] (re-frame/dispatch [::edit nil]))}] - - [:div.modal-card - [:header.modal-card-head - [:p.modal-card-title - (str "Reminder for " (:name (:vendor editing-reminder)))] - (when (:error editing-reminder) - [:span.icon.has-text-danger - [:i.fa.fa-exclamation-triangle]]) - [:button.delete {:on-click (fn [] (re-frame/dispatch [::edit nil]))}]] - [:section.modal-card-body - [horizontal-field - [:label.label "Email"] - [:div.control - [bind-field - [:input.input {:type "text" - :field :email - :event ::change - :subscription editing-reminder}]]]] - [horizontal-field - [:label.label "Subject"] - [:div.control - [bind-field - [:input.input {:type "text" - :field :subject - :event ::change - :subscription editing-reminder}]]]] - - [horizontal-field - [:label.label "Body"] - [:div.control - - [bind-field - [:textarea.textarea.is-expanded {:type "text" - :field :body - :event ::change - :subscription editing-reminder}]]]] - - (when (:saving? editing-reminder) [:div.is-overlay {:style {"backgroundColor" "rgba(150,150,150, 0.5)"}}])] - - [:footer.modal-card-foot - [:button.button.is-primary {:on-click (fn [] (re-frame/dispatch [::save])) - #_#_:disabled (when (not (s/valid? ::entity/vendor editing-reminder )) - "disabled")} - [:span "Save"] - (when (:saving? editing-reminder) - [:span.icon - [:i.fa.fa-spin.fa-spinner]])]]]])) - -(defn reminders-table [] - (let [{:keys [reminders start end total count]} @(re-frame/subscribe [::reminder-page]) - {:keys [sort-by asc]} @(re-frame/subscribe [::reminder-params]) - reminders (or reminders [])] - [:div - [:div.is-pulled-right - [paginator {:start start - :end end - :total total - :count count - :on-change (fn [params] - (re-frame/dispatch [::params-changed params]))}]] - [:table {:class "table", :style {:width "100%"}} - [:thead - [:tr - (for [[sort-key name] [["vendor" "Vendor"] - ["scheduled" "Scheduled Date"] - ["sent" "Status"] - ["email" "Email"]]] - ^{:key name} - [sorted-column {:on-sort (fn [params] (re-frame/dispatch [::params-changed params])) - :style {:width "20%" :cursor "pointer"} - :sort-key sort-key - :sort-by sort-by - :asc asc} - name])]] - [:tbody (for [{:keys [id vendor scheduled sent email ] :as r} reminders] - ^{:key id} - [:tr (when-not sent - {:on-click (fn [] (re-frame/dispatch [::edit r])) :style {:cursor "pointer"}}) - [:td (:name vendor)] - [:td (date->str scheduled)] - [:td (when sent - [:span [:span.icon [:i.fa.fa-check]] "Sent " (date-time->str sent)]) ] - [:td email]])]] - - (when @(re-frame/subscribe [::editing-reminder]) - [edit-dialog])])) - - -(defn admin-reminders-page [] - [(with-meta - (fn [] - [:div - [:h1.title "Reminders"] - [reminders-table]]) - {:component-did-mount (fn [] - (re-frame/dispatch [::mounted]))})]) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index bf967e9c..e43ec1de 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -1,23 +1,22 @@ (ns auto-ap.views.pages.admin.rules.form (:require - [auto-ap.entities.transaction-rule :as entity] [auto-ap.events :as events] [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] [auto-ap.status :as status] [auto-ap.subs :as subs] - [auto-ap.views.components.button-radio :refer [button-radio]] + [auto-ap.views.components :as com] [auto-ap.views.components.expense-accounts-field :as expense-accounts-field - :refer [expense-accounts-field]] + :refer [expense-accounts-field-v2]] [auto-ap.views.components.layouts :as layouts] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.components.typeahead.vendor - :refer [search-backed-typeahead]] + [auto-ap.views.components.level :refer [left-stack]] [auto-ap.views.pages.admin.rules.common :refer [default-read]] [auto-ap.views.pages.admin.rules.results-modal :as results-modal] - [auto-ap.views.utils :refer [dispatch-event with-user]] - [clojure.spec.alpha :as s] + [auto-ap.views.utils :refer [coerce-float dispatch-event with-user]] [clojure.string :as str] + [malli.core :as m] [re-frame.core :as re-frame] [reagent.core :as r] [vimsical.re-frame.cofx.inject :as inject] @@ -29,7 +28,6 @@ ::default-note :<- [::forms/form ::form] (fn [{{:keys [client description amount-lte amount-gte dom-lte dom-gte]} :data}] - (str/join " - " (filter (complement str/blank?) [(:code client) description @@ -47,12 +45,6 @@ (when dom-lte (str "<" dom-lte))))])))) -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::form] - (fn [{:keys [data]} _] - (s/valid? ::entity/transaction-rule data))) - (re-frame/reg-sub ::query :<- [::forms/form ::form] @@ -89,10 +81,6 @@ (assoc :bank-account-id (:id (:bank-account data))))} default-read]}]})) -(defn ungraphql-transaction-rule [x] - (-> x - (update :amount-lte #(some-> % js/parseFloat)) - (update :amount-gte #(some-> % js/parseFloat)))) (re-frame/reg-sub ::test-query @@ -160,6 +148,9 @@ :accounts :yodlee-merchant :transaction-approval-status]) + + (update :amount-lte coerce-float) + (update :amount-gte coerce-float) (update :accounts (fn [xs] (mapv #(-> % (assoc :amount-percentage (* (:percentage %) 100.0))) @@ -185,18 +176,21 @@ :on-success [::changed [:vendor-preferences]]}]}))) (re-frame/reg-event-db - ::changed - (forms/change-handler ::form - (fn [data field value] - (cond (and (= [:vendor-preferences] field) - value - (expense-accounts-field/can-replace-with-default? (:accounts data))) - [[:accounts] (expense-accounts-field/default-account (:accounts data) - (:default-account value) - (:total data) - [])] - :else - [])))) + ::changed + (forms/change-handler ::form + (fn [data field value] + (cond (and (= [:vendor-preferences] field) + value + (expense-accounts-field/can-replace-with-default? (:accounts data))) + [[:accounts] (expense-accounts-field/default-account (:accounts data) + (:default-account value) + (:total data) + [])] + + (= [:client] field) + [[:bank-account] nil] + :else + [])))) (re-frame/reg-event-fx ::saving @@ -205,9 +199,9 @@ {:graphql {:token user :query-obj query + :owns-state {:single ::form} :on-success (fn [result] - [::updated (:upsert-transaction-rule result)]) - :on-error [::forms/save-error ::form]}})) + [::updated (:upsert-transaction-rule result)])}})) (re-frame/reg-event-fx ::test-clicked @@ -223,9 +217,8 @@ (re-frame/reg-event-fx ::updated [(forms/triggers-stop ::form)] - (fn [{:keys [db]} [_ {:keys [rule-saved]} result]] - {:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])}) - :dispatch (conj rule-saved (:upsert-transaction-rule result))})) + (fn [{:keys [db]} _] + {:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})})) (re-frame/reg-event-fx ::succeeded-test @@ -238,10 +231,6 @@ ;; VIEWS -(def rule-form (forms/vertical-form {:can-submit [::can-submit] - :change-event [::changed] - :submit-event [::saving ] - :id ::form})) (re-frame/reg-event-fx ::mounted @@ -261,133 +250,119 @@ {::track/dispose [{:id ::client} {:id ::vendor-change}]})) -(defn form-contents [params] +(def rule-schema + (m/schema [:map + [:client {:optional true} + [:maybe schema/reference]] + [:bank-account {:optional true} + [:maybe schema/reference]] + [:description + schema/not-empty-string] + [:amount-gte {:optional true} + [:maybe schema/money]] + [:amount-lte {:optional true} + [:maybe schema/money]] + [:dom-gte {:optional true} + [:maybe [:int {:min 1 :max 31}]]] + [:dom-lte {:optional true} + [:maybe [:int {:min 1 :max 31}]]] + [:vendor {:optional true} + [:maybe schema/reference]] + [:transaction-approval-status {:optional true} + [:maybe schema/approval-status]] + [:note {:optional true} + [:maybe :string]]])) + +(defn form-contents [] [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} - (let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field raw-field error-notification submit-button ]} rule-form - default-note @(re-frame/subscribe [::default-note]) - test-state @(re-frame/subscribe [::status/single ::test])] - ^{:key id} - (form-inline (assoc params :title "New Transaction Rule") - [:<> - + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) + default-note @(re-frame/subscribe [::default-note]) + test-state @(re-frame/subscribe [::status/single ::test])] + [form-builder/builder {:change-event [::changed] + :submit-event [::saving ] + :id ::form + :schema rule-schema} + [form-builder/section {:title "Transaction Rule"} + [form-builder/field-v2 {:required? true + :field :client} + "Client" + [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients]) + :auto-focus true + :entity->text :name}]] - (field "Client" - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) - :auto-focus true - :entity->text :name - :type "typeahead-v3" - :field [:client] - :spec ::entity/client}]) + + [form-builder/field-v2 {:field [:bank-account]} + "Bank account" + [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)]) + :entity->text :name}]] - - (with-meta - (field "Bank account" - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)]) - :entity->text :name - :type "typeahead-v3" - :field [:bank-account] - :spec ::entity/bank-account}]) - ;; TODO this forces unmounting when client changes, since it is an "uncontorlled" input - {:key (str "client-" (:id (:client data)))}) + [form-builder/field-v2 {:field :description + :required? true} + [:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ] + [:input.input {:type "text"}]] - (field "Yodlee Merchant" - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/yodlee-merchants]) - :entity->text #(str (:name %) " - " (:yodlee-id %)) - :type "typeahead-v3" - :field [:yodlee-merchant]}]) + [:div.field + [:p.help "Amount"] + [left-stack + [form-builder/raw-field-v2 {:field :amount-gte} + [com/money-input {:placeholder ">="}]] + "-" + [form-builder/raw-field-v2 {:field :amount-lte} + [com/money-input {:placeholder "<="}]]]] - (field [:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ] - [:input.input {:type "text" - :field [:description] - :spec ::entity/description}]) + [:div.field + [:p.help "Day of month"] + [left-stack + [form-builder/raw-field-v2 {:field :dom-gte} + [com/number-input {:placeholder ">=" + :style {:width "7em"}}]] + "-" + [form-builder/raw-field-v2 {:field :dom-lte} + [com/number-input {:placeholder "<=" + :style {:width "7em"}}]]]] + - [:div.field - [:p.help "Amount"] - [:div.control - [:div.columns - [:div.column - (raw-field - [:input.input {:type "number" - :placeholder ">=" - :field [:amount-gte] - :spec ::entity/amount-gte - :step "0.01"}])] - [:div.column - (raw-field - [:input.input {:type "number" - :placeholder "<=" - :field [:amount-lte] - :spec ::entity/amount-lte - :step "0.01"}])]]]] + [:h2.title.is-4 "Outcomes"] - [:div.field - [:p.help "Day of Month"] - [:div.control - [:div.columns - [:div.column - (raw-field - [:input.input {:type "number" - :placeholder ">=" - :field [:dom-gte] - :spec ::entity/dom-gte - :precision 0 - :step "1"}])] - [:div.column - (raw-field - [:input.input {:type "number" - :placeholder "<=" - :field [:dom-lte] - :spec ::entity/dom-lte - :precision 0 - :step "1"}])]]]] + [form-builder/field-v2 {:field :vendor} + "Assign Vendor" + [com/search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]])}]] - [:h2.title.is-4 "Outcomes"] + [form-builder/field-v2 {:field :accounts} + "Accounts" + [expense-accounts-field-v2 {:descriptor "account asssignment" + :percentage-only? true + :client (:client data) + :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))])) + :max 100}]] - (field "Assign Vendor" - [search-backed-typeahead {:search-query (fn [i] - [:search_vendor - {:query i} - [:name :id]]) - :type "typeahead-v3" - :field [:vendor]}]) + [form-builder/field-v2 {:field :transaction-approval-status} + "Approval Status" + [com/button-radio-input + {:options [[:unapproved "Unapproved"] + [:requires-feedback "Client Review"] + [:approved "Approved"] + [:excluded "Excluded from Ledger"]]}]] - (with-meta - (field nil - [expense-accounts-field {:type "expense-accounts" - :descriptor "account asssignment" - :percentage-only? true - :client (:client data) - :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))])) - :max 100 - :field [:accounts]}]) - {:key (str (some-> data :vendor :id str) "-" (some-> data :client :id str))}) + [form-builder/field-v2 {:field :note} + "Note" + [:input.input {:type "text" + :placeholder default-note}]] - (field "Approval Status" - [button-radio - {:type "button-radio" - :field [:transaction-approval-status] - :options [[:unapproved "Unapproved"] - [:requires-feedback "Client Review"] - [:approved "Approved"] - [:excluded "Excluded from Ledger"]]}]) - - (field "Note" - [:input.input {:type "text" - :field [:note] - :placeholder default-note - :spec (s/nilable ::entity/note)}]) - - [:div.is-divider] - (error-notification) - [:div.columns - [:div.column - [:a.button.is-medium.is-fullwidth.is-outlined {:on-click (dispatch-event [::test-clicked]) - :disabled (status/disabled-for test-state) - :class (status/class-for test-state)} - "Test Rule"]] - [:div.column - (submit-button "Save")]]]))]) + [:div.is-divider] + [form-builder/error-notification] + [:div.columns + [:div.column + [:a.button.is-medium.is-fullwidth.is-outlined {:on-click (dispatch-event [::test-clicked]) + :disabled (status/disabled-for test-state) + :class (status/class-for test-state)} + "Test Rule"]] + [:div.column + [form-builder/submit-button {:class ["is-fullwidth"]} + "Save"]]]]])]) (defn form [_] (r/create-class diff --git a/src/cljs/auto_ap/views/pages/admin/users.cljs b/src/cljs/auto_ap/views/pages/admin/users.cljs index c7489f83..4c925c47 100644 --- a/src/cljs/auto_ap/views/pages/admin/users.cljs +++ b/src/cljs/auto_ap/views/pages/admin/users.cljs @@ -1,25 +1,15 @@ (ns auto-ap.views.pages.admin.users - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [clojure.string :as str] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.entities.clients :as entity] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.pages.admin.users.table :as table] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.utils :refer [login-url dispatch-value-change bind-field horizontal-field dispatch-event]] - [auto-ap.views.components.grid :as grid] - [auto-ap.utils :refer [by replace-by]] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.status :as status] - [auto-ap.views.pages.admin.users.form :as form] - [auto-ap.effects.forward :as forward])) - - + (:require + [auto-ap.effects.forward :as forward] + [auto-ap.status :as status] + [auto-ap.utils :refer [replace-by]] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.admin.users.form :as form] + [auto-ap.views.pages.admin.users.table :as table] + [re-frame.core :as re-frame] + [reagent.core :as reagent])) (re-frame/reg-sub ::params diff --git a/src/cljs/auto_ap/views/pages/admin/users/form.cljs b/src/cljs/auto_ap/views/pages/admin/users/form.cljs index 3942de13..91a3a67f 100644 --- a/src/cljs/auto_ap/views/pages/admin/users/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/users/form.cljs @@ -1,129 +1,104 @@ (ns auto-ap.views.pages.admin.users.form - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [clojure.string :as str] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.entities.clients :as entity] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.utils :refer [login-url dispatch-value-change bind-field horizontal-field dispatch-event with-user]] - [auto-ap.views.components.grid :as grid] - [auto-ap.utils :refer [by replace-if]] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.status :as status] - [auto-ap.forms :as forms] - [auto-ap.views.components.modal :as modal])) + (:require + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components :as com] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [malli.core :as m] + [re-frame.core :as re-frame])) -(re-frame/reg-sub - ::can-submit - (fn [db] - true)) +(def client-schema + (m/schema [:map [:client schema/reference]])) -(re-frame/reg-event-db - ::changed - (forms/change-handler ::form - (fn [data field value] - []))) - - - -(re-frame/reg-event-db - ::add-client - [(forms/in-form ::form)] - (fn [form [_ d]] - (let [client (get @(re-frame/subscribe [::subs/clients-by-id]) - (get-in form [:data :adding-client]))] - (update-in form [:data :clients] conj client )))) - -(re-frame/reg-event-db - ::remove-client - [(forms/in-form ::form)] - (fn [form [_ d]] - (update-in form [:data :clients] #(filter (fn [c] (not= (:id c) d)) %)))) +(def user-schema + (m/schema + [:map + [:name schema/not-empty-string] + [:role [:enum :none :user :manager :power_user :admin]] + [:clients {:optional true} + [:maybe + [:sequential client-schema]]]])) (re-frame/reg-event-fx ::saving [with-user (forms/in-form ::form)] (fn [{:keys [db user]} [_]] - {:graphql - {:token user - :owns-state {:single ::form} - :query-obj {:venia/operation {:operation/type :mutation - :operation/name "EditUser"} - :venia/queries [{:query/data [:edit-user - {:edit-user (-> (:data db) - (update :clients #(map :id %)) - (select-keys #{:id :name :clients :role}))} - [:id :name :role [:clients [:id :name]]]]}]} - :on-success [::saved]}})) + (if (m/validate user-schema (:data db)) + {:graphql + {:token user + :owns-state {:single ::form} + :query-obj {:venia/operation {:operation/type :mutation + :operation/name "EditUser"} + :venia/queries [{:query/data [:edit-user + {:edit-user (-> (:data db) + (update :clients #(map (comp :id :client) %)) + (select-keys #{:id :name :clients :role}))} + [:id :name :role [:clients [:id :name]]]]}]} + :on-success [::saved]}} + + {:dispatch-n [[::forms/attempted-submit ::form] + [::status/error ::form [{:message "Please fix the errors and try again."}]]]}) + )) (re-frame/reg-event-fx ::saved (forms/triggers-stop ::form) - (fn [{:keys [db]} [_ {:keys [edit-user]}]] + (fn [_ _] {:dispatch [::modal/modal-closed]})) -(def user-form (forms/vertical-form {:submit-event [::saving] - :change-event [::changed] - :can-submit [::can-submit] - :id ::form})) + + + (defn form [] - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field raw-field error-notification submit-button]} user-form] - (form-inline {} - [:<> - (field "Name" - [:input.input {:type "text" - :field [:name] - :spec ::entity/name}]) - [:div.field - [:p.help "Role"] - [:div.control - [:div.select - [raw-field - [:select {:type "select" - :field [:role]} - [:option {:value ":none"} "None"] - [:option {:value ":user"} "User"] - [:option {:value ":manager"} "Manager"] - [:option {:value ":power_user"} "Power User"] - [:option {:value ":admin"} "Admin"]]]]]] - (when (#{":user" ":manager" ":power_user"} (:role data)) - [:div.field - [:p.help "Clients"] - [:div.control - [:div.field.has-addons - [:div.control - [:div.select - [raw-field - [:select {:type "select" - :field [:adding-client]} - [:option] - (let [used-clients (set (map :id (:clients data)))] - (for [{:keys [id name] :as client} @(re-frame/subscribe [::subs/clients]) - :when (not (used-clients id))] - ^{:key id} [:option {:value id} name]))]]]] - [:p.control - [:button.button.is-primary {:on-click (dispatch-event [::add-client])} "Add"]]] + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) + clients @(re-frame/subscribe [::subs/clients])] + [:<> + [form-builder/builder {:submit-event [::saving] + :id ::form + :schema user-schema} + [form-builder/field-v2 {:required? true + :field :name} + "Name" + [:input.input {:type "text"}]] + [form-builder/field-v2 {:required? true + :field :role} + "Role" + [com/select-field {:options [[:none "None"] + [:user "User"] + [:manager "Manager"] + [:power_user "Power User"] + [:admin "Admin"]] + :allow-nil? false + :keywordize? true}]] + (when (#{:user :manager :power_user} (:role data)) + [form-builder/field-v2 {:field :clients} + "Client" + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client} + [com/entity-typeahead + {:entities clients + :entity->text :name + :style {:width "13em"}}]]] + :key-fn :id + :schema [:sequential client-schema] + :new-text "Grant access to client"}]]) + + [form-builder/hidden-submit-button]]])) - [:ul - (for [{:keys [id name]} (:clients data)] - ^{:key id} [:li name [:a.icon {:on-click (dispatch-event [::remove-client id])} [:i.fa.fa-times ]]])]]])]))) (re-frame/reg-event-fx ::editing (fn [{:keys [db]} [_ d]] - {:db (-> db - (forms/start-form ::form d)) - :dispatch [::modal/modal-requested {:title (str "Edit user " (:name d)) - :body [form] - :cancel? false - :confirm {:value "Save" + {:db (-> db + (forms/start-form ::form (update d :clients #(mapv (fn [x] {:client x :id (random-uuid)}) %)))) + :dispatch [::modal/modal-requested {:title (str "Edit user " (:name d)) + :body [form] + :cancel? false + :confirm {:value "Save" :status-from [::status/single ::form] - :class "is-primary" - :on-click (dispatch-event [::saving]) + :class "is-primary" + :on-click (dispatch-event [::saving]) :close-event [::status/completed ::form]}}]})) diff --git a/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs b/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs index 65182f73..1328c22d 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs @@ -1,65 +1,53 @@ (ns auto-ap.views.pages.admin.vendors.merge-dialog (:require [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] [auto-ap.status :as status] [auto-ap.subs :as subs] + [auto-ap.views.components :as com] [auto-ap.views.components.modal :as modal] - [auto-ap.views.components.typeahead.vendor - :refer [search-backed-typeahead]] [auto-ap.views.utils :refer [dispatch-event]] + [malli.core :as m] [re-frame.core :as re-frame])) -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::form] - (fn [{:keys [data]}] - (println data) - (and (:from data) - (:to data)))) - -(def merge-form (forms/vertical-form {:submit-event [::save] - :change-event [::forms/change ::form] - :can-submit [::can-submit] - :id ::form})) +(def merge-schema + (m/schema [:map + [:from schema/reference] + [:to schema/reference]])) (defn form [] - (let [_ @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field]} merge-form] - - - (form-inline {} - [:<> - (field "Form Vendor (will be deleted)" - [search-backed-typeahead {:search-query (fn [i] - [:search_vendor - {:query i} - [:name :id]]) - :type "typeahead-v3" - :auto-focus true - :field [:from]}]) + [form-builder/builder {:submit-event [::try-save] + :id ::form + :schema merge-schema} + [form-builder/field-v2 {:field :from} + "Form Vendor (will be deleted)" + [com/search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :auto-focus true}]] - (field "To Vendor" - [search-backed-typeahead {:search-query (fn [i] - [:search_vendor - {:query i} - [:name :id]]) - :type "typeahead-v3" - :field [:to]}])]))) + [form-builder/field-v2 {:field :to} + "To Vendor" + [com/search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]])}]] + [form-builder/hidden-submit-button]]) (re-frame/reg-event-fx ::show (fn [{:keys [db]} _] - {:dispatch [::modal/modal-requested {:title "Merge Vendors" - :body [form] + {:dispatch [::modal/modal-requested {:title "Merge Vendors" + :body [form] :confirm {:value "Merge" :status-from [::status/single ::form] :class "is-primary" - :on-click (dispatch-event [::save]) - :can-submit [::can-submit] + :on-click (dispatch-event [::try-save]) :close-event [::status/completed ::form]}}] - :db (forms/start-form db ::form {})} - )) + :db (forms/start-form db ::form {})})) (re-frame/reg-event-fx ::complete @@ -81,3 +69,12 @@ {:from (:id from) :to (:id to)} []]}]} :on-success [::complete]}}))) + +(re-frame/reg-event-fx + ::try-save + [(forms/in-form ::form)] + (fn [{:keys [db]}] + (if (not (m/validate merge-schema (:data db))) + {:dispatch-n [[::status/error ::form [{:message "Please correct any errors and try again"}]] + [::forms/attempted-submit ::form]]} + {:dispatch [::save]}))) diff --git a/src/cljs/auto_ap/views/pages/admin/yodlee.cljs b/src/cljs/auto_ap/views/pages/admin/yodlee.cljs deleted file mode 100644 index 6d159d5d..00000000 --- a/src/cljs/auto_ap/views/pages/admin/yodlee.cljs +++ /dev/null @@ -1,415 +0,0 @@ -(ns auto-ap.views.pages.admin.yodlee - (:require [re-frame.core :as re-frame] - [auto-ap.forms :as forms] - [reagent.core :as reagent] - [clojure.string :as str] - [cljs-time.format :as f] - [cljs-time.core :as time] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.entities.clients :as entity] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field str->date date->str with-user]] - [auto-ap.views.components.modal :as modal] - [auto-ap.status :as status] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi])) - - - -(re-frame/reg-sub - ::authentication - (fn [db] - (-> db ::yodlee :authentication))) - -(re-frame/reg-sub - ::can-submit - (fn [db] - true)) - -(re-frame/reg-sub - ::loading? - (fn [db] - (-> db ::yodlee :loading?))) - -(re-frame/reg-sub - ::accounts - (fn [db] - (-> db ::yodlee :accounts))) - -(re-frame/reg-sub - ::accounts-loading? - (fn [db] - (-> db ::yodlee :accounts-loading?))) - -(re-frame/reg-sub - ::provider-accounts-loading? - (fn [db] - (-> db ::provider-accounts-loading?))) - -(re-frame/reg-sub - ::provider-accounts - (fn [db] - (-> db ::provider-accounts))) - -(re-frame/reg-event-fx - ::authenticate-with-yodlee - (fn [{:keys [db]} _] - {:db (assoc-in db [::yodlee :loading?] true) - :http {:token (:user db) - :method :get - :headers {"Content-Type" "application/edn"} - :uri (str "/api/yodlee/fastlink") - :on-success [::authenticated] - :on-error [::save-error]}})) - -(re-frame/reg-event-fx - ::mounted - (fn [{:keys [db]} _] - {:db (-> db - (assoc ::yodlee {:provider-accounts-loading? true}) - (assoc ::save-error nil) - (assoc ::provider-accounts []) - (assoc ::provider-accounts-loading? true)) - :http {:token (:user db) - :method :get - :headers {"Content-Type" "application/edn"} - :uri (str "/api/yodlee/provider-accounts") - :on-success [::got-provider-accounts] - :on-error [::save-error]}})) - -(re-frame/reg-event-fx - ::kicked - (fn [{:keys [db]} [_ id state]] - {:dispatch [::mounted]})) - -(re-frame/reg-event-fx - ::kicked - (fn [{:keys [db]} [_ id state]] - {:dispatch [::mounted]})) - -(re-frame/reg-event-fx - ::kick - (fn [{:keys [db]} [_ id]] - {:http {:token (:user db) - :method :post - :headers {"Content-Type" "application/edn"} - :uri (str "/api/yodlee/provider-accounts/" id) - :on-success [::kicked id :kicked] - :on-error [::kicked id :errored]}})) - -(re-frame/reg-event-fx - ::got-accounts - (fn [{:keys [db]} [_ accounts]] - {:db (-> db - (assoc-in [::yodlee :accounts] accounts) - (assoc-in [::yodlee :accounts-loading?] false))})) - -(re-frame/reg-event-fx - ::got-provider-accounts - (fn [{:keys [db]} [_ accounts]] - {:db (-> db - (assoc-in [::provider-accounts] accounts) - (assoc-in [::provider-accounts-loading?] false))})) - -(re-frame/reg-event-fx - ::authenticated - (fn [{:keys [db]} [_ authentication]] - {:db (-> db - (assoc-in [::yodlee :authentication] authentication) - (assoc-in [::yodlee :loading?] false))})) - -(re-frame/reg-event-fx - ::authenticated-mfa - (fn [{:keys [db]} [_ provider-account-id authentication]] - {:db (-> db - (assoc-in [::yodlee :authentication] authentication) - (assoc-in [::yodlee :loading?] false) - (forms/stop-form [::mfa-form provider-account-id]))})) - -(re-frame/reg-event-fx - ::save-error - (fn [{:keys [db]} [_ authentication]] - {:db (assoc db ::load-error "error")})) - -(defn yodlee-link-button [] - [:div - (let [authentication @(re-frame/subscribe [::authentication]) - loading? @(re-frame/subscribe [::loading?])] - - (if authentication - [:div - "Authentication successful!" - [:form {:action (:url authentication) :method "POST"} - [:input {:type "hidden" - :name "rsession" - :value (:session authentication)}] - [:input {:type "hidden" - :name "token" - :value (:token authentication)}] - [:input {:type "hidden" - :name "app" - :value (:app authentication)}] - - [:input {:type "hidden" - :name "redirectReq" - :value "true"}] - [:button.button.is-primary [:span [:span.icon [:i.fa.fa-external-link]] " Go to yodlee"]]]] - - [:button.button.is-primary {:class (if loading? "is-loading" "") :on-click (dispatch-event [::authenticate-with-yodlee])} "Authenticate with Yodlee"]))]) - -(defn yodlee-date->date [d] - (try - (some-> d - (str->date (:date-time-no-ms f/formatters)) - ) - (catch js/Error e - nil))) - -(defn yodlee-date->str [d] - (try - (or (some-> d - (str->date (:date-time-no-ms f/formatters)) - date->str) - "N/A") - (catch js/Error e - "N/A"))) - -(defn yodlee-accounts-table [accounts] - (let [bank-accounts @(re-frame/subscribe [::bank-accounts-by-yodlee-account-id])] - [:div - [:table.table - [:thead - [:tr - [:th "Account Name"] - [:th "Account Number"] - [:th "Yodlee Account Number"] - [:th "Balance"] - [:th "Yodlee Status"] - [:th "Usage"]]] - [:tbody - - (for [account accounts] - ^{:key (:id account)} [:tr - [:td (:accountName account)] - [:td (:accountNumber account)] - [:td (:id account)] - [:td.has-text-right (:amount (:balance account))] - [:td (str/join ", " (map :additionalStatus (:dataset account)))] - [:td - (when-let [bank-accounts (get bank-accounts (:id account))] - [:div.tags - (for [bank-account bank-accounts] - ^{:key (:id bank-account)} - [:div.tag (:name bank-account) " (" (:code bank-account) ")"])])] - ])]]])) - -(re-frame/reg-event-fx - ::reauthenticate-mfa - [with-user ] - (fn [{:keys [user db]} [_ provider-account-id ]] - {:db (forms/loading db [::mfa-form provider-account-id]) - :http {:token user - :method :post - :headers {"Content-Type" "application/edn"} - :uri (str "/api/yodlee/reauthenticate/" provider-account-id ) - :body {"loginForm" - {"row" - (->> (get-in db [::forms/forms [::mfa-form provider-account-id]]) - :data - :login - (sort-by (fn [[k v]] k)) - (map second) - (map (fn [row] - {"field" - (mapv (fn [[k v]] - {"id" k - "value" v}) - row)})))} - "field" - (mapv (fn [[k v]] - {"id" k - "value" v}) - (:mfa (:data (get-in db [::forms/forms [::mfa-form provider-account-id]]))))} - - :on-success [::authenticated-mfa provider-account-id] - :on-error [::forms/save-error [::mfa-form provider-account-id] ]}})) - -(re-frame/reg-event-fx - ::provider-account-refreshed - (fn [{:keys [db]} [_ i result]] - - {:db (assoc-in db [::provider-accounts] result) - :dispatch [::forms/form-closing [::refresh-provider-account i]]})) - -(re-frame/reg-event-fx - ::refresh-provider-account - [with-user ] - (fn [{:keys [user db]} [_ provider-account-id ]] - {:db (forms/loading db [::refresh-provider-account provider-account-id]) - :http {:token user - :method :post - :headers {"Content-Type" "application/edn"} - :uri (str "/api/yodlee/provider-accounts/refresh/" provider-account-id ) - :body {} - :on-success [::provider-account-refreshed provider-account-id] - :on-error [::forms/save-error [::refresh-provider-account provider-account-id] ]}})) - -(re-frame/reg-event-fx - ::provider-account-deleted - (fn [{:keys [db]} [_ i result]] - {:db (assoc-in db [::provider-accounts] result) - :dispatch-n [[::forms/form-closing [::refresh-provider-account i]] - [::modal/modal-closed ]]})) - -(re-frame/reg-event-fx - ::delete-provider-account - [with-user ] - (fn [{:keys [user db]} [_ provider-account-id ]] - {:http {:token user - :method :post - :owns-state {:single ::delete-provider-account} - :headers {"Content-Type" "application/edn"} - :uri (str "/api/yodlee/provider-accounts/delete/" provider-account-id ) - :body {} - :on-success [::provider-account-deleted provider-account-id] - :on-error [::forms/save-error [::delete-provider-account provider-account-id] ]}})) - - - -(re-frame/reg-event-fx - ::delete-requested - [with-user] - (fn [{:keys [user db]} [_ account-id]] - {:dispatch - [::modal/modal-requested {:title "Delete Provider account " - :body [:div "Are you sure you want to delete provider account " account-id "?"] - :confirm {:value "Delete provider account" - :status-from [::status/single ::delete-provider-account] - :class "is-danger" - :on-click (dispatch-event [::delete-provider-account account-id]) - :close-event [::status/completed ::delete-provider-account]} - :cancel? true}]})) - - -(defn delete-button [account-id] - [:button.button - {:on-click (dispatch-event [::delete-requested account-id])} - [:span.icon [:i.fa.fa-times]]]) - -(re-frame/reg-sub - ::bank-accounts-by-yodlee-account-id - :<- [::subs/bank-accounts] - (fn [bank-accounts] - (group-by :yodlee-account-id bank-accounts))) - -(defn yodlee-provider-accounts-table [] - (let [bank-accounts @(re-frame/subscribe [::bank-accounts-by-yodlee-account-id])] - - (if @(re-frame/subscribe [::provider-accounts-loading?]) - [:div "Loading..."] - [:div.columns - [:div.column.is-half - (doall - (for [account @(re-frame/subscribe [::provider-accounts]) - :let [{:keys [error status] :as g} @(re-frame/subscribe [::forms/form [::refresh-provider-account (:id account)]]) - total-usages (mapcat (comp bank-accounts :id) (:accounts account))]] - - ^{:key (:id account)} - [:div.card {:style {:margin-bottom "1em"}} - [:div.card-header - [:div.card-header-title "Provider account " (:id account)] - [:div.card-header-icon - (when (seq total-usages) - [:div.tags - [:div.tag.is-primary (count total-usages) " usages"]])] - [:div.card-header-icon - [delete-button (:id account)]] - [:div.card-header-icon - (cond - (= :loading status) [:button.button.is-disabled.is-loading [:i.fa.fa-refresh]] - error [:button.button.is-disabled [:span.icon [:i.fa.fa-exclamation-triangle]]] - :else - [:button.button - {:on-click (dispatch-event [::refresh-provider-account (:id account)])} - [:span.icon [:i.fa.fa-refresh]]])]] - [:div.card-content - - (if (> (some-> (-> account :dataset first :lastUpdated) - (yodlee-date->date ) - (time/interval (time/now)) - (time/in-days )) - 1) - [:div.notification.is-info.is-light - [:div.level - [:div.level-left - [:div.level-item - [:p - "This account was last updated on " - (yodlee-date->str (-> account :dataset first :lastUpdated)) - ", and last attempted " - (yodlee-date->str (-> account :dataset first :lastUpdateAttempt)) - "."]]] - [:div.level-right [:button.button.is-success {:on-click (dispatch-event [::kick (:id account)] )} "Sync yodlee with bank" ]]] - - ]) - - - [yodlee-accounts-table (:accounts account)] - (if (not= (-> account :dataset first :additionalStatus) - "AVAILABLE_DATA_RETRIEVED") - [:div - [:div.notification.is-info.is-warning - [:div.level - [:div.level-left - [:div.level-item - "This provider account's status is '" - (-> account :dataset first :additionalStatus) - "'. If this is in error, it might help to try reauthenticating by filling out the form below."]]]] - (let [{error :error account-data :data } @(re-frame/subscribe [::forms/form [::mfa-form (:id account)]]) - change-event [::forms/change [::mfa-form (:id account)]] - {:keys [form-inline field field-holder raw-field error-notification submit-button]} (forms/vertical-form {:can-submit [::can-submit] - :change-event change-event - :submit-event [::reauthenticate-mfa (:id account)] - :id [::mfa-form (:id account)]} )] - (form-inline {:title "Reauthenticate"} - [:<> - (error-notification) - (doall - (for [[row i] (map vector (-> account :loginForm last :row) (range)) - f (:field row) - :let [options (map :optionValue (:option f))]] - ^{:key (:id f)} - [:div - (field (:label row) - [:input.input {:type "text" :field [:login i (:id f)]}]) - (if (seq options) - [:ul - (for [o options] - ^{:key o} - [:li [:pre o]])])])) - (doall - (for [f (-> account :field)] - ^{:key (:id f)} - (field (:label f) - [:input.input {:type "text" :mfa [:form (:id f)] :value (-> f :field first :value)}]))) - (submit-button "Reauthenticate")]))])]]))]]))) - - -(defn admin-yodlee-content [] - [(with-meta - (fn [] - [:div - [:h1.title "Yodlee provider accounts"] - - [yodlee-provider-accounts-table] - [yodlee-link-button]]) - {:component-did-mount (fn [] - (re-frame/dispatch [::mounted]))})]) - -#_(defn admin-yodlee-page [] - [side-bar-layout {:side-bar [admin-side-bar {}] - :main [admin-yodlee-content]}]) diff --git a/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs b/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs index ab7f19d1..29d27fef 100644 --- a/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs +++ b/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs @@ -1,26 +1,15 @@ (ns auto-ap.views.pages.admin.yodlee2 - (:require [re-frame.core :as re-frame] - [auto-ap.forms :as forms] - [reagent.core :as reagent] - [clojure.string :as str] - [cljs-time.format :as f] - [cljs-time.core :as time] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.entities.clients :as entity] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field str->date date->str with-user]] - [auto-ap.views.components.modal :as modal] - [auto-ap.status :as status] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.views.pages.admin.yodlee2.table :as table] - [auto-ap.views.pages.admin.yodlee2.form :as form] - [auto-ap.views.components.grid :as grid] - [auto-ap.effects.forward :as forward])) + (:require + [auto-ap.effects.forward :as forward] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.admin.yodlee2.table :as table] + [auto-ap.views.utils :refer [dispatch-event]] + [re-frame.core :as re-frame] + [reagent.core :as reagent])) (re-frame/reg-sub ::authentication diff --git a/src/cljs/auto_ap/views/pages/admin/plaid.cljs b/src/cljs/auto_ap/views/pages/company/plaid.cljs similarity index 93% rename from src/cljs/auto_ap/views/pages/admin/plaid.cljs rename to src/cljs/auto_ap/views/pages/company/plaid.cljs index 33dce7ce..346fd1e3 100644 --- a/src/cljs/auto_ap/views/pages/admin/plaid.cljs +++ b/src/cljs/auto_ap/views/pages/company/plaid.cljs @@ -1,9 +1,9 @@ -(ns auto-ap.views.pages.admin.plaid +(ns auto-ap.views.pages.company.plaid (:require [auto-ap.effects.forward :as forward] [auto-ap.status :as status] [auto-ap.subs :as subs] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.pages.company.side-bar :refer [company-side-bar]] [auto-ap.views.components.grid :as grid] [auto-ap.views.components.layouts :refer [side-bar-layout]] [auto-ap.views.pages.admin.plaid.table :as table] @@ -85,7 +85,8 @@ (re-frame/reg-event-fx ::unmounted (fn [{:keys [db]} _] - {::forward/dispose {:id ::plaid-item-deleted}})) + {::forward/dispose {:id ::plaid-item-deleted} + ::track/dispose [{:id ::params}]})) (re-frame/reg-event-fx @@ -146,19 +147,17 @@ (defn plaid-link-token-button [] (let [status @(re-frame/subscribe [::status/single ::get-link-token]) - client-code (:code @(re-frame/subscribe [::subs/client]))] + client @(re-frame/subscribe [::subs/client])] [:button.button.is-primary {:disabled (status/disabled-for status) :class (status/class-for status) - :on-click (dispatch-event [::get-link-token client-code])} - "Authenticate with Plaid (" client-code ")"])) + :on-click (dispatch-event [::get-link-token (:code client)])} + "Authenticate with Plaid (" (:name client) ")"])) (defn link-flow [] [:div (let [link-token @(re-frame/subscribe [::link-token]) client-code (:code @(re-frame/subscribe [::subs/client]))] (cond - - (and link-token client-code) [:div "Authentication successful!" @@ -185,12 +184,12 @@ ])) -(defn admin-plaid-page [] +(defn plaid-page [] (reagent/create-class {:component-will-unmount #(re-frame/dispatch [::unmounted]) :component-did-mount #(re-frame/dispatch [::mounted]) :reagent-render (fn [] - [side-bar-layout {:side-bar [admin-side-bar {}] + [side-bar-layout {:side-bar [company-side-bar {}] :main [admin-plaid-item-content]}])})) diff --git a/src/cljs/auto_ap/views/pages/company/side_bar.cljs b/src/cljs/auto_ap/views/pages/company/side_bar.cljs index 2f628883..ff04cafe 100644 --- a/src/cljs/auto_ap/views/pages/company/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/company/side_bar.cljs @@ -14,4 +14,8 @@ [:a.item {:href (bidi/path-for routes/routes :reports) :class [(active-when ap = :reports)]} [:span {:class "icon icon-receipt" :style {:font-size "25px"}}] - [:span {:class "name"} "Reports"]]]]])) + [:span {:class "name"} "Reports"]]]] + [:li.menu-item + [:a {:href (bidi/path-for routes/routes :plaid), :class (str "item" (active-when ap = :plaid))} + [:span {:class "icon icon-saving-bank-1" :style {:font-size "25px"}}] + [:span {:class "name"} "Plaid Link"]]]])) diff --git a/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs b/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs index a397c3b5..31fa9fd6 100644 --- a/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs @@ -1,105 +1,109 @@ (ns auto-ap.views.pages.invoices.advanced-print-checks - (:require [auto-ap.forms :as forms] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.utils :refer [by]] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.pages.invoices.common :refer [invoice-read does-amount-exceed-outstanding?]] - [auto-ap.views.pages.invoices.form :as form] - [auto-ap.views.utils :refer [dispatch-event horizontal-field with-user]] - [re-frame.core :as re-frame])) - -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::form] - (fn [{ {:keys [invoices invoice-amounts]} :data}] - (cond (seq (filter - (fn [{:keys [id outstanding-balance]}] - (does-amount-exceed-outstanding? (get-in invoice-amounts [id :amount]) outstanding-balance )) - invoices)) - false - - :else - true))) - -(def advanced-print-checks-form (forms/vertical-form {:submit-event [::save] - :change-event [::forms/change ::form] - :can-submit [::can-submit] - :id ::form})) + (:require + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components :as com] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.money-field :refer [money-field]] + [auto-ap.views.pages.invoices.common + :refer [does-amount-exceed-outstanding? invoice-read]] + [auto-ap.views.utils :refer [coerce-float dispatch-event with-user]] + [malli.core :as m] + [malli.error :as me] + [re-frame.core :as re-frame])) +(def advanced-print-schema (m/schema + [:and + [:map + [:bank-account-id schema/not-empty-string] + [:invoice-amounts [:map-of + :string + [:map + [:amount schema/money]]]]] + [:fn (fn [{:keys [invoices invoice-amounts] :as z}] + (if (seq (filter + (fn [{:keys [id outstanding-balance]}] + (does-amount-exceed-outstanding? (get-in invoice-amounts [id :amount]) outstanding-balance )) + invoices)) + (throw (ex-info "Invalid" {:type ::too-much-invoice})) + true))]])) (defn form [] (let [real-bank-accounts @(re-frame/subscribe [::subs/real-bank-accounts]) - {:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline horizontal-field field raw-field error-notification submit-button]} advanced-print-checks-form] + {:keys [data]} @(re-frame/subscribe [::forms/form ::form])] - (form-inline {} - [:<> - [:div.field - [:label.label "Pay using"] - [:div.control - [:span.select - [raw-field - [:select {:type "select" - :field :bank-account-id} - (for [{:keys [id number name]} real-bank-accounts] - ^{:key id} [:option {:value id} name])]]]]] + [form-builder/builder {:submit-event [::try-save] + :id ::form + :schema advanced-print-schema} + [form-builder/field-v2 {:field :bank-account-id} + "Pay using" + [com/select-field {:options (for [{:keys [id name]} real-bank-accounts] + [id name]) + :allow-nil? true}]] - [:table.table.is-fullwidth - [:thead - [:tr - [:th "Vendor"] - [:th "Invoice ID"] - [:th {:style {"width" "10em"}} "Payment"]]] - [:tbody - (doall - (for [{:keys [vendor payment outstanding-balance invoice-number id] :as i} (:invoices data)] - ^{:key id} - [:tr - [:td (:name vendor)] - [:td invoice-number] - - [:td [:div.field.has-addons.is-extended - [:p.control [:a.button.is-static "$"]] - [:p.control - (raw-field - [:input.input.has-text-right {:type "number" - :field [:invoice-amounts id :amount] - :step "0.01"}])]]]]))]]]))) + [:table.table.is-fullwidth + [:thead + [:tr + [:th "Vendor"] + [:th "Invoice ID"] + [:th {:style {"width" "10em"}} "Payment"]]] + [:tbody + (doall + (for [{:keys [vendor invoice-number id]} (:invoices data)] + ^{:key id} + [:tr + [:td (:name vendor)] + [:td invoice-number] + + [:td + [form-builder/raw-field-v2 {:field [:invoice-amounts id :amount]} + [money-field]]]]))]]])) (re-frame/reg-event-fx ::show (fn [{:keys [db]} [_ invoices]] - {:dispatch [::modal/modal-requested {:title "Print Checks" - :body [form] - :confirm {:value "Print checks" + {:dispatch [::modal/modal-requested {:title "Print Checks" + :body [form] + :confirm {:value "Print checks" :status-from [::status/single ::form] - :class "is-primary" - :on-click (dispatch-event [::save]) - :can-submit [::can-submit] + :class "is-primary" + :on-click (dispatch-event [::try-save]) :close-event [::status/completed ::form]}}] - :db (-> db + :db (-> db (forms/start-form ::form - {:bank-account-id (:id (first @(re-frame/subscribe [::subs/real-bank-accounts]))) - :invoices invoices + {:invoices invoices :invoice-amounts (into {} (map (fn [i] [(:id i) - {:amount (:outstanding-balance i)}]) + {:amount (coerce-float (:outstanding-balance i))}]) invoices))}))})) +(re-frame/reg-event-fx + ::try-save + [(forms/in-form ::form)] + (fn [{:keys [db]}] + #_(println (m/explain advanced-print-schema (:data db))) + (if (not (m/validate advanced-print-schema (:data db))) + {:dispatch-n [[::status/error ::form [{:message "Please correct any errors and try again"}]] + [::forms/attempted-submit ::form]]} + {:dispatch [::save]}))) + (re-frame/reg-event-fx ::save [with-user (forms/in-form ::form) ] - (fn [{:keys [db user]} [_ bank-account-id]] + (fn [{:keys [db user]} _] + (let [type (or (->> @(re-frame/subscribe [::subs/client]) :bank-accounts - (filter #(= bank-account-id (:id %))) + (filter #(= (:bank-account-id (:data db)) (:id %))) first :type) :check) - {:keys [date invoices invoice-amounts check-number bank-account-id client]} (:data db)] + {:keys [invoices invoice-amounts bank-account-id]} (:data db)] {:graphql {:token user :owns-state {:single ::form} @@ -123,6 +127,6 @@ (re-frame/reg-event-fx ::checks-printed - (fn [{:keys [db]} [_ data]] + (fn [{:keys [_]} [_ _]] {:dispatch [::modal/modal-closed]})) diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 789e9094..99cd372a 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -1,6 +1,5 @@ (ns auto-ap.views.pages.invoices.form (:require - [auto-ap.entities.invoice :as invoice] [auto-ap.events :as events] [auto-ap.forms :as forms] [auto-ap.forms.builder :as form-builder] @@ -8,10 +7,11 @@ [auto-ap.subs :as subs] [auto-ap.time-utils :refer [next-dom]] [auto-ap.utils :refer [dollars=]] + [auto-ap.schema :as schema] [auto-ap.views.components.expense-accounts-field :as eaf :refer [recalculate-amounts - expense-accounts-field]] + expense-accounts-field-v2]] [auto-ap.views.components.layouts :as layouts] [auto-ap.views.components.level :refer [left-stack]] [auto-ap.views.components.modal :as modal] @@ -20,19 +20,33 @@ [auto-ap.views.components.switch-field :refer [switch-field]] [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] [auto-ap.views.pages.invoices.common :refer [invoice-read]] [auto-ap.views.utils - :refer [date-picker-optional + :refer [date-picker dispatch-event with-user]] [cljs-time.core :as c] - [clojure.spec.alpha :as s] [clojure.string :as str] [re-frame.core :as re-frame] [reagent.core :as r] + [malli.core :as m] [vimsical.re-frame.cofx.inject :as inject] - [vimsical.re-frame.fx.track :as track])) + [vimsical.re-frame.fx.track :as track] + [auto-ap.views.components :as com])) + + +(def schema (m/schema + [:map + [:client schema/reference] + [:vendor schema/reference] + [:date schema/date] + [:due {:optional true} [:maybe schema/date]] + [:scheduled-payment {:optional true} [:maybe schema/date]] + [:invoice-number schema/not-empty-string] + [:total schema/money] + [:expense-accounts eaf/schema]])) ;; SUBS (re-frame/reg-sub @@ -42,11 +56,11 @@ (let [min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) nil (- (:total (:original data)) (:outstanding-balance (:original data)))) - account-total (reduce + 0 (map (fn [ea] (js/parseFloat (:amount ea))) (:expense-accounts data)))] - (and (s/valid? ::invoice/invoice data) - (or (not min-total) (>= (:total data) min-total)) - (or (not (:id data)) - (dollars= (Math/abs (js/parseFloat (:total data))) (Math/abs account-total))))))) + account-total (reduce + 0 (map :amount (:expense-accounts data)))] + (and + (or (not min-total) (>= (:total data) min-total)) + (or (not (:id data)) + (dollars= (Math/abs (:total data)) (Math/abs account-total))))))) (re-frame/reg-sub ::create-query @@ -143,8 +157,8 @@ :vendor (:vendor edit-invoice) :client (:client edit-invoice) :expense-accounts (eaf/from-graphql (:expense-accounts which) - (:total which) - locations-for-client)}))}))) + (:total which) + locations-for-client)}))}))) @@ -321,7 +335,8 @@ [form-builder/builder {:can-submit [::can-submit] :change-event [::changed] :submit-event [::save-requested [::saving ]] - :id ::form} + :id ::form + :schema schema} [form-builder/section {:title [:div "New Invoice " (cond @@ -344,71 +359,61 @@ nil)]} (when-not active-client - [form-builder/field {:required? true} + [form-builder/field-v2 {:required? true + :field [:client]} "Client" [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) :entity->text :name - :type "typeahead-v3" + :style {:width "18em"} :auto-focus (if active-client false true) - :field [:client] :disabled exists?}]]) - [form-builder/field {:required? true} + [form-builder/field-v2 {:required? true + :field [:vendor]} "Vendor" [search-backed-typeahead {:disabled exists? :search-query (fn [i] [:search_vendor {:query i} [:name :id]]) - :type "typeahead-v3" - :auto-focus (if active-client true false) - :field [:vendor]}]] - [form-builder/vertical-control {:required? true} + :style {:width "18em"} + :auto-focus (if active-client true false)}]] + [form-builder/field-v2 {:required? true + :field :date} "Date" - [:label - [form-builder/raw-field - [date-picker-optional {:type "date2" - :field [:date] - :output :cljs-date}]]]] + [date-picker {:output :cljs-date}]] - [form-builder/field + [form-builder/field-v2 {:field [:due]} "Due (optional)" - [date-picker-optional {:type "date2" - :field [:due] - :output :cljs-date}]] + [date-picker {:output :cljs-date}]] [form-builder/vertical-control "Scheduled payment (optional)" [left-stack [:div.control - [form-builder/raw-field - [date-picker-optional {:type "date2" - :field [:scheduled-payment] - :output :cljs-date}]]] + [form-builder/raw-field-v2 {:field :scheduled-payment} + [date-picker {:output :cljs-date}]] + [form-builder/raw-error-v2 {:field :scheduled-payment}]] [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :schedule-when-due} - [switch-field {:id "schedule-when-due" - :field [:schedule-when-due] - :label "Same as due date" - :type "checkbox"}]]]]] - [form-builder/field {:required? true} + [com/switch-input {:id "schedule-when-due" + :label "Same as due date"}]]]]] + [form-builder/field-v2 {:required? true + :field :invoice-number} "Invoice #" - [:input.input {:type "text" - :field [:invoice-number]}]] - [form-builder/field {:required? true} + [:input.input {:style {:width "12em"}}]] + + [form-builder/field-v2 {:required? true + :field :total} "Total" - [money-field {:type "money" - :field [:total] - :disabled (if can-change-amount? "" "disabled") + [money-field {:disabled (if can-change-amount? "" "disabled") :style {:max-width "8em"} - :min min-total - :step "0.01"}]]] - [form-builder/raw-field - [expense-accounts-field {:type "expense-accounts" - :descriptor "expense account" - :locations (:locations (:client data)) - :max (:total data) - :client (or (:client data) active-client) - :field [:expense-accounts]}]] + :min min-total}]]] + [form-builder/field-v2 {:field :expense-accounts} + "Expense Accounts" + [expense-accounts-field-v2 {:descriptor "expense account" + :locations (:locations (:client data)) + :max (:total data) + :client (or (:client data) active-client)}]] [form-builder/error-notification] [:div {:style {:margin-bottom "1em"}}] [:div.columns @@ -433,6 +438,7 @@ (list ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :check]])} "Print checks from " name] ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :debit]])} "Debit from " name]))))]]]) + [:div.column [form-builder/submit-button {:class ["is-fullwidth"]} "Save"]]]])]) diff --git a/src/cljs/auto_ap/views/pages/invoices/handwritten_checks.cljs b/src/cljs/auto_ap/views/pages/invoices/handwritten_checks.cljs index a30c687b..7bec532f 100644 --- a/src/cljs/auto_ap/views/pages/invoices/handwritten_checks.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/handwritten_checks.cljs @@ -1,84 +1,74 @@ (ns auto-ap.views.pages.invoices.handwritten-checks - (:require [auto-ap.entities.invoice :as invoice] - [auto-ap.forms :as forms] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.utils :refer [by]] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.pages.invoices.common - :refer - [does-amount-exceed-outstanding? invoice-read]] - [auto-ap.views.pages.invoices.form :as form] - [auto-ap.views.utils - :refer - [date-picker dispatch-event horizontal-field with-user]] - [clojure.string :as str] - [re-frame.core :as re-frame])) + (:require + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.money-field :refer [money-field]] + [auto-ap.views.pages.invoices.common + :refer [does-amount-exceed-outstanding? invoice-read]] + [auto-ap.views.utils + :refer [date-picker dispatch-event with-user]] + [clojure.string :as str] + [re-frame.core :as re-frame] + [auto-ap.views.components :as com] + [malli.core :as m] + [auto-ap.schema :as schema])) -(def handwrite-checks-form (forms/vertical-form {:submit-event [::save] - :change-event [::forms/change ::form] - :can-submit [::can-submit] - :id ::form})) +(def handwritten-check-schema + (m/schema + [:map + [:bank-account-id schema/not-empty-string] + [:date schema/date] + [:check-number [:int {:min 1000 :max 99999}]] + ])) (defn form [] (let [real-bank-accounts @(re-frame/subscribe [::subs/real-bank-accounts]) - {:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline horizontal-field field raw-field error-notification submit-button]} handwrite-checks-form] - (form-inline {} - [:<> - [:div.field - [:label.label "Pay using"] - [:div.control - [:span.select - [raw-field - [:select {:type "select" - :field :bank-account-id} - (for [{:keys [id number name]} real-bank-accounts] - ^{:key id} [:option {:value id} name])]]]]] - (field "Date" - [date-picker {:class-name "input" - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder "mm/dd/yyyy" - :next-month-button-label "" - :next-month-label "" - :type "date" - :field [:date] - :spec ::invoice/date}]) - (field "Check number" - [:input.input {:type "number" - :field [:check-number]}]) - [:table.table.is-fullwidth - [:thead - [:tr - [:th "Invoice ID"] - [:th {:style {"width" "14em"}} "Payment"]]] - [:tbody - (doall - (for [{:keys [payment outstanding-balance invoice-number id] :as i} (:invoices data)] - ^{:key id} - [:tr - [:td invoice-number] - [:td [:div.field.has-addons.is-extended - [:p.control [:a.button.is-static "$"]] - [:p.control + {:keys [data]} @(re-frame/subscribe [::forms/form ::form])] + [form-builder/builder {:submit-event [::save] + :can-submit [::can-submit] + :id ::form + :schema handwritten-check-schema} + [form-builder/field-v2 {:field :bank-account-id} + "Pay using" + [com/select-field {:options (for [{:keys [id name]} real-bank-accounts] + [id name]) + :allow-nil? true}]] - (raw-field - [:input.input.has-text-right {:type "number" - :field [:invoice-amounts id :amount] - #_#_:max outstanding-balance - :step "0.01"}])]]]]))]]]))) + [form-builder/field-v2 {:field :date} + "Date" + [date-picker {:type "date" + :output :cljs-date}]] + + [form-builder/field-v2 {:field :check-number} + "Check number" + [com/number-input {:style {:width "8em"}}]] + [:table.table.is-fullwidth + [:thead + [:tr + [:th "Invoice ID"] + [:th {:style {"width" "14em"}} "Payment"]]] + [:tbody + (doall + (for [{:keys [invoice-number id]} (:invoices data)] + ^{:key id} + [:tr + [:td invoice-number] + [:td + [form-builder/raw-field-v2 {:field [:invoice-amounts id :amount]} + [money-field {:style {:max-width "9em"}}]]]]))]] + [form-builder/hidden-submit-button]])) (re-frame/reg-sub ::can-submit :<- [::forms/form ::form] (fn [{ {:keys [check-number date invoices invoice-amounts]} :data}] (boolean (cond (seq (filter - (fn [{:keys [id outstanding-balance]}] - (does-amount-exceed-outstanding? (get-in invoice-amounts [id :amount]) outstanding-balance )) - invoices)) + (fn [{:keys [id outstanding-balance]}] + (does-amount-exceed-outstanding? (get-in invoice-amounts [id :amount]) outstanding-balance )) + invoices)) false :else @@ -98,7 +88,7 @@ :close-event [::status/completed ::form]}}] :db (-> db (forms/start-form ::form - {:bank-account-id (:id (first @(re-frame/subscribe [::subs/real-bank-accounts]))) + {:bank-account-id nil :invoices invoices :invoice-amounts (into {} (map (fn [i] [(:id i) @@ -132,6 +122,6 @@ (re-frame/reg-event-fx ::succeeded [(forms/triggers-stop ::form)] - (fn [{:keys [db]} [_ invoices]] + (fn [_ _] {:dispatch [::modal/modal-closed]})) diff --git a/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs b/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs index 529839c5..c167d3c5 100644 --- a/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs @@ -13,11 +13,9 @@ [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] [auto-ap.views.pages.ledger.table :as ledger-table] [auto-ap.views.utils - :refer [date->str - date-picker-friendly + :refer [date-picker dispatch-event local-now - standard with-user]] [cljs-time.core :as t] [clojure.set :as set] @@ -25,7 +23,9 @@ [reagent.core :as reagent] [vimsical.re-frame.fx.track :as track] [vimsical.re-frame.cofx.inject :as inject] - [auto-ap.views.pages.ledger.report-table :as rtable])) + [auto-ap.views.pages.ledger.report-table :as rtable] + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components :as com])) (defn data-params->query-params [params] (when params @@ -38,11 +38,6 @@ :to-numeric-code (:to-numeric-code params) :date-range (:date-range params)})) -(re-frame/reg-sub - ::can-submit - (fn [_] - true)) - (re-frame/reg-sub ::ledger-list-active? @@ -78,9 +73,7 @@ :graphql {:token user :query-obj {:venia/queries [[:balance-sheet (-> (:data db) - (assoc :client-id (:id client)) - (update :date (fnil #(date->str % standard) nil)) - (update :comparison-date (fnil #(date->str % standard) nil))) + (assoc :client-id (:id client))) [[:balance-sheet-accounts [:name :amount :account-type :id :numeric-code]] [:comparable-balance-sheet-accounts [:name :amount :account-type :id :numeric-code]]]]]} @@ -122,9 +115,7 @@ NOTE: Please review the transactions we may have question for you here: https:// :graphql {:token user :query-obj {:venia/queries [[:balance-sheet-pdf (-> (:data db) - (assoc :client-id (:id client)) - (update :date (fnil #(date->str % standard) nil)) - (update :comparison-date (fnil #(date->str % standard) nil))) + (assoc :client-id (:id client))) [:url :name]]]} :owns-state {:single ::page} @@ -139,7 +130,7 @@ NOTE: Please review the transactions we may have question for you here: https:// :from-numeric-code from-numeric-code :to-numeric-code to-numeric-code :date-range {:start "2000-01-01" - :end (date->str date-range standard)}}]})) + :end date-range}}]})) (re-frame/reg-event-fx ::ledger-params-change @@ -189,53 +180,40 @@ NOTE: Please review the transactions we may have question for you here: https:// :event-fn (fn [params] [::ledger-params-change params])}})) -(def balance-sheet-form (forms/vertical-form {:can-submit [::can-submit] - :change-event [::change] - :submit-event [::report-requested] - :id ::form})) - (defn report-form [] - (let [{:keys [form-inline raw-field]} balance-sheet-form - {:keys [data]} @(re-frame/subscribe [::forms/form ::form])] - (form-inline {} - [:div - [:div.report-controls - [:div.level - [:div.level-left - [:div.level-item - [:div.control - [:p.help "Date"] - (raw-field - [date-picker-friendly {:cljs-date? true - :type "date" - :field [:date]}])]] - [:div.level-item - [:div.control - [:div.mt-3] - [switch-field {:id "include-comparison" - :checked (:include-comparison data) - :on-change (fn [e] - (re-frame/dispatch [::change [:include-comparison] (.-checked (.-target e))])) - :label "Include comparison" - :type "checkbox"}]]] - [:div.level-item + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])] + [form-builder/builder {:change-event [::change] + :submit-event [::report-requested] + :id ::form} + [:div + [:div.report-controls + [:div.level + [:div.level-left + [:div.level-item + [:div.control + [form-builder/field-v2 {:field :date} + "Date" + [date-picker {:output :cljs-date}]]]] + [:div.level-item + [form-builder/field-v2 {:field :include-comparison} + [:div.mt-5] + [com/switch-input {:id "include-comparison" + :label "Include compariison"}]]] + [:div.level-item - (when (boolean (:include-comparison data)) - [:div.control - [:p.help "Comparison Date"] - (raw-field - [date-picker-friendly {:cljs-date? true - :type "date" - :field [:comparison-date]}])])]] - [:div.level-right - [:div.buttons + (when (boolean (:include-comparison data)) + [form-builder/field-v2 {:field :comparison-date} + "Comparison Date" + [date-picker {:output :cljs-date}]])]] + [:div.level-right + [:div.buttons - (when @(re-frame/subscribe [::subs/is-admin?]) - [:button.button.is-secondary {:on-click (dispatch-event [::export-pdf])} "Export"]) - [:button.button.is-primary "Run"]]]]]]))) + (when @(re-frame/subscribe [::subs/is-admin?]) + [:button.button.is-secondary {:on-click (dispatch-event [::export-pdf])} "Export"]) + [:button.button.is-primary "Run"]]]]]]])) (defn balance-sheet-report [{:keys [args report-data]}] - (let [pnl-data (concat (->> (:balance-sheet-accounts report-data) + (let [pnl-data (concat (->> (:balance-sheet-accounts report-data) (map (fn [b] (assoc b :period (:date args) diff --git a/src/cljs/auto_ap/views/pages/ledger/external_import.cljs b/src/cljs/auto_ap/views/pages/ledger/external_import.cljs index 1bff9894..8a5d150f 100644 --- a/src/cljs/auto_ap/views/pages/ledger/external_import.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/external_import.cljs @@ -1,41 +1,29 @@ (ns auto-ap.views.pages.ledger.external-import - (:require [auto-ap.subs :as subs] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [goog.string :as gstring] - [auto-ap.forms :as forms] - [auto-ap.utils :refer [by]] - [auto-ap.events :as events] - [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] - [auto-ap.views.utils :refer [date->str date-picker bind-field local-now standard ->$ str->date dispatch-event]] - [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]] - [cljs-time.core :as t] - [re-frame.core :as re-frame] - [reagent.core :as r] - [clojure.string :as str] - [auto-ap.status :as status])) + (:require + [auto-ap.events :as events] + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.status :as status] + [auto-ap.views.components :as com] + [auto-ap.views.components.dropdown + :refer [drop-down drop-down-contents]] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] + [auto-ap.views.utils :refer [dispatch-event]] + [clojure.string :as str] + [re-frame.core :as re-frame] + [reagent.core :as r])) - - -(re-frame/reg-sub - ::loading - (fn [db] - (-> db ::loading))) - -(re-frame/reg-sub - ::can-submit - (fn [db] - true)) - -(defn line->id [{:keys [source id client-code date vendor-name] :as line}] +(defn line->id [{:keys [source id client-code]}] (str client-code "-" source "-" id)) (re-frame/reg-sub ::request :<- [::forms/form ::form] - (fn [{{lines :line-items :as d} :data :as g}] + (fn [{{lines :line-items} :data}] (into [] - (for [[external-id lines] (group-by line->id lines) - :let [{:keys [source id client-code date vendor-name note cleared-against] :as line} (first lines)]] + (for [[_ lines] (group-by line->id lines) + :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]] {:source source :external-id (line->id line) :client-code client-code @@ -142,6 +130,8 @@ [:form.form (if value [:div + [:a.button {:on-click #(on-change nil)} + "reset"] [:table.table {:style {:width "100%"}} [:thead [:tr @@ -193,68 +183,72 @@ (def balance-sheet-content (with-meta (fn [] - (let [current-client @(re-frame/subscribe [::subs/client]) - user @(re-frame/subscribe [::subs/user]) - status @(re-frame/subscribe [::status/single ::import]) - {:keys [data result active? error id]} @(re-frame/subscribe [::forms/form ::form]) ] - [:div - [:div.level - [:div.level-left - [:h1.title "Eternal Import"]] - - [:div.level-right - [:button.button.is-primary.is-pulled-right.is-large {:disabled (or (not data) - (= :loading (:state status ))) - :on-click (dispatch-event [::importing])} "Import"]]] - [status/status-notification {:statuses [[::status/single ::import]]} ] - (when result - [:div.notification - "Imported with " - (count (:errors result)) " errors, " - (count (:ignored result)) " ignored, " - (count (:success result)) " successful."]) - (if (= :loading (:state status )) - [status/big-loader status] - [:div - [:div.is-clearfix - [:div.is-pulled-right - [:label.checkbox - [bind-field - [:input {:type "checkbox" - :event [::forms/change ::form] - :subscription data - :field [:only-show-errors?]}]] - "Only show errors"]]] - [:div - [bind-field - [textarea->table {:type "textarea->table" - :field [:line-items] - :headings [["Id" :id] - ["Client" :client-code] - ["Source" :source] - ["Vendor" :vendor-name] - ["Date" :date] - ["Account" :account-identifier] - ["Location" :location] - ["Debit" :debit] - ["Credit" :credit] - ["Note" :note] - ["Cleared against" :cleared-against]] - :read-only-headings - [["status" :status]] + (let [status @(re-frame/subscribe [::status/single ::import]) + {:keys [data result]} @(re-frame/subscribe [::forms/form ::form]) ] + [form-builder/builder {:id ::form + :submit-event [::importing]} + [:div + [:div.level + [:div.level-left + [:h1.title "Eternal Import"]] + + [:div.level-right + [form-builder/submit-button "Import"]]] + [status/status-notification {:statuses [[::status/single ::import]]} ] + (when result + [:div.notification + "Imported with " + (count (:errors result)) " errors, " + (count (:ignored result)) " ignored, " + (count (:success result)) " successful."]) + (if (= :loading (:state status )) + [status/big-loader status] + [:div + [:div.is-clearfix + [:div.is-pulled-right + [form-builder/raw-field-v2 {:field :only-show-errors?} + [com/checkbox {:label "Only show errors"}]]]] + [:div + [form-builder/raw-field-v2 {:field :line-items} + [textarea->table {:headings [["Id" :id] + ["Client" :client-code] + ["Source" :source] + ["Vendor" :vendor-name] + ["Date" :date] + ["Account" :account-identifier] + ["Location" :location] + ["Debit" :debit] + ["Credit" :credit] + ["Note" :note] + ["Cleared against" :cleared-against]] + :read-only-headings + [["status" :status]] - :row-filter - (fn [{:keys [status-category]}] - (if (:only-show-errors? data) - (= :error status-category) - true)) - - :event [::forms/change ::form] - :subscription data} - ]]]])])) + :row-filter + (fn [{:keys [status-category]}] + (if (:only-show-errors? data) + (= :error status-category) + true))}]]]])]])) {})) -(defn external-import-page [] +(re-frame/reg-event-fx + ::mounted + (fn [_ _] + {:dispatch [::forms/start-form ::form]})) + +(re-frame/reg-event-fx + ::unmounted + (fn [_ _] + {:dispatch [::forms/form-closing ::form]})) + +(defn external-import-page-internal [] [side-bar-layout {:side-bar [ledger-side-bar] :main [balance-sheet-content]}]) + +(defn external-import-page [] + (r/create-class + {:display-name "external-import-page" + :component-will-unmount #(re-frame/dispatch-sync [::unmounted]) + :component-did-mount #(re-frame/dispatch [::mounted]) + :reagent-render external-import-page-internal})) diff --git a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs index ec0fc73e..eecd0704 100644 --- a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs @@ -10,16 +10,14 @@ :refer [appearing-side-bar side-bar-layout]] [auto-ap.views.components.modal :as modal] [auto-ap.views.components.switch-field :refer [switch-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.pages.data-page :as data-page] [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] [auto-ap.views.pages.ledger.table :as ledger-table] [auto-ap.views.utils :refer [date->str - date-picker-friendly + date-picker dispatch-event local-today - multi-field query-params standard str->date @@ -31,7 +29,9 @@ [react-dom :as react-dom] [reagent.core :as reagent] [vimsical.re-frame.cofx.inject :as inject] - [vimsical.re-frame.fx.track :as track])) + [vimsical.re-frame.fx.track :as track] + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components :as com])) @@ -72,8 +72,8 @@ (cond-> {:graphql {:token user :owns-state {:single ::page} :query-obj {:venia/queries [[:profit-and-loss - {:client-ids (map :id (:clients (:data db))) - :periods (mapv encode-period (:periods (:data db))) + {:client-ids (map (comp :id :client) (:clients (:data db))) + :periods (mapv #(select-keys % #{:start :end} ) (:periods (:data db))) :include-deltas (:include-deltas (:data db)) :column-per-location (:column-per-location (:data db))} [[:periods [[:accounts [:name :amount :client_id :account-type :id :count :numeric-code :location]]]]]]]} @@ -81,7 +81,7 @@ :set-uri-params {:periods (mapv encode-period (:periods (:data db))) - :clients (mapv #(select-keys % [:name :id]) (:clients (:data db))) } + :clients (mapv #(select-keys (:client %) [:name :id]) (:clients (:data db))) } :db (-> db (dissoc :report) (update-in [:data :clients] #(into [] (filter seq %))))}))) @@ -126,14 +126,14 @@ NOTE: Please review the transactions we may have question for you here: https:// (cond-> {:graphql {:token user :owns-state {:single ::page} :query-obj {:venia/queries [[:profit-and-loss-pdf - {:client-ids (map :id (:clients (:data db))) + {:client-ids (map (:comp :id :client) (:clients (:data db))) :include-deltas (:include-deltas (:data db)) :column-per-location (:column-per-location (:data db)) - :periods (mapv encode-period (:periods (:data db)))} + :periods (mapv #(select-keys % #{:start :end}) (:periods (:data db)))} [:url :name]]]} :on-success [::received-pdf]} :set-uri-params {:periods (mapv encode-period (:periods (:data db))) - :clients (mapv #(select-keys % [:name :id]) (:clients (:data db))) } + :clients (mapv #(select-keys (:client %) [:name :id]) (:clients (:data db))) } :db (dissoc db :report)}))) @@ -223,10 +223,6 @@ NOTE: Please review the transactions we may have question for you here: https:// (fn [_] true)) -(def pnl-form (forms/vertical-form {:can-submit [::can-submit] - :change-event [::change] - :submit-event [::report-requested] - :id ::form})) (defn report-control-detail [{:keys [active box which]} children] (when (and @box @@ -243,191 +239,177 @@ NOTE: Please review the transactions we may have question for you here: https:// [:div.control [:a.button {:class (when (= selected-preset title) "is-active") - :on-click (dispatch-event - [::change - [:periods] - periods - [:selected-preset] title])} + :on-click (fn [] + (re-frame/dispatch-sync [::change + [:periods] + periods + [:selected-preset] title]) + (re-frame/dispatch-sync [::change + [:show-advanced?] + false]))} title]])) -(defn report-controls [_] - (let [!box (reagent/atom nil) +(defn report-controls [] + (let [!box (atom nil) active (reagent/atom nil)] - (fn [pnl-form] - (let [{:keys [raw-field]} pnl-form - {:keys [data]} @(re-frame/subscribe [::forms/form ::form]) + (fn [] + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) {:keys [periods selected-preset include-deltas column-per-location]} data] - [:div.report-controls - [:div.level.mb-2 - [:div.level-left - [:div.level-item - [buttons/dropdown {:on-click (fn [] (reset! active :clients))} - [:span (str "Companies" - (when-let [clients (:clients data)] - (str " (" (str/join ", " (map :name clients)) ")")))]] - [report-control-detail {:active active :box !box :which :clients} - [:div {:style {:width "20em"}} - [:h4.subtitle "Companies"] - [raw-field - [multi-field {:type "multi-field" - :field [:clients] - :template [[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) - :entity->text :name - :type "typeahead-v3"}]]}]] - ]]] - [:div.level-item - [buttons/dropdown {:on-click (fn [] (reset! active :range))} - [:span (str "Range" - (when selected-preset - (str " (" selected-preset ")")))]] - [report-control-detail {:active active :box !box :which :range} - [:div - [:h4.subtitle "Range"] - [:div.field.is-grouped - [:div.control - [:div.field.has-addons - [:div.control - (raw-field - [date-picker-friendly {:placeholder "End date" - :type "date" - :cljs-date? true - :field [:thirteen-periods-end]}])] - [period-preset-button {:title "13 periods" - :periods (let [today (or (some-> (:thirteen-periods-end data)) - (local-today))] - (into - [{:start (t/plus (t/minus today (t/weeks (* 13 4))) - (t/days 1)) - :end today - :title "Total"}] - (for [i (range 13)] - {:start (t/plus (t/minus today (t/weeks (* (inc i) 4))) + [form-builder/builder {:can-submit [::can-submit] + :change-event [::change] + :submit-event [::report-requested] + :id ::form} + [:div.report-controls + [:div.level.mb-2 + [:div.level-left + [:div.level-item + [buttons/dropdown {:on-click (fn [] (reset! active :clients))} + [:span (str "Companies" + (when-let [clients (:clients data)] + (str " (" (str/join ", " (map (comp :name :client) clients)) ")")))]] + [report-control-detail {:active active :box !box :which :clients} + [:div {:style {:width "20em"}} + [:h4.subtitle "Companies"] + [form-builder/raw-field-v2 {:field :clients} + [com/multi-field-v2 {:new-text "Add another company" + :template [[form-builder/raw-field-v2 {:field :client} + [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients]) + :style {:width "18em"} + :entity->text :name}]]] + :key-fn :id}]]]]] + [:div.level-item + [buttons/dropdown {:on-click (fn [] (reset! active :range))} + [:span (str "Range" + (when selected-preset + (str " (" selected-preset ")")))]] + [report-control-detail {:active active :box !box :which :range} + [:div + [:h4.subtitle "Range"] + [:div.field.is-grouped + [:div.control + [:div.field.has-addons + [:div.control + [form-builder/raw-field-v2 {:field :thirteen-periods-end} + [date-picker {:placeholder "End date" + :output :cljs-date}]]] + [period-preset-button {:title "13 periods" + :periods (let [today (or (some-> (:thirteen-periods-end data)) + (local-today))] + (into + [{:start (t/plus (t/minus today (t/weeks (* 13 4))) (t/days 1)) - :end (t/minus today (t/weeks (* i 4)))})))}]]] + :end today + :title "Total"}] + (for [i (range 13)] + {:start (t/plus (t/minus today (t/weeks (* (inc i) 4))) + (t/days 1)) + :end (t/minus today (t/weeks (* i 4)))})))}]]] - [:div.control - [:div.field.has-addons - [:div.control - (raw-field - [date-picker-friendly {:placeholder "End date" - :cljs-date? true - :type "date" - :field [:twelve-periods-end]}])] - [period-preset-button {:title "12 months" - :periods (let [end-date (or (some-> (:twelve-periods-end data)) - (local-today)) - this-month (t/local-date (t/year end-date) - (t/month end-date) - 1)] - (into - [{:start (t/minus this-month (t/months 11)) - :end (t/minus (t/plus this-month (t/months 1)) - (t/days 1)) - :title "Total"}] - (for [i (range 12)] - {:start (t/minus this-month (t/months (- 11 i))) - :end (t/minus (t/minus this-month (t/months (- 10 i))) - (t/days 1))})))}]]] + [:div.control + [:div.field.has-addons + [:div.control + [form-builder/raw-field-v2 {:field :twelve-periods-end} + [date-picker {:placeholder "End date" + :output :cljs-date}]]] + [period-preset-button {:title "12 months" + :periods (let [end-date (or (some-> (:twelve-periods-end data)) + (local-today)) + this-month (t/local-date (t/year end-date) + (t/month end-date) + 1)] + (into + [{:start (t/minus this-month (t/months 11)) + :end (t/minus (t/plus this-month (t/months 1)) + (t/days 1)) + :title "Total"}] + (for [i (range 12)] + {:start (t/minus this-month (t/months (- 11 i))) + :end (t/minus (t/minus this-month (t/months (- 10 i))) + (t/days 1))})))}]]] - [period-preset-button {:periods (let [last-sunday (loop [current (local-today)] - (if (= 7 (t/day-of-week current)) - current - (recur (t/minus current (t/period :days 1)))))] - (and-last-year {:start (t/minus last-sunday (t/period :days 6)) - :end last-sunday})) - :title "Last week"}] + [period-preset-button {:periods (let [last-sunday (loop [current (local-today)] + (if (= 7 (t/day-of-week current)) + current + (recur (t/minus current (t/period :days 1)))))] + (and-last-year {:start (t/minus last-sunday (t/period :days 6)) + :end last-sunday})) + :title "Last week"}] - [period-preset-button {:periods (and-last-year {:start (loop [current (local-today)] - (if (= 1 (t/day-of-week current)) - current - (recur (t/minus current (t/period :days 1))))) - :end (local-today)}) - :title "Week to date"}] + [period-preset-button {:periods (and-last-year {:start (loop [current (local-today)] + (if (= 1 (t/day-of-week current)) + current + (recur (t/minus current (t/period :days 1))))) + :end (local-today)}) + :title "Week to date"}] - [period-preset-button {:periods (and-last-year {:start (t/minus (t/local-date (t/year (local-today)) - (t/month (local-today)) - 1) - (t/period :months 1)) - :end (t/minus (t/local-date (t/year (local-today)) - (t/month (local-today)) - 1) - (t/period :days 1))}) - :title "Last month"}] + [period-preset-button {:periods (and-last-year {:start (t/minus (t/local-date (t/year (local-today)) + (t/month (local-today)) + 1) + (t/period :months 1)) + :end (t/minus (t/local-date (t/year (local-today)) + (t/month (local-today)) + 1) + (t/period :days 1))}) + :title "Last month"}] - [period-preset-button {:periods (and-last-year {:start (t/local-date (t/year (local-today)) - (t/month (local-today)) - 1) - :end (local-today)}) - :title "Month to date"}] + [period-preset-button {:periods (and-last-year {:start (t/local-date (t/year (local-today)) + (t/month (local-today)) + 1) + :end (local-today)}) + :title "Month to date"}] - [period-preset-button {:periods (and-last-year {:start (t/local-date (t/year (local-today)) 1 1) - :end - (local-today)}) - :title "Year to date"}] + [period-preset-button {:periods (and-last-year {:start (t/local-date (t/year (local-today)) 1 1) + :end + (local-today)}) + :title "Year to date"}] - [period-preset-button {:periods [{:start (t/local-date (dec (t/year (local-today))) 1 1) - :end (t/local-date (dec (t/year (local-today))) 12 31)}] - :title "Last calendar year"}] + [period-preset-button {:periods [{:start (t/local-date (dec (t/year (local-today))) 1 1) + :end (t/local-date (dec (t/year (local-today))) 12 31)}] + :title "Last calendar year"}] - [period-preset-button {:periods (and-last-year {:start (t/plus (t/minus (local-today) (t/period :years 1)) - (t/period :days 1)) - :end (local-today)}) - :title "Full year"}]] - [:div - [:div.field - [:label.checkbox - (raw-field - [:input {:type "checkbox" - :field [:show-advanced?]}]) - " Show Advanced"]]] - (when (:show-advanced? data) - (doall - (for [[_ i] (map vector periods (range))] - ^{:key i} - [:div.field.is-grouped - [:div.control - [:p.help "From"] - (raw-field - [date-picker-friendly {:type "date" - :cljs-date? true - :field [:periods i :start]}])] + [period-preset-button {:periods (and-last-year {:start (t/plus (t/minus (local-today) (t/period :years 1)) + (t/period :days 1)) + :end (local-today)}) + :title "Full year"}]] + [:div + [form-builder/raw-field-v2 {:field :show-advanced?} + [com/checkbox {:label "Show Advanced"}]]] + (when (:show-advanced? data) + [form-builder/raw-field-v2 {:field :periods} + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :start} + [date-picker {:output :cljs-date}]] + [form-builder/raw-field-v2 {:field :end} + [date-picker {:output :cljs-date}]]]}]])]]] - [:div.control - [:p.help "To"] - (raw-field - [date-picker-friendly {:type "date" - :cljs-date? true - :field [:periods i :end]}])]])))]]] + [:div.level-item + [:div + [switch-field {:id "include-deltas" + :checked (boolean include-deltas) + :on-change (fn [e] + (re-frame/dispatch [::change + [:include-deltas] (.-checked (.-target e))])) + :label "Include deltas" + :type "checkbox"}]]] + [:div.level-item + [:div + [switch-field {:id "column-per-location" + :checked (boolean column-per-location) + :on-change (fn [e] + (re-frame/dispatch [::change + [:column-per-location] (.-checked (.-target e))])) + :label "Column per location" + :type "checkbox"}]]]] + [:div.level-right + [:div.buttons - [:div.level-item - [:div - [switch-field {:id "include-deltas" - :checked (boolean include-deltas) - :on-change (fn [e] - (re-frame/dispatch [::change - [:include-deltas] (.-checked (.-target e))])) - :label "Include deltas" - :type "checkbox"}]]] - [:div.level-item - [:div - [switch-field {:id "column-per-location" - :checked (boolean column-per-location) - :on-change (fn [e] - (re-frame/dispatch [::change - [:column-per-location] (.-checked (.-target e))])) - :label "Column per location" - :type "checkbox"}]]]] - [:div.level-right - [:div.buttons + (when @(re-frame/subscribe [::subs/is-admin?]) + [:button.button.is-secondary {:on-click (dispatch-event [::export-pdf])} "Export"]) + [:button.button.is-primary "Run"]] - (when @(re-frame/subscribe [::subs/is-admin?]) - [:button.button.is-secondary {:on-click (dispatch-event [::export-pdf])} "Export"]) - [:button.button.is-primary "Run"]] - - ]] - [:div.report-control-detail {:ref (fn [el] - (when-not @!box - (reset! !box el)))}]])))) + ]] + [:div.report-control-detail {:ref (fn [el] + (when (not= @!box el) + (reset! !box el)))}]]])))) @@ -450,7 +432,7 @@ NOTE: Please review the transactions we may have question for you here: https:// report (l-reports/summarize-pnl pnl-data) table (rtable/concat-tables (concat (:summaries report) (:details report)))] [:div - [:h1.title "Profit and Loss - " (str/join ", " (map :name (:clients args)))] + [:h1.title "Profit and Loss - " (str/join ", " (map (comp :name :client) (:clients args)))] (when (:warning report) [:div.notification.is-warning.is-light (:warning report)]) @@ -466,13 +448,11 @@ NOTE: Please review the transactions we may have question for you here: https:// (defn profit-and-loss-content [] (let [status @(re-frame/subscribe [::status/single ::page]) - {:keys [data report]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline]} pnl-form] + {:keys [data report]} @(re-frame/subscribe [::forms/form ::form])] [:div - (form-inline {} - [:div - [status/status-notification {:statuses [[::status/single ::page]]}] - [report-controls pnl-form]]) + [:div + [status/status-notification {:statuses [[::status/single ::page]]}] + [report-controls]] [status/big-loader status] (when (and (not= :loading (:state status)) report) @@ -493,9 +473,11 @@ NOTE: Please review the transactions we may have question for you here: https:// (mapv (fn [period] {:start (str->date (:start period) standard) :end (str->date (:end period) standard)}))) - :clients (or (:clients qp) - [(some-> @(re-frame/subscribe [::subs/client]) (select-keys [:name :id]))]) - :include-deltas false}) + :clients (mapv (fn [c] {:client c :id (random-uuid)}) + (or (:clients qp) + [(some-> @(re-frame/subscribe [::subs/client]) (select-keys [:name :id]) )])) + :include-deltas false + :show-advanced? false}) ::track/register {:id ::ledger-params :subscription [::data-page/params ::ledger] :event-fn (fn [params] [::ledger-params-change params])}}))) diff --git a/src/cljs/auto_ap/views/pages/ledger/report_table.cljs b/src/cljs/auto_ap/views/pages/ledger/report_table.cljs index f33bbacd..c5c3e629 100644 --- a/src/cljs/auto_ap/views/pages/ledger/report_table.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/report_table.cljs @@ -30,7 +30,7 @@ [(keyword (str "border-" (name b))) "1px solid black"]) ) (into s)))) - (:colspan c) (assoc :colspan (:colspan c)) + (:colspan c) (assoc :col-span (:colspan c)) (:align c) (assoc :align (:align c)) (= :dollar (:format c)) (assoc :align :right) (= :percent (:format c)) (assoc :align :right) diff --git a/src/cljs/auto_ap/views/pages/payments.cljs b/src/cljs/auto_ap/views/pages/payments.cljs index d389fccd..52236e47 100644 --- a/src/cljs/auto_ap/views/pages/payments.cljs +++ b/src/cljs/auto_ap/views/pages/payments.cljs @@ -17,19 +17,22 @@ [vimsical.re-frame.fx.track :as track])) (defn data-params->query-params [params] - {:start (:start params 0) - :per-page (:per-page params) - :sort (:sort params) - :client-id (:id @(re-frame/subscribe [::subs/client])) - :vendor-id (:id (:vendor params)) - :payment-type (:payment-type params) - :status (:status params) - :exact-match-id (some-> (:exact-match-id params) str) - :date-range (:date-range params) - :amount-gte (:amount-gte (:amount-range params)) - :amount-lte (:amount-lte (:amount-range params)) - :check-number-like (str (:check-number-like params)) - :invoice-number (:invoice-number params)}) + (if (:exact-match-id params) + {:client-id (:id @(re-frame/subscribe [::subs/client])) + :exact-match-id (some-> (:exact-match-id params) str)} + {:start (:start params 0) + :per-page (:per-page params) + :sort (:sort params) + :client-id (:id @(re-frame/subscribe [::subs/client])) + :vendor-id (:id (:vendor params)) + :payment-type (:payment-type params) + :status (:status params) + + :date-range (:date-range params) + :amount-gte (:amount-gte (:amount-range params)) + :amount-lte (:amount-lte (:amount-range params)) + :check-number-like (str (:check-number-like params)) + :invoice-number (:invoice-number params)})) (re-frame/reg-event-fx ::params-change diff --git a/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs b/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs index 9d04cd7a..53ced149 100644 --- a/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs +++ b/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs @@ -15,18 +15,21 @@ (re-frame/reg-event-fx ::params-change [with-user] - (fn [{:keys [user db ]}[_ params]] + (fn [{:keys [user]}[_ params]] {:graphql {:token user :owns-state {:single [::data-page/page ::page]} :query-obj {:venia/queries [[:expected_deposit_page - {:start (:start params 0) - :sort (:sort params) - :per-page (:per-page params) - :exact-match-id (some-> (:exact-match-id params) str) - :total-gte (:amount-gte (:total-range params)) - :total-lte (:amount-lte (:total-range params)) - :date-range (:date-range params) - :client-id (:id @(re-frame/subscribe [::subs/client]))} + (if (:exact-match-id params) + {:exact-match-id (some-> (:exact-match-id params) str) + :client-id (:id @(re-frame/subscribe [::subs/client]))} + {:start (:start params 0) + :sort (:sort params) + :per-page (:per-page params) + :exact-match-id (some-> (:exact-match-id params) str) + :total-gte (:amount-gte (:total-range params)) + :total-lte (:amount-lte (:total-range params)) + :date-range (:date-range params) + :client-id (:id @(re-frame/subscribe [::subs/client]))}) [[:expected-deposits [:id :total :fee :location :date :status [:totals [:date :count :amount]] [:transaction [:id :date]] @@ -42,13 +45,13 @@ (re-frame/reg-event-fx ::unmounted - (fn [{:keys [db]} _] + (fn [_ _] {:dispatch [::data-page/dispose ::page] ::track/dispose {:id ::params}})) (re-frame/reg-event-fx ::mounted - (fn [{:keys [db]} _] + (fn [_ _] {::track/register {:id ::params :subscription [::data-page/params ::page] :event-fn (fn [params] diff --git a/src/cljs/auto_ap/views/pages/pos/form.cljs b/src/cljs/auto_ap/views/pages/pos/form.cljs index 58e2d5f0..b1b5f71c 100644 --- a/src/cljs/auto_ap/views/pages/pos/form.cljs +++ b/src/cljs/auto_ap/views/pages/pos/form.cljs @@ -1,25 +1,15 @@ (ns auto-ap.views.pages.pos.form - (:require - [auto-ap.events :as events] - [auto-ap.forms :as forms] - [auto-ap.subs :as subs] - [auto-ap.utils :refer [dollars=]] - [auto-ap.views.components.dropdown :refer [drop-down]] - [auto-ap.views.components.expense-accounts-field :as expense-accounts-field :refer [expense-accounts-field recalculate-amounts]] - [auto-ap.views.components.layouts :as layouts] - [auto-ap.views.components.money-field :refer [money-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.status :as status] - [auto-ap.views.utils :refer [date->str date-picker dispatch-event standard with-user]] - [cljs-time.core :as c] - [clojure.spec.alpha :as s] - [clojure.string :as str] - [re-frame.core :as re-frame])) -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::form] - (fn [{:keys [data status]} _] - false)) + (:require + [auto-ap.forms :as forms] + [auto-ap.subs :as subs] + [auto-ap.views.components.layouts :as layouts] + [auto-ap.views.components.money-field :refer [money-field]] + [auto-ap.views.utils + :refer [date->str date-picker dispatch-event standard]] + [re-frame.core :as re-frame] + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components :as com])) + (re-frame/reg-event-db ::editing @@ -27,70 +17,56 @@ (let [which (update which :date #(date->str % standard))] (forms/start-form db ::form which)))) -(def sales-order-form (forms/vertical-form {:can-submit [::can-submit] - :change-event [::forms/changed] - :submit-event [::saving ] - :id ::form})) -(defn form [{:keys [can-change-amount?] :as params}] +(defn form [] [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field raw-field error-notification submit-button ]} sales-order-form] - (with-meta - (form-inline (assoc params :title "Sales order") - [:<> - (when-not @(re-frame/subscribe [::subs/client]) - (field [:span "Client" - [:span.has-text-danger " *"]] - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) - :entity->text :name - :type "typeahead-v3" - :field [:client] - :disabled true}])) + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])] + [form-builder/builder {:submit-event [::saving ] + :id ::form} + [form-builder/section {:title "Sales Order"} + (when-not @(re-frame/subscribe [::subs/client]) + [form-builder/field-v2 {:field :client} + "Client" + [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients]) + :entity->text :name + :disabled true}]]) - (field "Date" - [date-picker {:class-name "input" - :class "input" - :type "date" - :disabled true - :field [:date]}]) - (field "Total" - [money-field {:type "money" - :field [:total] - :disabled true}]) - (field "Tax" - [money-field {:type "money" - :field [:tax] - :disabled true}]) - (field "Discount" - [money-field {:type "money" - :field [:discount] - :disabled true}]) + [form-builder/field-v2 {:field :date} + "Date" + [date-picker {:output :cljs-date + :disabled true}]] + [form-builder/field-v2 {:field :total} + "Total" + [money-field {:disabled true}]] + [form-builder/field-v2 {:field :tax} + "Tax" + [money-field {:disabled true}] + [form-builder/field-v2 {:field :discount} + "Discount" + [money-field {:disabled true}]]] - (field "Returns" - [money-field {:type "money" - :field [:returns] - :disabled true}]) + [form-builder/field-v2 {:field :returns} + "Returns" + [money-field {:disabled true}]] - (field "Service Charge" - [money-field {:type "money" - :field [:service-charge] - :disabled true}]) + [form-builder/field-v2 {:field :service-charge} + "Service Charge" + [money-field {:disabled true}]] - (field "Tip" - [money-field {:type "money" - :field [:tip] - :disabled true}]) + [form-builder/field-v2 {:field :tip} + "Tip" + [money-field {:disabled true}]] - [:h1.subtitle.is-4 "Charges"] - [:ul - (for [charge (:charges data)] - [:li (:type-name charge) ": " (:total charge)])] + [form-builder/section {:title "Charges"} + [:ul + (for [charge (:charges data)] + ^{:key (:id charge)} + [:li (:type-name charge) ": " (:total charge)])]] - [:h1.subtitle.is-4 "Line Items"] - [:ul - (for [line-item (:line-items data)] - [:li (:item-name line-item) ": " (:total line-item) [:span.tag (:category line-item)]])]]) - {:key (:id data)}))]) + [form-builder/section {:title "Line Items"} + [:ul + (for [line-item (:line-items data)] + ^{:key (:item-name line-item)} + [:li (:item-name line-item) ": " (:total line-item) [:span.tag (:category line-item)]])]]]])]) diff --git a/src/cljs/auto_ap/views/pages/transactions.cljs b/src/cljs/auto_ap/views/pages/transactions.cljs index ee13f25b..3a05b037 100644 --- a/src/cljs/auto_ap/views/pages/transactions.cljs +++ b/src/cljs/auto_ap/views/pages/transactions.cljs @@ -112,7 +112,6 @@ {:id ::manual-import :events #{::manual/import-completed} :event-fn (fn [[_ result]] - (println result) [::status/info ::manual-import (str "Successfully " (str/join ", " diff --git a/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs b/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs index a8480993..7e61fe1b 100644 --- a/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs @@ -3,13 +3,10 @@ [auto-ap.forms :as forms] [auto-ap.status :as status] [auto-ap.subs :as subs] - [auto-ap.views.components.button-radio :refer [button-radio]] [auto-ap.views.components.expense-accounts-field :as expense-accounts-field - :refer [expense-accounts-field]] + :refer [expense-accounts-field-v2]] [auto-ap.views.components.modal :as modal] - [auto-ap.views.components.typeahead.vendor - :refer [search-backed-typeahead]] [auto-ap.views.pages.data-page :as data-page] [auto-ap.views.pages.transactions.common :refer [data-params->query-params]] @@ -19,7 +16,11 @@ [reagent.core :as r] [vimsical.re-frame.fx.track :as track] [auto-ap.events :as events] - [vimsical.re-frame.cofx.inject :as inject])) + [vimsical.re-frame.cofx.inject :as inject] + [auto-ap.forms.builder :as form-builder] + [malli.core :as m] + [auto-ap.schema :as schema] + [auto-ap.views.components :as com])) (re-frame/reg-sub ::can-submit @@ -105,45 +106,41 @@ (fn [] {::track/dispose {:id ::vendor-change}})) -(def code-form (forms/vertical-form {:submit-event [::code-selected] - :change-event [::changed] - :can-submit [::can-submit] - :id ::form})) + +(def bulk-update-schema + (m/schema + [:map + [:vendor schema/reference]])) (defn form-content [_] - (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field]} code-form] + (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])] + [form-builder/builder {:submit-event [::code-selected] + :change-event [::changed] + :can-submit [::can-submit] + :id ::form} - (form-inline {} - [:<> - (field "Vendor" - [search-backed-typeahead {:search-query (fn [i] - [:search_vendor - {:query i} - [:name :id]]) - :type "typeahead-v3" - :auto-focus true - :field [:vendor]}]) + + [form-builder/field-v2 {:field :vendor} + "Vendor" + [com/search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :auto-focus true}]] - (field "Approval Status" - [button-radio - {:type "button-radio" - :field [:transaction-approval-status] - :options [[:unapproved "Unapproved"] - [:requires-feedback "Client Review"] - [:approved "Approved"] - [:excluded "Excluded from Ledger"]]}]) + [form-builder/field-v2 {:field [:transaction-approval-status]} + "Approval Status" + [com/button-radio-input + {:options [[:unapproved "Unapproved"] + [:requires-feedback "Client Review"] + [:approved "Approved"] + [:excluded "Excluded from Ledger"]]}]] - (with-meta - (field nil - [expense-accounts-field {:type "expense-accounts" - :descriptor "account asssignment" - :percentage-only? true - :client (:client data) - :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))])) - :max 100 - :field [:accounts]}]) - {:key (some-> data :vendor :id str)}) - ]))) + [form-builder/raw-field-v2 {:field :accounts} + [expense-accounts-field-v2 {:descriptor "account asssignment" + :percentage-only? true + :client (:client data) + :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))])) + :max 100}]]])) (defn form [_] (r/create-class {:display-name "transaction-bulk-update-form" diff --git a/src/cljs/auto_ap/views/pages/transactions/common.cljs b/src/cljs/auto_ap/views/pages/transactions/common.cljs index 6a86e7da..1f011c2b 100644 --- a/src/cljs/auto_ap/views/pages/transactions/common.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/common.cljs @@ -23,25 +23,28 @@ [:bank-account [:name :yodlee-account-id :current-balance]]]) (defn data-params->query-params [params] - {:start (:start params 0) - :per-page (:per-page params) - :sort (:sort params) - :client-id (:id @(re-frame/subscribe [::subs/client])) - :vendor-id (:id (:vendor params)) - :date-range (:date-range params) - :account-id (:id (:account params)) - :bank-account-id (:id (:bank-account params)) - :amount-gte (:amount-gte (:amount-range params)) - :exact-match-id (some-> (:exact-match-id params) str) - :unresolved (:unresolved params) - :potential-duplicates (:potential-duplicates params) - :location (:location params) - :import-batch-id (some-> (:import-batch-id params) str) - :amount-lte (:amount-lte (:amount-range params)) - :description (:description params) - :approval-status (condp = @(re-frame/subscribe [::subs/active-page]) - :transactions nil - :unapproved-transactions :unapproved - :requires-feedback-transactions :requires-feedback - :excluded-transactions :excluded - :approved-transactions :approved)}) + (if (:exact-match-id params) + {:client-id (:id @(re-frame/subscribe [::subs/client])) + :exact-match-id (some-> (:exact-match-id params) str)} + {:start (:start params 0) + :per-page (:per-page params) + :sort (:sort params) + :client-id (:id @(re-frame/subscribe [::subs/client])) + :vendor-id (:id (:vendor params)) + :date-range (:date-range params) + :account-id (:id (:account params)) + :bank-account-id (:id (:bank-account params)) + :amount-gte (:amount-gte (:amount-range params)) + :exact-match-id (some-> (:exact-match-id params) str) + :unresolved (:unresolved params) + :potential-duplicates (:potential-duplicates params) + :location (:location params) + :import-batch-id (some-> (:import-batch-id params) str) + :amount-lte (:amount-lte (:amount-range params)) + :description (:description params) + :approval-status (condp = @(re-frame/subscribe [::subs/active-page]) + :transactions nil + :unapproved-transactions :unapproved + :requires-feedback-transactions :requires-feedback + :excluded-transactions :excluded + :approved-transactions :approved)})) diff --git a/src/cljs/auto_ap/views/pages/transactions/form.cljs b/src/cljs/auto_ap/views/pages/transactions/form.cljs index d14174f6..4c5ed719 100644 --- a/src/cljs/auto_ap/views/pages/transactions/form.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/form.cljs @@ -1,25 +1,31 @@ (ns auto-ap.views.pages.transactions.form (:require + [auto-ap.events :as events] [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] [auto-ap.status :as status] [auto-ap.subs :as subs] - [auto-ap.views.components.button-radio :refer [button-radio]] + [auto-ap.views.components :as com] [auto-ap.views.components.expense-accounts-field :as expense-accounts-field - :refer [expense-accounts-field]] + :refer [expense-accounts-field-v2]] [auto-ap.views.components.layouts :as layouts] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.components.typeahead.vendor - :refer [search-backed-typeahead]] [auto-ap.views.pages.transactions.common :refer [transaction-read]] [auto-ap.views.utils - :refer [->$ date->str dispatch-event pretty with-user]] + :refer [->$ date->str date-picker dispatch-event pretty with-user]] [clojure.string :as str] + [malli.core :as m] [re-frame.core :as re-frame] [react :as react] [reagent.core :as r] - [vimsical.re-frame.fx.track :as track] - [auto-ap.events :as events])) + [vimsical.re-frame.fx.track :as track])) + +(def schema + (m/schema [:map + [:vendor schema/reference] + [:accounts expense-accounts-field/schema] + [:approval-status schema/approval-status]])) ;; SUBS (re-frame/reg-sub @@ -44,13 +50,6 @@ accounts)}} transaction-read]}]})) -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::form] - (fn [{:keys [status]} _] - (not= :loading status))) - - ;; EVENTS (re-frame/reg-event-db @@ -211,10 +210,6 @@ ;; VIEWS -(def transaction-form (forms/vertical-form {:can-submit [::can-submit] - :change-event [::changed] - :submit-event [::saving ] - :id ::form})) (defn potential-transaction-rule-matches-box [{:keys [potential-transaction-rule-matches]}] (let [states @(re-frame/subscribe [::status/multi ::matching])] @@ -303,7 +298,6 @@ (defonce ^js/React.Context current-tab-context ( react/createContext "default")) (def ^js/React.Provider CurrentTabProvider (. current-tab-context -Provider)) -#_(println "Provider is" Provider) (def ^js/React.Consumer CurrentTabConsumer (. current-tab-context -Consumer)) (defn tabs [props & _] @@ -340,122 +334,106 @@ [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form])} (let [{:keys [data] } @(re-frame/subscribe [::forms/form ::form]) locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))]) - {:keys [form-inline field error-notification submit-button ]} transaction-form is-admin? @(re-frame/subscribe [::subs/is-admin?]) is-power-user? @(re-frame/subscribe [::subs/is-power-user?]) should-disable-for-client? (and (not (or is-admin? is-power-user?)) (not= :requires-feedback (:original-status data))) is-already-matched? (:payment data)] - (with-meta - (form-inline {:title "Transaction"} - [:<> + [form-builder/builder {:change-event [::changed] + :submit-event [::saving ] + :id ::form + :schema schema} + [form-builder/section {:title "Transaction"} + [:<> - (when (and @(re-frame/subscribe [::subs/is-admin?]) - (get-in data [:yodlee-merchant])) - [:div.control - [:p.help "Merchant"] - [:input.input {:type "text" - :disabled true - :value (str (get-in data [:yodlee-merchant :name]) - " - " - (get-in data [:yodlee-merchant :yodlee-id]))}]]) + (when is-admin? - (when is-admin? - (field "Matched Rule" - [:input.input {:type "text" - :field [:matched-rule :note] - :disabled "disabled"}])) - (field "Amount" - [:input.input {:type "text" - :field [:amount] - :disabled "disabled"}]) - (field "Description" - [:input.input {:type "text" - :field [:description-original] - :disabled "disabled"}]) + [form-builder/field-v2 {:field [:matched-rule :note] } + "Matched Rule" + [:input.input {:type "text" :disabled "disabled"}]]) + [form-builder/field-v2 {:field :amount} + "Amount" + [:input.input {:type "text" + :disabled "disabled"}]] + [form-builder/field-v2 {:field [:description-original]} + "Description" + [:input.input {:type "text" + :disabled "disabled"}]] - (field "Date" - [:input.input {:type "text" - :field [:date] - :disabled "disabled"}]) + [form-builder/field-v2 {:field [:date]} + "Date" + [date-picker { + :disabled "disabled"}]] - (when (and (:payment data) - (or is-admin? is-power-user?)) - [:p.notification.is-info.is-light>div.level>div.level-left - [:div.level-item "This transaction is linked to a payment "] - [:div.level-item [:button.button.is-warning {:on-click (dispatch-event [::unlink])} "Unlink"]]]) - [tabs {:default-tab :details} - (when - (and (seq (:potential-transaction-rule-matches data)) - (not (:matched-rule data)) - is-admin?) - [tab {:title "Transaction Rule" :key :transaction-rule} - [potential-transaction-rule-matches-box {:potential-transaction-rule-matches (:potential-transaction-rule-matches data)}]]) - (when - (and (seq (:potential-autopay-invoices-matches data)) - (not is-already-matched?) - (or is-admin? is-power-user?)) - [tab {:title "Autopay Invoices" :key :autopay-invoices} - [potential-autopay-invoices-matches-box {:potential-autopay-invoices-matches (:potential-autopay-invoices-matches data)}]]) + (when (and (:payment data) + (or is-admin? is-power-user?)) + [:p.notification.is-info.is-light>div.level>div.level-left + [:div.level-item "This transaction is linked to a payment "] + [:div.level-item [:button.button.is-warning {:on-click (dispatch-event [::unlink])} "Unlink"]]]) + [tabs {:default-tab :details} + (when + (and (seq (:potential-transaction-rule-matches data)) + (not (:matched-rule data)) + is-admin?) + [tab {:title "Transaction Rule" :key :transaction-rule} + [potential-transaction-rule-matches-box {:potential-transaction-rule-matches (:potential-transaction-rule-matches data)}]]) + (when + (and (seq (:potential-autopay-invoices-matches data)) + (not is-already-matched?) + (or is-admin? is-power-user?)) + [tab {:title "Autopay Invoices" :key :autopay-invoices} + [potential-autopay-invoices-matches-box {:potential-autopay-invoices-matches (:potential-autopay-invoices-matches data)}]]) - (when - (and (seq (:potential-unpaid-invoices-matches data)) - (not is-already-matched?) - (or is-admin? is-power-user?)) - [tab {:title "Unpaid Invoices" :key :unpaid-invoices} - [potential-unpaid-invoices-matches-box {:potential-unpaid-invoices-matches (:potential-unpaid-invoices-matches data)}]]) - (when - (and (seq (:potential-payment-matches data)) - (not is-already-matched?) - (or is-admin? is-power-user?)) - [tab {:title "Payment" :key :payment} - [potential-payment-matches-box {:potential-payment-matches (:potential-payment-matches data)}]]) + (when + (and (seq (:potential-unpaid-invoices-matches data)) + (not is-already-matched?) + (or is-admin? is-power-user?)) + [tab {:title "Unpaid Invoices" :key :unpaid-invoices} + [potential-unpaid-invoices-matches-box {:potential-unpaid-invoices-matches (:potential-unpaid-invoices-matches data)}]]) + (when + (and (seq (:potential-payment-matches data)) + (not is-already-matched?) + (or is-admin? is-power-user?)) + [tab {:title "Payment" :key :payment} + [potential-payment-matches-box {:potential-payment-matches (:potential-payment-matches data)}]]) - [tab {:title "Details" :key :details} - [:div - (field "Vendor" - [search-backed-typeahead {:search-query (fn [i] - [:search_vendor - {:query i} - [:name :id]]) - :type "typeahead-v3" - :auto-focus true - :field [:vendor] - :disabled (or (boolean (:payment data)) - should-disable-for-client?)}]) - (with-meta - (field nil - [expense-accounts-field - {:type "expense-accounts" - :field [:accounts] - :max (Math/abs (js/parseFloat (:amount data))) - :descriptor "credit account" - :client (:client data) - :disabled (or (boolean (:payment data)) - should-disable-for-client?) - :locations locations}]) - {:key (str (:id (:vendor data)))}) - (field "Approval Status" - [button-radio - {:type "button-radio" - :field [:approval-status] - :options [[:unapproved "Unapproved"] - [:requires-feedback "Client Review"] - [:approved "Approved"] - [:excluded "Excluded from Ledger"]] - :disabled should-disable-for-client?}]) + [tab {:title "Details" :key :details} + [:div + [form-builder/field-v2 {:field :vendor} + "Vendor" + [com/search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :auto-focus true + :disabled (or (boolean (:payment data)) + should-disable-for-client?)}]] + [form-builder/raw-field-v2 {:field :accounts} + [expense-accounts-field-v2 + {:max (Math/abs (js/parseFloat (:amount data))) + :descriptor "credit account" + :client (:client data) + :disabled (or (boolean (:payment data)) + should-disable-for-client?) + :locations locations}]] + [form-builder/field-v2 {:field :approval-status} + "Approval Status" + [com/button-radio-input + {:options [[:unapproved "Unapproved"] + [:requires-feedback "Client Review"] + [:approved "Approved"] + [:excluded "Excluded from Ledger"]] + :disabled should-disable-for-client?}]] - (field "Forecasted-transaction" - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/forecasted-transactions-for-client (:id (:client data))]) - :entity->text :identifier - :type "typeahead-v3" - :field [:forecast-match]}]) - (error-notification) - (when-not should-disable-for-client? - (submit-button "Save"))]]]]) - {:key (:id data)}))]) + [form-builder/field-v2 {:field [:forecast-match]} + "Forecasted-transaction" + [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/forecasted-transactions-for-client (:id (:client data))]) + :entity->text :identifier}]] + [form-builder/error-notification] + (when-not should-disable-for-client? + [form-builder/submit-button "Save"])]]]]]])]) (defn form [_] (r/create-class diff --git a/src/cljs/auto_ap/views/pages/transactions/manual.cljs b/src/cljs/auto_ap/views/pages/transactions/manual.cljs index 89b4b143..06198a34 100644 --- a/src/cljs/auto_ap/views/pages/transactions/manual.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/manual.cljs @@ -5,7 +5,10 @@ [auto-ap.subs :as subs] [auto-ap.views.components.modal :as modal] [auto-ap.views.utils :refer [dispatch-event with-user]] - [re-frame.core :as re-frame])) + [re-frame.core :as re-frame] + [auto-ap.forms.builder :as form-builder] + [auto-ap.schema :as schema] + [malli.core :as m])) (re-frame/reg-sub ::can-submit @@ -13,22 +16,19 @@ (fn [{ {:keys [data]} :data}] (not-empty data))) -(def import-form (forms/vertical-form {:submit-event [::save] - :change-event [::forms/change ::form] - :can-submit [::can-submit] - :id ::form})) +(def schema + (m/schema [:map [:data schema/not-empty-string]])) -(defn form [{import-completed-event :import-completed}] - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline horizontal-field field raw-field error-notification submit-button]} import-form] +(defn form [] + [form-builder/builder {:submit-event [::try-save] + :id ::form + :schema schema} - (form-inline {} - [:div.field - [:label.label - "Yodlee manual import table"] - [:div.control - [raw-field - [:textarea.textarea {:field [:data]}]]]]))) + [form-builder/field-v2 {:required? true + :field :data} + "Yodlee manual import table" + [:textarea.textarea ]] + [form-builder/hidden-submit-button]]) (re-frame/reg-event-fx ::opening @@ -38,19 +38,16 @@ :confirm {:value "Import" :status-from [::status/single ::form] :class "is-primary" - :on-click (dispatch-event [::save]) - :can-submit [::can-submit] + :on-click (dispatch-event [::try-save]) :close-event [::status/completed ::form]}}] :db (-> db (forms/start-form ::form {:client-id (:id @(re-frame/subscribe [::subs/client])) :data ""}))})) - - (re-frame/reg-event-fx ::import-completed - (fn [{:keys [db]} [_ {:keys [imported errors] :as result}]] + (fn [_ _] {:dispatch [::modal/modal-closed ]})) (re-frame/reg-event-fx @@ -66,6 +63,15 @@ :uri (str "/api/transactions/batch-upload") :on-success [::import-completed]}}))) +(re-frame/reg-event-fx + ::try-save + [(forms/in-form ::form)] + (fn [{:keys [db]}] + (if (not (m/validate schema (:data db))) + {:dispatch-n [[::status/error ::form [{:message "Please correct any errors and try again"}]] + [::forms/attempted-submit ::form]]} + {:dispatch [::save]}))) + diff --git a/src/cljs/auto_ap/views/pages/transactions/table.cljs b/src/cljs/auto_ap/views/pages/transactions/table.cljs index 201a716b..8df9ac91 100644 --- a/src/cljs/auto_ap/views/pages/transactions/table.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/table.cljs @@ -1,32 +1,35 @@ (ns auto-ap.views.pages.transactions.table - (:require [auto-ap.events :as events] - [auto-ap.subs :as subs] - [auto-ap.views.components.dropdown - :refer - [drop-down drop-down-contents]] - [auto-ap.views.components.grid :as grid] - [auto-ap.views.pages.transactions.form :as edit] - [auto-ap.views.utils - :refer - [action-cell-width date->str dispatch-event dispatch-event-with-propagation nf pretty with-role]] - [goog.string :as gstring] - [re-frame.core :as re-frame] - [auto-ap.views.components.buttons :as buttons] - [auto-ap.status :as status] - [auto-ap.views.pages.data-page :as data-page] - [bidi.bidi :as bidi] - [cemerick.url :as url] - [auto-ap.routes :as routes])) + (:require + [auto-ap.events :as events] + [auto-ap.routes :as routes] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.buttons :as buttons] + [auto-ap.views.components.dropdown + :refer [drop-down drop-down-contents]] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.pages.transactions.form :as edit] + [auto-ap.views.utils + :refer [action-cell-width + date->str + dispatch-event-with-propagation + nf + pretty + with-role]] + [bidi.bidi :as bidi] + [cemerick.url :as url] + [re-frame.core :as re-frame])) (re-frame/reg-event-fx ::editing-matches-found - (fn [{:keys [db]} [_ which matches]] + (fn [_ [_ which matches]] {:dispatch [::edit/editing which (:potential-payment-matches matches) (:potential-autopay-invoices-matches matches) (:potential-unpaid-invoices-matches matches) (:potential-transaction-rule-matches matches)]})) (re-frame/reg-event-fx ::editing-matches-failed - (fn [{:keys [db]} [_ which payment-matches]] + (fn [_ [_ which payment-matches]] {:dispatch [::edit/editing which payment-matches]})) @@ -71,12 +74,10 @@ (fn [{table-params :db} [_ params :as z]] {:db (merge table-params params)})) -(defn table [{:keys [id data-page check-boxes?]}] +(defn table [{:keys [data-page check-boxes?]}] (let [selected-client @(re-frame/subscribe [::subs/client]) - {:keys [data status params]} @(re-frame/subscribe [::data-page/page data-page]) - states @(re-frame/subscribe [::status/multi ::edits]) - is-power-user? @(re-frame/subscribe [::subs/is-power-user?]) - is-admin? @(re-frame/subscribe [::subs/is-admin?])] + {:keys [data params]} @(re-frame/subscribe [::data-page/page data-page]) + states @(re-frame/subscribe [::status/multi ::edits])] [grid/grid {:data-page data-page :column-count (if selected-client 6 7) :check-boxes? check-boxes?} @@ -94,7 +95,7 @@ [grid/sortable-header-cell {:sort-key "status" :sort-name "Status" :style {:width "7em"}} "Status"] [grid/header-cell {:style {:width (action-cell-width 3)}}]]] [grid/body - (for [{:keys [client account vendor approval-status payment expected-deposit status bank-account description-original date amount id yodlee-merchant ] :as i} (:data data)] + (for [{:keys [client vendor payment expected-deposit status bank-account description-original date amount id yodlee-merchant ] :as i} (:data data)] ^{:key id} [grid/row {:class (:class i) :id id :entity i} (when-not selected-client diff --git a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs index 691e92ea..bd50a7a4 100644 --- a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs @@ -51,7 +51,8 @@ (re-frame/reg-event-fx ::unmounted (fn [{:keys [db]} _] - {:dispatch [::data-page/dispose :invoices] + {:dispatch-n [[::data-page/dispose :invoices] + [::forms/form-closing ::form/form]] ::forward/dispose [{:id ::updated} {:id ::checks-printed}] ::track/dispose [{:id ::params}]})) diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index e6523da6..257ffa3e 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -11,7 +11,6 @@ [re-frame.core :as re-frame] [react-transition-group :as react-transition-group] #_{:clj-kondo/ignore [:unused-namespace]} - [react-datepicker :as react-datepicker] [reagent.core :as reagent] [reagent.core :as r] [react :as react] @@ -64,7 +63,7 @@ (def login-url (let [client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com" redirect-uri (js/encodeURI (str (.-origin (.-location js/window)) "/api/oauth"))] - (str "https://accounts.google.com/o/oauth2/auth?access_type=online&client_id=" client-id "&redirect_uri=" redirect-uri "&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile"))) + (str "https://accounts.google.com/o/oauth2/auth?access_type=online&client_id=" client-id "&redirect_uri=" redirect-uri "&response_type=code&max_auth_age=0&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile"))) (defn dispatch-value-change [event] (fn [e] @@ -107,26 +106,6 @@ (when d (format/parse f d))) -(defn dispatch-date-change [event] - (fn [e] - (re-frame/dispatch (conj event - (if (str/blank? e) - e - (date->str (t/from-default-time-zone (c/from-date e)) standard)))))) - -(defn dispatch-cljs-date-change [event] - (fn [e] - (re-frame/dispatch (conj event - (if (str/blank? e) - e - (c/to-local-date e)))))) - -;; TODO inline on-changes causes each field to be rerendered each time. When we fix this -;; let's make sure that we find away not to trigger a re-render for every component any time any form field -;; changes -(defmulti do-bind (fn [_ {:keys [type]}] - type)) - (defn with-keys [children] (map-indexed (fn [i c] ^{:key i} c) children)) @@ -151,358 +130,33 @@ (first children) [:span])]))) +(defn appearing-group [] + (let [children (r/children (r/current-component))] + (into [transition-group {:exit true + :enter true} + (for [child children] + ^{:key (:key (meta child))} + [transition + {:timeout 200 + :exit true + :in true #_ (= current-stack- (:key (meta child)))} + (clj->js (fn [state] + (r/as-element + [:div {:style { + :transition "opacity 150ms ease-in-out" + :opacity (cond + (= "entered" state) + 1.0 -(defn multi-field [{:keys [value]} ] - (let [value-repr (reagent/atom (mapv - (fn [x] - (assoc x :key (random-uuid) :new? false)) - value))] - (fn [{:keys [template on-change allow-change? disable-new? disable-remove?]} ] - (let [value @value-repr - already-has-new-row? (= [:key :new?] (keys (last value))) - value (if (or already-has-new-row? disable-new?) - value - (conj value {:key (random-uuid) - :new? true}))] - [:div {:style {:margin-bottom "0.25em"}} - (for [[i override] (map vector (range) value) - :let [is-disabled? (if (= false allow-change?) - (not (boolean (:new? override))) - nil)] - ] - ^{:key (:key override)} - [:div.level {:style {:margin-bottom "0.25em"}} - [:div.level-left {:style {:padding "0.5em 1em"} - :class (cond - (and (= i (dec (count value))) - (:new? override)) - "has-background-light" + (= "entering" state) + 0.0 - (:new? override) - "has-background-info-light" - :else - "")} - (let [template (if (fn? template) - (template override) - template)] - - [:<> (for [[idx template] (map vector (range ) template)] - ^{:key idx} + (= "exiting" state) + 0.0 - [:div.level-item - (update template 1 assoc - :value (let [value (get-in override (get-in template [1 :field])) ;; TODO this is really ugly to support maps or strings - value (if (map? value) - (dissoc value :key :new?) - value)] - (if (= value {}) - nil - value)) - :disabled (or is-disabled? (get-in template [1 :disabled])) - :on-change (fn [e] - (reset! value-repr - (into [] - (filter (fn [r] - (not= [:key :new?] (keys r))) - (assoc-in value - (into [i] (get-in template [1 :field])) - (let [this-value (if (and e (.. e -target)) - (.. e -target -value ) - e)] - (if (map? this-value) - (update this-value :key (fnil identity (random-uuid))) - this-value)) )))) - (on-change (mapv - (fn [v] - (dissoc v :new? :key)) - @value-repr))))]) - ]) - (when-not disable-remove? - [:div.level-item - [:a.button.level-item - {:disabled is-disabled? - :on-click (fn [] - (when-not is-disabled? - (reset! value-repr (into [] - (filter (fn [{:keys [key ]}] - (not= key (:key override))) - (filter (fn [r] - (not= [:key :new?] (keys r))) - value)))) - - (on-change (mapv - (fn [v] - (dissoc v :new? :key)) - @value-repr))))} - [:span.icon [:span.icon-remove]]]]) - ]])])))) - -(defmethod do-bind "select" [dom {:keys [field allow-nil? subscription event class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (dispatch-value-change (conj event field)) - - :value (or (get-in subscription field) "") - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec) - options (if allow-nil? - (with-keys (conj rest [:option {:value nil}])) - (with-keys rest))] - (into [dom (dissoc keys :allow-nil?)] options))) - - -(defmethod do-bind "radio" [dom {:keys [field subscription event class value spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (dispatch-value-change (conj event field)) - :checked (= (get-in subscription field) value) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field )))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "checkbox" [dom {:keys [field subscription event class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (dispatch-event (-> event - (conj field) - (conj (not (get-in subscription field))))) - :checked (boolean (get-in subscription field)) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field )))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "typeahead" [dom {:keys [field text-field event text-event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [selected text-value] - (re-frame/dispatch (conj (conj event field) selected)) - (when text-field - (re-frame/dispatch (conj (conj (or text-event event) text-field) text-value)))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "multi-field" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [value] - (re-frame/dispatch (conj (conj event field) value))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - - -(defmethod do-bind "typeahead-entity" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [selected] - (re-frame/dispatch (conj (conj event field) selected)) - #_(when text-field - (re-frame/dispatch (conj (conj (or text-event event) text-field) text-value)))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "typeahead-v3" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [selected] - (re-frame/dispatch (conj (conj event field) selected)) - #_(when text-field - (re-frame/dispatch (conj (conj (or text-event event) text-field) text-value)))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "date" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - selected (get-in subscription field) - - selected (cond (string? selected) - (c/to-date (t/to-default-time-zone (t/from-default-time-zone (str->date selected standard)))) - - (instance? goog.date.DateTime selected) - (c/to-date (t/to-default-time-zone (t/from-default-time-zone selected))) - - (instance? goog.date.Date selected) - (c/to-date selected) - - :else - selected ) - keys (assoc keys - :on-change (if (:cljs-date? keys) - (dispatch-cljs-date-change (conj event field)) - (dispatch-date-change (conj event field))) - :selected selected - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "date2" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - selected (get-in subscription field) - keys (assoc keys - :on-change (fn [v] - (re-frame/dispatch (-> event (conj field) (conj v)))) - - :value selected - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "expense-accounts" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :value (get-in subscription field) - :event (conj event field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :spec)] - (into [dom keys] (with-keys rest)))) - - -(defmethod do-bind "button-radio" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :value (get-in subscription field) - :on-change (fn [v] - (re-frame/dispatch (-> event (conj field) (conj v)))) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :event :subscription :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "number" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [e] - (.preventDefault e) - (re-frame/dispatch (-> event - (conj field) - (conj (let [val (.. e -target -value)] - (cond (and val - (re-matches #"[\-]?(\d+)(\.\d{2})?" val)) - (js/parseFloat val) - - (str/blank? val ) - nil - - :else - val)))))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "textarea->table" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [x] - (re-frame/dispatch (-> event - (conj field) - (conj x)))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind "money" [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (fn [x] - (re-frame/dispatch (-> event - (conj field) - (conj x)))) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defmethod do-bind :default [dom {:keys [field event subscription class spec] :as keys} & rest] - (let [field (if (keyword? field) [field] field) - event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (dispatch-value-change (conj event field)) - :value (get-in subscription field) - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec)] - (into [dom keys] (with-keys rest)))) - -(defn bind-field [all] - (apply do-bind all)) - - - -(defn horizontal-field [label & controls] - [:div.field.is-horizontal - (when label - [:div.field-label - label - ]) - (into - [:div.field-body] - (with-keys (map (fn [x] [:div.field x]) controls)))]) - -(def date-picker - (reagent/adapt-react-class (.-default react-datepicker))) - -(defn date-picker-friendly [params] - [date-picker (assoc params - :class-name "input" - :disabled-keyboard-navigation true - :start-open false - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :next-month-button-label "" - :next-month-label "" - :type "date")]) + (= "exited" state) + 0.0)}} + child])))])]))) (defn coerce-date [d] (cond (and (string? d) @@ -524,7 +178,7 @@ :else nil )) -(defn date-picker-optional-internal [params] +(defn date-picker-internal [params] (let [[text set-text ] (react/useState (some-> params :value coerce-date (date->str standard))) [value set-value ] (react/useState (some-> params :value coerce-date)) @@ -561,12 +215,14 @@ (swap-external-value (some-> (.. e -target -value) coerce-date)))) :on-blur (fn [] - (swap-external-value (some-> text coerce-date))) + (swap-external-value (some-> text coerce-date)) + (when (:on-blur params) + ((:on-blur params)))) :type "date" :placeholder "12/1/2021")] ]])) -(defn date-picker-optional [] - [:f> date-picker-optional-internal +(defn date-picker [] + [:f> date-picker-internal (r/props (r/current-component))]) (defn local-now [] @@ -654,3 +310,25 @@ :else x)) + + +(defn parse-jwt [jwt] + (when-let [json (some-> jwt + (str/split #"\.") + second + base64/decodeString)] + (js->clj (.parse js/JSON json) :keywordize-keys true))) + +(defn coerce-float [f] + (cond (str/blank? f) + nil + + (float? f) + f + + (and (string? f) + (not (js/Number.isNaN (js/parseFloat f)))) + (js/parseFloat f) + + :else + nil))