split front end to separate repo
23
.gitignore
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
./dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
24
README.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# cmc_fe
|
||||
|
||||
## Project setup
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
5
babel.config.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
1
dist/css/165.83e5555a.css
vendored
Normal file
1
dist/css/17.ae3c234a.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.scroller[data-v-3c9948e5]{height:500px}.customer[data-v-3c9948e5]{height:32%;padding:0 12px;display:flex;align-items:center}
|
1
dist/css/287.b8b74994.css
vendored
Normal file
1
dist/css/337.1da97b3f.css
vendored
Normal file
1
dist/css/553.c3657471.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.v-switch .v-label{-webkit-padding-start:10px;padding-inline-start:10px}.v-switch__loader{display:flex}.v-switch__thumb,.v-switch__track{background-color:currentColor;transition:none}.v-selection-control--error:not(.v-selection-control--disabled) .v-switch__thumb,.v-selection-control--error:not(.v-selection-control--disabled) .v-switch__track{background-color:rgb(var(--v-theme-error))}.v-switch__track{border-radius:8px;height:14px;opacity:.6;width:36px;cursor:pointer}.v-switch--inset .v-switch__track{border-radius:14px;height:28px;width:48px}.v-switch__thumb{align-items:center;border-radius:50%;color:rgb(var(--v-theme-surface));display:flex;height:20px;justify-content:center;width:20px;pointer-events:none;transition:transform .15s cubic-bezier(.4,0,.2,1);box-shadow:0 2px 4px -1px var(--v-shadow-key-umbra-opacity,rgba(0,0,0,.2)),0 4px 5px 0 var(--v-shadow-key-penumbra-opacity,rgba(0,0,0,.14)),0 1px 10px 0 var(--v-shadow-key-penumbra-opacity,rgba(0,0,0,.12))}.v-switch--inset .v-switch__thumb{box-shadow:0 0 0 0 var(--v-shadow-key-umbra-opacity,rgba(0,0,0,.2)),0 0 0 0 var(--v-shadow-key-penumbra-opacity,rgba(0,0,0,.14)),0 0 0 0 var(--v-shadow-key-penumbra-opacity,rgba(0,0,0,.12))}.v-switch--loading .v-selection-control__input>.v-icon,.v-switch:not(.v-switch--loading) .v-icon~.v-switch__thumb{display:none}.v-switch .v-selection-control{min-height:var(--v-input-control-height)}.v-switch .v-selection-control__wrapper{width:auto}.v-switch .v-selection-control__input{border-radius:50%;transition:transform .15s cubic-bezier(.4,0,.2,1);transform:translateX(-10px);position:absolute}.v-switch .v-selection-control--dirty .v-selection-control__input{transform:translateX(10px)}.v-switch.v-switch--indeterminate .v-selection-control__input{transform:scale(.8)}.v-switch.v-switch--indeterminate .v-switch__thumb{transform:scale(.75);box-shadow:none}.v-selection-control{align-items:center;contain:layout;display:flex;flex:1 0;grid-area:control;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.v-selection-control .v-label{white-space:normal;word-break:break-word;height:100%;width:100%}.v-selection-control--disabled{opacity:var(--v-disabled-opacity);pointer-events:none}.v-selection-control--disabled .v-label,.v-selection-control--error .v-label{opacity:1}.v-selection-control--error:not(.v-selection-control--disabled) .v-label{color:rgb(var(--v-theme-error))}.v-selection-control--inline{display:inline-flex;flex:0 0 auto;min-width:0;max-width:100%}.v-selection-control--inline .v-label{width:auto}.v-selection-control--density-default{--v-selection-control-size:40px}.v-selection-control--density-comfortable{--v-selection-control-size:36px}.v-selection-control--density-compact{--v-selection-control-size:28px}.v-selection-control__wrapper{display:inline-flex}.v-selection-control__input,.v-selection-control__wrapper{width:var(--v-selection-control-size);height:var(--v-selection-control-size);align-items:center;position:relative;justify-content:center;flex:none}.v-selection-control__input{display:flex;border-radius:50%}.v-selection-control__input input{cursor:pointer;position:absolute;left:0;top:0;width:100%;height:100%;opacity:0}.v-selection-control__input:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;border-radius:100%;background-color:currentColor;opacity:0;pointer-events:none}.v-selection-control__input:hover:before{opacity:calc(var(--v-theme-overlay-multiplier)*.04)}.v-selection-control__input>.v-icon{opacity:var(--v-medium-emphasis-opacity)}.v-selection-control--dirty .v-selection-control__input>.v-icon,.v-selection-control--disabled .v-selection-control__input>.v-icon,.v-selection-control--error .v-selection-control__input>.v-icon{opacity:1}.v-selection-control--error:not(.v-selection-control--disabled) .v-selection-control__input>.v-icon{color:rgb(var(--v-theme-error))}.v-selection-control--focus-visible .v-selection-control__input:before{opacity:calc(var(--v-theme-overlay-multiplier)*.12)}.v-selection-control-group{grid-area:control;display:flex;flex-direction:column}.v-selection-control-group--inline{flex-direction:row;flex-wrap:wrap}.v-sheet{display:block;border-color:rgba(var(--v-border-color),var(--v-border-opacity));border-style:solid;border-width:0;box-shadow:0 0 0 0 var(--v-shadow-key-umbra-opacity,rgba(0,0,0,.2)),0 0 0 0 var(--v-shadow-key-penumbra-opacity,rgba(0,0,0,.14)),0 0 0 0 var(--v-shadow-key-penumbra-opacity,rgba(0,0,0,.12));border-radius:0;background:rgb(var(--v-theme-surface));color:rgba(var(--v-theme-on-background),var(--v-high-emphasis-opacity))}.v-sheet--border{border-width:thin;box-shadow:none}.v-sheet--absolute{position:absolute}.v-sheet--fixed{position:fixed}.v-sheet--rounded{border-radius:4px}.item{height:100px}
|
1
dist/css/803.be0ee137.css
vendored
Normal file
1
dist/css/83.46f88035.css
vendored
Normal file
1
dist/css/975.9ad07ca2.css
vendored
Normal file
1
dist/css/app.f3159a54.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
[v-cloak]{display:none}.lastrefresh{font-size:10px;float:right;text-align:right;margin-bottom:0}.loading{margin:auto;display:block}.title-bar{background:#ffae00;color:#000}.menu-icon:after{background:#000;box-shadow:0 7px 0 #000,0 14px 0 #000}main{margin-top:2rem;padding-top:2rem}.icon,.icon-2{max-height:2rem}.icon-1{max-height:1rem}table td{padding-right:.4em;padding-left:.4em;padding-top:0;padding-bottom:0}table .button{margin-top:.2rem;margin-bottom:.2rem}.off-canvas{background:#ffe}#editModal{height:750px}img.img-small{max-height:16px}table tr.at_risk{background:#ffb6c1}table tr.at_risk:hover{background:pink!important}.scrollable{max-height:90vh;overflow-y:scroll}.scrollable-50{max-height:50vh}.inactive{background:#d3d3d3;color:gray}.scroller{border:1px dotted #333;height:600px}.item{height:138px;overflow-y:hidden;padding:0 12px;margin-bottom:2px;display:flex;align-items:center;border-bottom:1px solid #000}
|
7
dist/css/chunk-vendors.fab48178.css
vendored
Normal file
BIN
dist/favicon.ico
vendored
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
dist/fonts/materialdesignicons-webfont.21f691f0.ttf
vendored
Normal file
BIN
dist/fonts/materialdesignicons-webfont.54b0f60d.woff2
vendored
Normal file
BIN
dist/fonts/materialdesignicons-webfont.5d875350.eot
vendored
Normal file
BIN
dist/fonts/materialdesignicons-webfont.d671cbf6.woff
vendored
Normal file
BIN
dist/images/cmc-logo.png
vendored
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
dist/images/icons/Completed.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
dist/images/icons/Live.png
vendored
Normal file
After Width: | Height: | Size: 276 B |
BIN
dist/images/icons/finish.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
dist/images/icons/loading.gif
vendored
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
dist/images/icons/loading.png
vendored
Normal file
After Width: | Height: | Size: 791 B |
BIN
dist/images/icons/printed.png
vendored
Normal file
After Width: | Height: | Size: 500 B |
BIN
dist/images/icons/printedconfirm.png
vendored
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
dist/images/icons/repeat.png
vendored
Normal file
After Width: | Height: | Size: 941 B |
BIN
dist/images/icons/warning.png
vendored
Normal file
After Width: | Height: | Size: 359 B |
1
dist/index.html
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>cmc_fe</title><script defer="defer" src="/js/chunk-vendors.391950dc.js"></script><script defer="defer" src="/js/app.a93abe60.js"></script><link href="/css/chunk-vendors.fab48178.css" rel="stylesheet"><link href="/css/app.f3159a54.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but cmc_fe doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
114
dist/js/121.5997df3e.js
vendored
Normal file
1
dist/js/121.5997df3e.js.map
vendored
Normal file
2
dist/js/165.211e8e0a.js
vendored
Normal file
1
dist/js/165.211e8e0a.js.map
vendored
Normal file
2
dist/js/17.e124ae12.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
"use strict";(self["webpackChunkcmc_fe"]=self["webpackChunkcmc_fe"]||[]).push([[17],{8099:function(){},9017:function(e,t,l){l.r(t),l.d(t,{default:function(){return y}});var n=l(3396),s=l(7139),r=l(6824),o=l(8521),a=l(4162),i=l(165);const u=e=>((0,n.dD)("data-v-3c9948e5"),e=e(),(0,n.Cn)(),e),c=u((()=>(0,n._)("h3",null,"Customer List",-1))),d=u((()=>(0,n._)("hr",null,null,-1)));function f(e,t,l,u,f,g){const m=(0,n.up)("RecycleScroller");return(0,n.wg)(),(0,n.iD)(n.HY,null,[c,(0,n.Wm)(a.t,{"max-width":"500"},{default:(0,n.w5)((()=>[(0,n.Wm)(i.hw,{clearable:"",label:"Search",variant:"outlined",modelValue:f.searchQuery,"onUpdate:modelValue":t[0]||(t[0]=e=>f.searchQuery=e),density:"compact","append-inner-icon":"mdi-magnify"},null,8,["modelValue"])])),_:1}),(0,n.Wm)(m,{class:"scroller",items:g.filteredCustomers,"item-size":50,"key-field":"acc_no"},{default:(0,n.w5)((({item:e})=>[(0,n.Wm)(r.o,{class:"customer"},{default:(0,n.w5)((()=>[(0,n.Wm)(o.D,{cols:"6"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.acc_no)+" - "+(0,s.zw)(e.name),1)])),_:2},1024)])),_:2},1024),d])),_:1},8,["items"])],64)}var g=l(6943),m={props:{site_info:{},user_info:{}},data(){return{searchQuery:"",showSearch:!0,open:!1,list:[],limit:5e3,loading:!0,listreceived:!1}},components:{},computed:{filteredCustomers(){let e=this.searchQuery.toLowerCase();this.listreceived||this.getCustomerList();let t=this.list.filter((t=>t.name.toLowerCase().includes(e)||t.acc_no.includes(e)));return t}},methods:{async getCustomerList(){this.loading=!0;let e=this.$api_url+"/customers/list";g.Z.get(e,{params:{limit:this.limit,query:this.searchQuery}}).then((e=>{this.list=e.data,this.listreceived=!0,this.loading=!1})).catch((e=>console.log(e)))}}},p=l(89);const h=(0,p.Z)(m,[["render",f],["__scopeId","data-v-3c9948e5"]]);var y=h},8521:function(e,t,l){l.d(t,{D:function(){return m}});l(7658),l(8099);var n=l(1138),s=l(7139),r=l(3396),o=l(320);const a=["sm","md","lg","xl","xxl"],i=(()=>a.reduce(((e,t)=>(e[t]={type:[Boolean,String,Number],default:!1},e)),{}))(),u=(()=>a.reduce(((e,t)=>(e["offset"+(0,s.kC)(t)]={type:[String,Number],default:null},e)),{}))(),c=(()=>a.reduce(((e,t)=>(e["order"+(0,s.kC)(t)]={type:[String,Number],default:null},e)),{}))(),d={col:Object.keys(i),offset:Object.keys(u),order:Object.keys(c)};function f(e,t,l){let n=e;if(null!=l&&!1!==l){if(t){const l=t.replace(e,"");n+=`-${l}`}return"col"===e&&(n="v-"+n),"col"!==e||""!==l&&!0!==l?(n+=`-${l}`,n.toLowerCase()):n.toLowerCase()}}const g=["auto","start","end","center","baseline","stretch"],m=(0,o.a)({name:"VCol",props:{cols:{type:[Boolean,String,Number],default:!1},...i,offset:{type:[String,Number],default:null},...u,order:{type:[String,Number],default:null},...c,alignSelf:{type:String,default:null,validator:e=>g.includes(e)},...(0,n.Q)()},setup(e,t){let{slots:l}=t;const n=(0,r.Fl)((()=>{const t=[];let l;for(l in d)d[l].forEach((n=>{const s=e[n],r=f(l,n,s);r&&t.push(r)}));const n=t.some((e=>e.startsWith("v-col-")));return t.push({"v-col":!n||!e.cols,[`v-col-${e.cols}`]:e.cols,[`offset-${e.offset}`]:e.offset,[`order-${e.order}`]:e.order,[`align-self-${e.alignSelf}`]:e.alignSelf}),t}));return()=>{var t;return(0,r.h)(e.tag,{class:n.value},null==(t=l.default)?void 0:t.call(l))}}})},6824:function(e,t,l){l.d(t,{o:function(){return _}});l(7658),l(8099);var n=l(1138),s=l(7139),r=l(3396),o=l(320);const a=["sm","md","lg","xl","xxl"],i=["start","end","center"],u=["space-between","space-around","space-evenly"];function c(e,t){return a.reduce(((l,n)=>(l[e+(0,s.kC)(n)]=t(),l)),{})}const d=[...i,"baseline","stretch"],f=e=>d.includes(e),g=c("align",(()=>({type:String,default:null,validator:f}))),m=[...i,...u],p=e=>m.includes(e),h=c("justify",(()=>({type:String,default:null,validator:p}))),y=[...i,...u,"stretch"],v=e=>y.includes(e),C=c("alignContent",(()=>({type:String,default:null,validator:v}))),w={align:Object.keys(g),justify:Object.keys(h),alignContent:Object.keys(C)},b={align:"align",justify:"justify",alignContent:"align-content"};function S(e,t,l){let n=b[e];if(null!=l){if(t){const l=t.replace(e,"");n+=`-${l}`}return n+=`-${l}`,n.toLowerCase()}}const _=(0,o.a)({name:"VRow",props:{dense:Boolean,noGutters:Boolean,align:{type:String,default:null,validator:f},...g,justify:{type:String,default:null,validator:p},...h,alignContent:{type:String,default:null,validator:v},...C,...(0,n.Q)()},setup(e,t){let{slots:l}=t;const n=(0,r.Fl)((()=>{const t=[];let l;for(l in w)w[l].forEach((n=>{const s=e[n],r=S(l,n,s);r&&t.push(r)}));return t.push({"v-row--no-gutters":e.noGutters,"v-row--dense":e.dense,[`align-${e.align}`]:e.align,[`justify-${e.justify}`]:e.justify,[`align-content-${e.alignContent}`]:e.alignContent}),t}));return()=>{var t;return(0,r.h)(e.tag,{class:["v-row",n.value]},null==(t=l.default)?void 0:t.call(l))}}})}}]);
|
||||
//# sourceMappingURL=17.e124ae12.js.map
|
1
dist/js/17.e124ae12.js.map
vendored
Normal file
2
dist/js/189.d48eb76c.js
vendored
Normal file
1
dist/js/189.d48eb76c.js.map
vendored
Normal file
2
dist/js/287.3d3496d6.js
vendored
Normal file
1
dist/js/287.3d3496d6.js.map
vendored
Normal file
2
dist/js/337.7acf902b.js
vendored
Normal file
1
dist/js/337.7acf902b.js.map
vendored
Normal file
2
dist/js/463.dd239b65.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
"use strict";(self["webpackChunkcmc_fe"]=self["webpackChunkcmc_fe"]||[]).push([[463],{4463:function(n,u,c){c.r(u),c.d(u,{default:function(){return b}});var e=c(3396);const t={class:"about"},r=(0,e._)("h1",null,"About",-1),l=(0,e._)("p",null,null,-1),s=[r,l];function a(n,u){return(0,e.wg)(),(0,e.iD)("div",t,s)}var f=c(89);const o={},i=(0,f.Z)(o,[["render",a]]);var b=i}}]);
|
||||
//# sourceMappingURL=463.dd239b65.js.map
|
1
dist/js/463.dd239b65.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"js/463.dd239b65.js","mappings":"+KACOA,MAAM,S,GACTC,EAAAA,EAAAA,GAAc,UAAV,SAAK,G,GACTA,EAAAA,EAAAA,GAAO,kB,GADPC,EACAC,G,kCAFFC,EAAAA,EAAAA,IAGM,MAHNC,EAGM,E,aCHR,MAAMC,EAAS,CAAC,EAGVC,GAA2B,OAAgBD,EAAQ,CAAC,CAAC,SAASE,KAEpE,O","sources":["webpack://cmc_fe/./src/views/AboutView.vue","webpack://cmc_fe/./src/views/AboutView.vue?d56f"],"sourcesContent":["<template>\n <div class=\"about\">\n <h1>About</h1>\n <p></p>\n </div>\n</template>\n","import { render } from \"./AboutView.vue?vue&type=template&id=7c1ef17b\"\nconst script = {}\n\nimport exportComponent from \"/srv/http/Development/Clients/cmc_app/public/cmc_fe/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render]])\n\nexport default __exports__"],"names":["class","_createElementVNode","_hoisted_2","_hoisted_3","_createElementBlock","_hoisted_1","script","__exports__","render"],"sourceRoot":""}
|
2
dist/js/553.fb74f1fb.js
vendored
Normal file
1
dist/js/553.fb74f1fb.js.map
vendored
Normal file
274
dist/js/577.f88c2014.js
vendored
Normal file
1
dist/js/577.f88c2014.js.map
vendored
Normal file
2
dist/js/803.d5052a11.js
vendored
Normal file
1
dist/js/803.d5052a11.js.map
vendored
Normal file
2
dist/js/823.8ba5256b.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
"use strict";(self["webpackChunkcmc_fe"]=self["webpackChunkcmc_fe"]||[]).push([[823],{1823:function(e,o,n){n.r(o),n.d(o,{default:function(){return t}});var c={mounted(){this.endSession(),localStorage.removeItem("access_token"),window.location.href="/"},methods:{endSession(){console.log("Logging out...")}}};const s=c;var t=s}}]);
|
||||
//# sourceMappingURL=823.8ba5256b.js.map
|
1
dist/js/823.8ba5256b.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"js/823.8ba5256b.js","mappings":"wJACA,OACIA,UACIC,KAAKC,aACLC,aAAaC,WAAW,gBACxBC,OAAOC,SAASC,KAAQ,GAC5B,EACAC,QAAS,CACLN,aACIO,QAAQC,IAAI,iBAEhB,ICRR,MAAMC,EAAc,EAEpB,O","sources":["webpack://cmc_fe/./src/components/LogOut.vue","webpack://cmc_fe/./src/components/LogOut.vue?62d9"],"sourcesContent":["<script>\nexport default {\n mounted() {\n this.endSession()\n localStorage.removeItem('access_token')\n window.location.href = \"/\"\n },\n methods: {\n endSession(){\n console.log(\"Logging out...\")\n // Do something here to remove the token from the database\n }\n\n }\n}\n</script>\n","import script from \"./LogOut.vue?vue&type=script&lang=js\"\nexport * from \"./LogOut.vue?vue&type=script&lang=js\"\n\nconst __exports__ = script;\n\nexport default __exports__"],"names":["mounted","this","endSession","localStorage","removeItem","window","location","href","methods","console","log","__exports__"],"sourceRoot":""}
|
2
dist/js/83.37aa53f6.js
vendored
Normal file
1
dist/js/83.37aa53f6.js.map
vendored
Normal file
2
dist/js/app.a93abe60.js
vendored
Normal file
1
dist/js/app.a93abe60.js.map
vendored
Normal file
14
dist/js/chunk-vendors.391950dc.js
vendored
Normal file
1
dist/js/chunk-vendors.391950dc.js.map
vendored
Normal file
3
dist/js/html2pdf.bundle.min.js
vendored
Normal file
2
dist/js/webfontloader.95ddae74.js
vendored
Normal file
1
dist/js/webfontloader.95ddae74.js.map
vendored
Normal file
19
jsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
62
package.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"name": "cmc_fe",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "5.9.55",
|
||||
"@vuepic/vue-datepicker": "^3.6.4",
|
||||
"axios": "^1.2.2",
|
||||
"core-js": "^3.8.3",
|
||||
"moment": "^2.29.4",
|
||||
"roboto-fontface": "*",
|
||||
"sass": "^1.57.1",
|
||||
"sass-loader": "^13.2.0",
|
||||
"scss": "^0.2.4",
|
||||
"vue": "^3.2.13",
|
||||
"vue-datepicker": "^1.3.0",
|
||||
"vue-meta": "^2.4.0",
|
||||
"vue-router": "^4.0.3",
|
||||
"vue-virtual-scroller": "^2.0.0-beta.7",
|
||||
"vue3-html2pdf": "^1.1.2",
|
||||
"vue3-print-nb": "^0.1.4",
|
||||
"vuetify": "^3.0.0-beta.0",
|
||||
"webfontloader": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"vue-cli-plugin-vuetify": "~2.5.8",
|
||||
"webpack-plugin-vuetify": "^2.0.0-alpha.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
}
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
public/images/cmc-logo.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
public/images/icons/Completed.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/icons/Live.png
Normal file
After Width: | Height: | Size: 276 B |
BIN
public/images/icons/finish.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/icons/loading.gif
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/images/icons/loading.png
Normal file
After Width: | Height: | Size: 791 B |
BIN
public/images/icons/printed.png
Normal file
After Width: | Height: | Size: 500 B |
BIN
public/images/icons/printedconfirm.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
public/images/icons/repeat.png
Normal file
After Width: | Height: | Size: 941 B |
BIN
public/images/icons/warning.png
Normal file
After Width: | Height: | Size: 359 B |
16
public/index.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
3
public/js/html2pdf.bundle.min.js
vendored
Normal file
12
src/App.vue
Normal file
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<CMCApp />
|
||||
</template>
|
||||
<script>
|
||||
import CMCApp from './CMCApp.vue'
|
||||
export default {
|
||||
components: {
|
||||
CMCApp
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
99
src/CMCApp.vue
Normal file
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<v-app>
|
||||
<MyNav :user="user" :site_info="site_info" />
|
||||
<v-main class="ma-4">
|
||||
<router-view :site_info="site_info" :user_info="user_info"></router-view>
|
||||
</v-main>
|
||||
<v-footer>
|
||||
<sub>{{ site_info.name }} v{{ site_info.version }}</sub>
|
||||
</v-footer>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MyNav from './components/MyNav.vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
MyNav,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
site_info: {
|
||||
name: "Loading...",
|
||||
features: {}
|
||||
},
|
||||
user: {
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
email: "",
|
||||
token: "",
|
||||
logged_in: false
|
||||
},
|
||||
user_info: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'user.logged_in'(val) {
|
||||
console.log("Login status is " + val)
|
||||
},
|
||||
'site_info.name'() {
|
||||
document.title = this.site_info.name
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkLoginStatus() {
|
||||
let url = this.$api_url + "/users/check_login"
|
||||
console.log("Checking login status...")
|
||||
axios
|
||||
.post(url)
|
||||
.then(resp => {
|
||||
this.user.logged_in = resp.data.logged_in
|
||||
this.user.first_name = resp.data.user.first_name
|
||||
if (this.user.logged_in) {
|
||||
console.log("Logged in.")
|
||||
if (window.location.pathname == "/login") {
|
||||
this.$router.push("/")
|
||||
}
|
||||
this.getUserInfo()
|
||||
} else {
|
||||
console.log("Not logged in.")
|
||||
this.$router.push("/login")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("Error checking login status..." + error)
|
||||
this.$router.push("/login")
|
||||
})
|
||||
|
||||
},
|
||||
async getSiteInfo() {
|
||||
axios
|
||||
.get(this.$api_url + "/info")
|
||||
.then(response => {this.site_info = response.data})
|
||||
.catch(error => (console.log(error)))
|
||||
|
||||
},
|
||||
async getUserInfo() {
|
||||
let url = this.$api_url + "/users/info"
|
||||
console.log(url)
|
||||
axios.get(url)
|
||||
.then(resp => {
|
||||
this.user_info = resp.data
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSiteInfo()
|
||||
if (window.location.pathname != "/login") {
|
||||
this.checkLoginStatus()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
31
src/CommonMethods.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<script>
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
methods:{
|
||||
formatDate(d,f) {
|
||||
return moment(String(d)).format(f)
|
||||
},
|
||||
formatNumber(num,chars) {
|
||||
return parseFloat(num).toFixed(chars)
|
||||
},
|
||||
getDateNow() {
|
||||
let now = new Date()
|
||||
return now.toLocaleString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})
|
||||
},
|
||||
getTimeNow() {
|
||||
let now = new Date()
|
||||
return now.toLocaleString('en-GB', {
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
84
src/assets/css/app.css
Normal file
|
@ -0,0 +1,84 @@
|
|||
[v-cloak] {
|
||||
display:none;
|
||||
}
|
||||
.lastrefresh {
|
||||
font-size:10px;
|
||||
float:right;
|
||||
text-align:right;
|
||||
margin-bottom:0;
|
||||
}
|
||||
.loading {
|
||||
margin:auto;
|
||||
display:block;
|
||||
}
|
||||
.title-bar {
|
||||
background:#ffae00;
|
||||
color:#000;
|
||||
}
|
||||
.menu-icon::after {
|
||||
background:#000;
|
||||
box-shadow:0 7px 0 #000,0 14px 0 #000;
|
||||
}
|
||||
main {
|
||||
margin-top:2rem;
|
||||
padding-top:2rem;
|
||||
}
|
||||
.icon, .icon-2 {
|
||||
max-height:2rem;
|
||||
}
|
||||
.icon-1 {
|
||||
max-height:1rem;
|
||||
}
|
||||
table td {
|
||||
padding-right:0.4em;
|
||||
padding-left:0.4em;
|
||||
padding-top:0;
|
||||
padding-bottom:0;
|
||||
}
|
||||
table .button {
|
||||
margin-top:0.2rem;
|
||||
margin-bottom:0.2rem;
|
||||
|
||||
}
|
||||
.off-canvas {
|
||||
background:#ffffee
|
||||
}
|
||||
#editModal {
|
||||
height:750px;
|
||||
}
|
||||
img.img-small {
|
||||
max-height:16px;
|
||||
}
|
||||
table tr.at_risk {
|
||||
background:lightpink;
|
||||
}
|
||||
table tr.at_risk:hover {
|
||||
background:pink !important;
|
||||
}
|
||||
.scrollable {
|
||||
max-height:90vh;
|
||||
overflow-y:scroll;
|
||||
}
|
||||
.scrollable-50 {
|
||||
max-height:50vh;
|
||||
}
|
||||
.inactive {
|
||||
background:lightgrey;
|
||||
color: grey;
|
||||
}
|
||||
.scroller {
|
||||
border: 1px dotted #333;
|
||||
}
|
||||
.scroller {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 138px;
|
||||
overflow-y:hidden;
|
||||
padding: 0 12px;
|
||||
margin-bottom:2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
1
src/assets/css/app.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import './app.css'
|
1
src/assets/css/foundation.min.css
vendored
Normal file
69
src/assets/css/reports.css
Normal file
|
@ -0,0 +1,69 @@
|
|||
p,address,ul {
|
||||
font-size:1rem;
|
||||
margin-bottom:0.8rem;
|
||||
}
|
||||
body {
|
||||
|
||||
}
|
||||
.button-container {
|
||||
margin-left:8mm;
|
||||
}
|
||||
.page {
|
||||
max-width:210mm;
|
||||
min-width:210mm;
|
||||
max-height:296mm;
|
||||
min-height:296mm;
|
||||
}
|
||||
.pdf-scope {
|
||||
padding:8mm;
|
||||
}
|
||||
h1,h2,h3,h4,h5,hr {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
hr {
|
||||
margin-bottom: 8px;
|
||||
border-color:black;
|
||||
}
|
||||
hr.bold {
|
||||
border-width:4px;
|
||||
}
|
||||
header img {
|
||||
max-width:250px;
|
||||
}
|
||||
header img.smaller {
|
||||
max-width:200px;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
ul li {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.letter {
|
||||
margin-top:140px;
|
||||
}
|
||||
.signature {
|
||||
min-height:60px;
|
||||
}
|
||||
.text-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.text-italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.gap-before {
|
||||
margin-left:2em;
|
||||
}
|
||||
.gap-12 {
|
||||
margin-left:12em;
|
||||
}
|
||||
.red-text {
|
||||
color:red;
|
||||
}
|
||||
.b_line {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
BIN
src/assets/logo.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
6
src/assets/logo.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg width="488" height="424" viewBox="0 0 488 424" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M249.126 95.017L151.843 263.694L243.959 423.473L365.966 211.973L487.918 0.473206H303.629L249.126 95.017Z" fill="#1697F6"/>
|
||||
<path d="M122.007 211.973L128.396 223.096L219.402 65.2635L256.793 0.473206H243.959H0L122.007 211.973Z" fill="#AEDDFF"/>
|
||||
<path d="M303.629 0.473206C349.743 152.355 243.959 423.473 243.959 423.473L151.843 263.694L303.629 0.473206Z" fill="#1867C0"/>
|
||||
<path d="M256.793 0.473206C62.5042 0.473206 128.397 223.096 128.397 223.096L256.793 0.473206Z" fill="#7BC6FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 598 B |
31
src/common.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import moment from 'moment';
|
||||
export default {
|
||||
getAuthToken() {
|
||||
let tok = localStorage.getItem('access_token')
|
||||
console.log(tok)
|
||||
return tok
|
||||
},
|
||||
formatDate(dateVal, format) {
|
||||
if (dateVal) {
|
||||
return moment(String(dateVal)).format(format)
|
||||
}
|
||||
},
|
||||
getDateNow() {
|
||||
let now = new Date()
|
||||
return now.toLocaleString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})
|
||||
},
|
||||
getTimeNow() {
|
||||
let now = new Date()
|
||||
return now.toLocaleString('en-GB', {
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'})
|
||||
},
|
||||
}
|
16
src/components/LogOut.vue
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
this.endSession()
|
||||
localStorage.removeItem('access_token')
|
||||
window.location.href = "/"
|
||||
},
|
||||
methods: {
|
||||
endSession(){
|
||||
console.log("Logging out...")
|
||||
// Do something here to remove the token from the database
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
92
src/components/MyNav.vue
Normal file
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<v-app-bar
|
||||
color="yellow">
|
||||
<v-app-bar-nav-icon v-if="user.logged_in" variant="text" @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
|
||||
<v-toolbar-title>{{ site_info.name }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" v-if="user.logged_in">Hi {{ user.first_name }}</v-btn>
|
||||
</v-app-bar>
|
||||
<v-navigation-drawer
|
||||
v-if="user.logged_in"
|
||||
v-model="drawer"
|
||||
theme="dark">
|
||||
<v-list>
|
||||
<template v-for="item in items" v-bind:key="item.title">
|
||||
<template v-if="item.children">
|
||||
<v-list-item :title="item.title">
|
||||
<v-list>
|
||||
<v-list-item v-for="citem in item.children"
|
||||
v-bind:key="citem.title"
|
||||
:title="citem.title"
|
||||
:to="citem.value">
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-list-item :title="item.title" :to="item.value">
|
||||
</v-list-item>
|
||||
</template>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default{
|
||||
name: "MyNav",
|
||||
props: {
|
||||
site_info: {},
|
||||
user: {
|
||||
logged_in: false,
|
||||
first_name: "null"
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'user.logged_in'(isLoggedIn) {
|
||||
if (isLoggedIn) {
|
||||
this.items = this.get_menu()
|
||||
} else {
|
||||
this.items = []
|
||||
}
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
items: [],
|
||||
drawer: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (window.location.pathname != "/login") {
|
||||
this.items = this.get_menu()
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
get_menu() {
|
||||
let items = []
|
||||
items.push({title: "Dashboard", value:"/about"})
|
||||
items.push({title: "Customers",
|
||||
value:"/customers",
|
||||
children:[{title:"List",
|
||||
value:"/customers/list"},
|
||||
{title:"Contracts",
|
||||
value:"/customers/contracts/list"},
|
||||
{title:"Complaints",
|
||||
value:"/customers/complaints/list"},
|
||||
{title:"Medicated Feeds",
|
||||
value:"/customers/medicated-feeds/list"}
|
||||
]
|
||||
})
|
||||
items.push({title: "Sales Orders",
|
||||
value:"/sop",
|
||||
children:[{title:"Printed",
|
||||
value:"/sop/printed"}]
|
||||
})
|
||||
|
||||
items.push({title: "Logout", value:"/logout"})
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
33
src/components/PrintButtons.vue
Normal file
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<v-container class="button-container">
|
||||
<v-btn color="primary" @click="generatePdf()" class="mr-2">Create PDF</v-btn>
|
||||
<v-btn color="grey" v-print="printObj">Print</v-btn>
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
import html2pdf from 'html2pdf.js';
|
||||
export default {
|
||||
props: {
|
||||
scope: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
printObj: {
|
||||
id: this.scope,
|
||||
previewTitle: "Report Print",
|
||||
popTitle: "Report Print Pop"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
async generatePdf() {
|
||||
html2pdf(document.getElementById(this.scope))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.button-container {
|
||||
margin-top:1rem;
|
||||
}
|
||||
</style>
|
32
src/components/ReportLayout.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<PrintButtons :scope="scope" />
|
||||
<v-container>
|
||||
<v-card class="a4 page">
|
||||
<div :id="scope" class="pdf-scope">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
import PrintButtons from './PrintButtons.vue'
|
||||
export default {
|
||||
props: {
|
||||
scope: String
|
||||
},
|
||||
components: {
|
||||
PrintButtons
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@media print {
|
||||
.pdf-scope {
|
||||
padding:0;
|
||||
}
|
||||
}
|
||||
</style>
|
18
src/components/SearchBox.vue
Normal file
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<v-responsive
|
||||
class="mx-8"
|
||||
max-width="500"
|
||||
>
|
||||
<v-text-field
|
||||
clearable
|
||||
label="Search"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
append-inner-icon="mdi-magnify"></v-text-field>
|
||||
</v-responsive>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'SearchBox',
|
||||
}
|
||||
</script>
|
7
src/components/UserInfo.vue
Normal file
|
@ -0,0 +1,7 @@
|
|||
<script>
|
||||
import axios from 'axios';
|
||||
export default {
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
45
src/main.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import vuetify from './plugins/vuetify'
|
||||
import { loadFonts } from './plugins/webfontloader'
|
||||
import router from './router'
|
||||
import print from 'vue3-print-nb'
|
||||
import Datepicker from '@vuepic/vue-datepicker'
|
||||
import '@vuepic/vue-datepicker/dist/main.css'
|
||||
import './assets/css/app.scss'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
import axios from 'axios'
|
||||
import VueVirtualScroller from 'vue-virtual-scroller'
|
||||
|
||||
axios.defaults.headers.common['X-Authentication'] = `Bearer ${localStorage.getItem('access_token')}`;
|
||||
|
||||
loadFonts()
|
||||
|
||||
const app = createApp(App).use(router)
|
||||
.use(vuetify)
|
||||
.use(print)
|
||||
.use(VueVirtualScroller)
|
||||
.component('DatePicker', Datepicker)
|
||||
|
||||
var url = window.location.protocol + "//" + window.location.host + "/api/v1"
|
||||
|
||||
let site_info
|
||||
|
||||
console.log("URL :" + url)
|
||||
axios.get(url + "/info")
|
||||
.then(resp => {
|
||||
console.log("URL :" + url)
|
||||
site_info = resp.data
|
||||
app.config.globalProperties.$api_url = url
|
||||
app.config.globalProperties.$site_info = site_info
|
||||
app.mount('#app')
|
||||
})
|
||||
.catch(err => {
|
||||
console.log("Error : " + err)
|
||||
url = "http://localhost:3000/api/v1"
|
||||
console.log("Trying to use URL : " + url)
|
||||
app.config.globalProperties.$api_url = url
|
||||
app.config.globalProperties.$site_info = site_info
|
||||
app.mount('#app')
|
||||
})
|
||||
|
10
src/plugins/vuetify.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Styles
|
||||
import '@mdi/font/css/materialdesignicons.css'
|
||||
import 'vuetify/styles'
|
||||
|
||||
// Vuetify
|
||||
import { createVuetify } from 'vuetify'
|
||||
|
||||
export default createVuetify(
|
||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||
)
|
15
src/plugins/webfontloader.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* plugins/webfontloader.js
|
||||
*
|
||||
* webfontloader documentation: https://github.com/typekit/webfontloader
|
||||
*/
|
||||
|
||||
export async function loadFonts () {
|
||||
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader')
|
||||
|
||||
webFontLoader.load({
|
||||
google: {
|
||||
families: ['Roboto:100,300,400,500,700,900&display=swap'],
|
||||
},
|
||||
})
|
||||
}
|
65
src/router/index.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
const AboutView = () => import('../views/AboutView.vue')
|
||||
const LoginPage = () => import('../views/LoginPage.vue')
|
||||
const LogOut = () => import('../components/LogOut.vue')
|
||||
const CustomerList = () => import('../views/customers/CustomerList.vue')
|
||||
const ContractList = () => import('../views/contracts/ContractList.vue')
|
||||
const ComplaintsList = () => import('../views/complaints/ComplaintsList.vue')
|
||||
const MedFeedsList = () => import('../views/medfeeds/MedFeedsList.vue')
|
||||
const SOPPrintedList = () => import('../views/salesorders/SOPPrinted.vue')
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
component: AboutView
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: LoginPage
|
||||
},
|
||||
{
|
||||
path: '/logout',
|
||||
name: 'logout',
|
||||
component: LogOut
|
||||
},
|
||||
{
|
||||
path: '/customers/list',
|
||||
name: 'customerlist',
|
||||
component: CustomerList
|
||||
},
|
||||
{
|
||||
path: '/customers/contracts/list',
|
||||
name: 'contractlist',
|
||||
component: ContractList
|
||||
},
|
||||
{
|
||||
path: '/customers/medicated-feeds/list',
|
||||
name: 'medfeedslist',
|
||||
component: MedFeedsList
|
||||
},
|
||||
{
|
||||
path: '/customers/complaints/list',
|
||||
name: 'complaintslist',
|
||||
component: ComplaintsList
|
||||
},
|
||||
{
|
||||
path: '/sop/printed',
|
||||
name: 'sopprinted',
|
||||
component: SOPPrintedList
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
6
src/views/AboutView.vue
Normal file
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>About</h1>
|
||||
<p></p>
|
||||
</div>
|
||||
</template>
|
15
src/views/HomeView.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<div class="home">
|
||||
Dashboard
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
|
||||
export default {
|
||||
name: 'HomeView',
|
||||
components: {
|
||||
}
|
||||
}
|
||||
</script>
|
57
src/views/LoginPage.vue
Normal file
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<v-card width="500" height="300"
|
||||
class="mx-auto my-12"
|
||||
title="Welcome!"
|
||||
subtitle="Please Log In">
|
||||
<v-form>
|
||||
<v-responsive class="mx-auto" max-width="475">
|
||||
<v-text-field label="Email" clearable v-model="user.email"></v-text-field>
|
||||
<v-text-field label="Password" v-model="user.password" clearable
|
||||
type="password"></v-text-field>
|
||||
<v-btn color="blue" @click="submitLogin">Login</v-btn>
|
||||
</v-responsive>
|
||||
</v-form>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'LoginPage',
|
||||
data() {
|
||||
return {
|
||||
user: {
|
||||
email: "",
|
||||
password: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitLogin() {
|
||||
let url = this.$api_url + "/users/login"
|
||||
console.log("Logging in...")
|
||||
axios
|
||||
.post(url, {
|
||||
email: this.user.email,
|
||||
password: this.user.password
|
||||
})
|
||||
.then(resp => {
|
||||
let data = resp.data
|
||||
if (data.logged_in) {
|
||||
let token = data.token
|
||||
localStorage.setItem('access_token', token.content)
|
||||
let tok = localStorage.getItem('access_token')
|
||||
if (tok.content != "") {
|
||||
this.user.logged_in = true
|
||||
console.log("Logged in")
|
||||
window.location.href = '/'
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => { console.log(error) })
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
208
src/views/complaints/ComplaintsList.vue
Normal file
|
@ -0,0 +1,208 @@
|
|||
<template>
|
||||
<h3>Complaints</h3>
|
||||
<v-tabs v-model="tab" fixed-tabs>
|
||||
<v-tab title="List" v-model="list" />
|
||||
<v-tab title="Edit" v-model="edit" v-if="edit" />
|
||||
<v-tab title="Report" v-model="report" v-if="report" />
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item v-model="list">
|
||||
<v-responsive
|
||||
max-width="500"
|
||||
>
|
||||
<v-text-field
|
||||
clearable
|
||||
label="Search"
|
||||
variant="outlined"
|
||||
v-model="searchQuery"
|
||||
density="compact"
|
||||
append-inner-icon="mdi-magnify"></v-text-field>
|
||||
<v-switch color="blue" label="Show Only Active" v-model="showActive"></v-switch>
|
||||
<v-btn v-if="site_info.features.addcomplaint" color="warning">+ Add</v-btn>
|
||||
</v-responsive>
|
||||
<v-table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Comp No</th>
|
||||
<th>Date</th>
|
||||
<th>Order</th>
|
||||
<th>Acc No</th>
|
||||
<th>Name</th>
|
||||
<th>Reason</th>
|
||||
<th>Active</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="loading">
|
||||
<td colspan=7>
|
||||
<img class="loading" src="/images/icons/loading.gif"/>
|
||||
</td>
|
||||
</tr>
|
||||
<template v-for="complaint in filteredComplaints" :key="complaint.id">
|
||||
<tr :class="{ at_risk : complaint.at_risk, cust_at_risk: complaint.customer.at_risk }">
|
||||
<td>{{ complaint.id }}</td>
|
||||
<td>{{ complaint.complaint_date }}</td>
|
||||
<td>{{ complaint.sop.doc_no }}</td>
|
||||
<td>{{ complaint.customer.acc_no }}</td>
|
||||
<td>{{ complaint.customer.name }}
|
||||
</td>
|
||||
<td>{{ complaint.reason }}</td>
|
||||
<td>
|
||||
<img class="icon" v-if="complaint.active" v-bind:alt="complaint.active" v-bind:title="complaint.active" src="/images/icons/Live.png"/>
|
||||
</td>
|
||||
<td>
|
||||
<v-btn @click="showInfo(complaint)">
|
||||
<span v-if="complaint.info_shown">
|
||||
Hide
|
||||
</span>
|
||||
<span v-else>
|
||||
View
|
||||
</span>
|
||||
</v-btn>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="complaint.info_shown">
|
||||
<template v-if="complaint.info">
|
||||
<template v-if="complaint.info_loaded">
|
||||
<td colspan=4>
|
||||
{{ complaint.info.comments }}
|
||||
<br />
|
||||
<sub>- {{ complaint.info.added_by }}</sub>
|
||||
</td>
|
||||
<td colspan=2>
|
||||
<v-container>
|
||||
<v-form :disabled="complaint.info.loading">
|
||||
<v-row dense nogutters>
|
||||
<v-checkbox label="1" type="checkbox" v-model="complaint.info.one" @change="changeComplaintCheckbox(complaint)" />
|
||||
<v-checkbox label="2" v-model="complaint.info.two" @change="changeComplaintCheckbox(complaint)"/>
|
||||
<v-checkbox label="3" v-model="complaint.info.three" @change="changeComplaintCheckbox(complaint)"/>
|
||||
</v-row>
|
||||
<v-row dense nogutters>
|
||||
<v-checkbox label="At Risk" v-model="complaint.at_risk" @change="changeComplaintCheckbox(complaint)" />
|
||||
<v-checkbox label="Permanent" v-model="complaint.info.permanent" @change="changeComplaintCheckbox(complaint)" />
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-container>
|
||||
</td>
|
||||
<td>
|
||||
<img v-if="complaint.info.loading" class="loading" src="/images/icons/loading.gif"/>
|
||||
<template v-else>
|
||||
<v-btn v-if="site_info.features.editcomplaint" color="warning">Edit</v-btn>
|
||||
</template>
|
||||
</td>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td colspan=6>
|
||||
<img class="loading" src="/images/icons/loading.gif"/>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
export default {
|
||||
props: {
|
||||
site_info:{},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: "list",
|
||||
list: [],
|
||||
listreceived: false,
|
||||
showActive: true,
|
||||
loading: true,
|
||||
limit: 300,
|
||||
searchQuery: "",
|
||||
edit: false,
|
||||
report: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredComplaints() {
|
||||
let query = this.searchQuery.toLowerCase()
|
||||
if (!this.listreceived){
|
||||
this.getComplaintsList()
|
||||
}
|
||||
let clist = this.list.filter(q =>
|
||||
q.customer.name.toLowerCase().includes(query) ||
|
||||
q.customer.acc_no.includes(query) ||
|
||||
q.id == query ||
|
||||
q.reason.toLowerCase().includes(query)
|
||||
)
|
||||
if (this.showActive) {
|
||||
clist = clist.filter(q =>
|
||||
q.active == true
|
||||
)
|
||||
}
|
||||
return clist
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getComplaintsList(){
|
||||
this.loading = true
|
||||
let url = this.$api_url + "/customers/complaints/list"
|
||||
console.log("Getting Complaint list...")
|
||||
axios.get(url,{
|
||||
params: {
|
||||
limit: this.limit,
|
||||
query: this.searchQuery
|
||||
}
|
||||
}).then(resp => {
|
||||
this.list = resp.data
|
||||
this.loading = false
|
||||
this.listreceived = true
|
||||
})
|
||||
},
|
||||
async getComplaintInfo(complaint) {
|
||||
complaint.info.loading = true
|
||||
let url = this.$api_url + "/customers/complaints/" + complaint.id + "/info"
|
||||
console.log("Getting Complaint Info...")
|
||||
axios.get(url)
|
||||
.then(resp =>{
|
||||
complaint.info = resp.data
|
||||
complaint.info.loading = false
|
||||
})
|
||||
},
|
||||
async showInfo(complaint) {
|
||||
console.log(complaint.id)
|
||||
complaint.info_loaded = false
|
||||
complaint.info_shown = !complaint.info_shown
|
||||
if (complaint.info_shown) {
|
||||
complaint.info = await this.getComplaintInfo(complaint)
|
||||
complaint.info_loaded = true
|
||||
}
|
||||
},
|
||||
async changeComplaintCheckbox(complaint) {
|
||||
let set = await this.setComplaintStatus(complaint)
|
||||
if (complaint.at_risk && set && complaint.info.one && complaint.info.two && complaint.info.three) {
|
||||
console.log("Contract no longer at risk")
|
||||
complaint.at_risk = false
|
||||
}
|
||||
},
|
||||
async setComplaintStatus(complaint) {
|
||||
complaint.info.loading = true
|
||||
let url = this.$api_url + "/customers/complaints/" + complaint.id + "/info/set"
|
||||
console.log("Getting Complaint Info...")
|
||||
axios.post(url, {
|
||||
one: complaint.info.one,
|
||||
two: complaint.info.two,
|
||||
three: complaint.info.three,
|
||||
at_risk: complaint.at_risk,
|
||||
permanent: complaint.info.permanent
|
||||
}).then(resp => {
|
||||
console.log(resp.data)
|
||||
this.getComplaintInfo(complaint)
|
||||
complaint.info_loaded = true
|
||||
})
|
||||
return true
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
162
src/views/contracts/ContractEdit.vue
Normal file
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<v-card title="Edit Contract" :subtitle="'Contract : ' + contract.no">
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-text-field label="Customer" v-model="contract.customer.name" readonly></v-text-field>
|
||||
<v-text-field type="number" label="Tonnage Per Month" v-model="contract.tonnage_per_month"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<label>
|
||||
Start Date
|
||||
<DatePicker v-model="contract.start_date" format="dd/MM/yyyy" />
|
||||
</label><br />
|
||||
<label>
|
||||
Finish Date
|
||||
<DatePicker v-model="contract.finish_date" format="dd/MM/yyyy" />
|
||||
</label><br />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<template v-for="p in contract.products" :key="p.code">
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-autocomplete v-model="p.code"
|
||||
v-model:search="product_search"
|
||||
:loading="products_loading"
|
||||
:items="products"
|
||||
cache-items
|
||||
hide-no-data
|
||||
hide-details
|
||||
solo-inverted
|
||||
label="Code"
|
||||
no-data-text="No Products Found"
|
||||
item-title="code"
|
||||
item-value="code" ></v-autocomplete>
|
||||
</v-col>
|
||||
<v-col cols="4">
|
||||
<v-text-field type="number" label="Price" v-model="p.price"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="2">
|
||||
<v-btn size="small" color="error" title="Remove" variant="plain" icon @click="contract.products.pop()"><v-icon>mdi-minus</v-icon></v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
<v-row>
|
||||
<v-col cols="4">
|
||||
<v-btn @click="contract.products.push({})" v-if="contract.products.length < 4">+ Add Product</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<label>
|
||||
Agree Date
|
||||
<DatePicker v-model="contract.agree_date" format="dd/MM/yyyy" />
|
||||
</label>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-textarea rows=3 label="Comments" v-model="contract.comments"></v-textarea>
|
||||
<v-textarea rows=3 label="Office Comments" v-model="contract.office_comments"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="red-darken-1"
|
||||
variant="text"
|
||||
@click="saveContract(selected_contract)">Save</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="blue-darken-1"
|
||||
variant="text"
|
||||
@click="close">Close</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import DatePicker from '@vuepic/vue-datepicker'
|
||||
export default {
|
||||
props: {
|
||||
setcontract: {
|
||||
no: Number,
|
||||
customer: {
|
||||
acc_no: String,
|
||||
name: String,
|
||||
at_risk: Boolean,
|
||||
},
|
||||
products: [{
|
||||
code: String,
|
||||
name: String,
|
||||
price: Number,
|
||||
}],
|
||||
start_date: String,
|
||||
finish_date: String,
|
||||
agree_date: String,
|
||||
tonnage_per_month: Number,
|
||||
comments: String,
|
||||
office_comments: String,
|
||||
active: Boolean
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DatePicker
|
||||
},
|
||||
watch: {
|
||||
setcontract(newval) {
|
||||
this.contract = newval
|
||||
},
|
||||
product_search(val) {
|
||||
if (val && val.length > 1) {
|
||||
this.searchProducts(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
contract: this.setcontract,
|
||||
dialog: this.opendialog,
|
||||
saving: false,
|
||||
product_search: null,
|
||||
products_loading: false,
|
||||
products: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit('closetab','list')
|
||||
},
|
||||
async saveContract(){
|
||||
this.saving = true
|
||||
let url = this.$api_url + "/customers/contracts/" + this.contract.no + "/save"
|
||||
console.log("Saving Contract : ", this.contract.no)
|
||||
console.log(this.contract)
|
||||
axios.post(url, {
|
||||
contract: this.contract
|
||||
}).then(resp => {
|
||||
console.log("Saved Contract : " + JSON.stringify(resp.data))
|
||||
this.saving = false
|
||||
this.$emit('contractupdate', resp.data)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.saving = false
|
||||
})
|
||||
},
|
||||
searchProducts(code) {
|
||||
let url = this.$api_url + "/products/search/" + code
|
||||
console.log(url)
|
||||
axios.get(url)
|
||||
.then(resp => {
|
||||
console.log(resp)
|
||||
this.products = resp.data
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
this.products = [{code:"NoProductsFound", name:"No Products Found"}]
|
||||
})
|
||||
},
|
||||
productCodeName(p) {
|
||||
return p.code + ' - ' + p.name
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|