mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-10 04:25:08 +00:00
Merge branch 'master' of https://github.com/withinpixels/fuse2
This commit is contained in:
commit
786180958d
918
package-lock.json
generated
918
package-lock.json
generated
|
@ -1299,7 +1299,6 @@
|
|||
"requires": {
|
||||
"anymatch": "1.3.0",
|
||||
"async-each": "1.0.1",
|
||||
"fsevents": "1.1.2",
|
||||
"glob-parent": "2.0.0",
|
||||
"inherits": "2.0.3",
|
||||
"is-binary-path": "1.0.1",
|
||||
|
@ -3443,905 +3442,6 @@
|
|||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
|
||||
"integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "2.6.2",
|
||||
"node-pre-gyp": "0.6.36"
|
||||
},
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "4.11.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"json-stable-stringify": "1.0.1"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegates": "1.0.0",
|
||||
"readable-stream": "2.2.9"
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "0.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.6.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.6.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "0.4.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tweetnacl": "0.14.5"
|
||||
}
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.9",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"boom": {
|
||||
"version": "2.10.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"hoek": "2.16.3"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.7",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "0.4.2",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"buffer-shims": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"boom": "2.10.1"
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.1.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"combined-stream": "1.0.5",
|
||||
"mime-types": "2.1.15"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"inherits": "2.0.3",
|
||||
"mkdirp": "0.5.1",
|
||||
"rimraf": "2.6.1"
|
||||
}
|
||||
},
|
||||
"fstream-ignore": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"fstream": "1.0.11",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aproba": "1.1.1",
|
||||
"console-control-strings": "1.1.0",
|
||||
"has-unicode": "2.0.1",
|
||||
"object-assign": "4.1.1",
|
||||
"signal-exit": "3.0.2",
|
||||
"string-width": "1.0.2",
|
||||
"strip-ansi": "3.0.1",
|
||||
"wide-align": "1.1.2"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "4.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ajv": "4.11.8",
|
||||
"har-schema": "1.0.5"
|
||||
}
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"boom": "2.10.1",
|
||||
"cryptiles": "2.0.5",
|
||||
"hoek": "2.16.3",
|
||||
"sntp": "1.0.9"
|
||||
}
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "0.2.0",
|
||||
"jsprim": "1.4.0",
|
||||
"sshpk": "1.13.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"json-stable-stringify": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsonify": "0.0.0"
|
||||
}
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"jsonify": {
|
||||
"version": "0.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.0.2",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.3.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.27.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.15",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.27.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.6.36",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"mkdirp": "0.5.1",
|
||||
"nopt": "4.0.1",
|
||||
"npmlog": "4.1.0",
|
||||
"rc": "1.2.1",
|
||||
"request": "2.81.0",
|
||||
"rimraf": "2.6.1",
|
||||
"semver": "5.3.0",
|
||||
"tar": "2.2.1",
|
||||
"tar-pack": "3.4.0"
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"abbrev": "1.1.0",
|
||||
"osenv": "0.1.4"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "1.1.4",
|
||||
"console-control-strings": "1.1.0",
|
||||
"gauge": "2.7.4",
|
||||
"set-blocking": "2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"os-homedir": "1.0.2",
|
||||
"os-tmpdir": "1.0.2"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "0.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"deep-extend": "0.4.2",
|
||||
"ini": "1.3.4",
|
||||
"minimist": "1.2.0",
|
||||
"strip-json-comments": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.2.9",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-shims": "1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "1.0.7",
|
||||
"string_decoder": "1.0.1",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.81.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aws-sign2": "0.6.0",
|
||||
"aws4": "1.6.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.5",
|
||||
"extend": "3.0.1",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.1.4",
|
||||
"har-validator": "4.2.1",
|
||||
"hawk": "3.1.3",
|
||||
"http-signature": "1.1.1",
|
||||
"is-typedarray": "1.0.0",
|
||||
"isstream": "0.1.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"mime-types": "2.1.15",
|
||||
"oauth-sign": "0.8.2",
|
||||
"performance-now": "0.2.0",
|
||||
"qs": "6.4.0",
|
||||
"safe-buffer": "5.0.1",
|
||||
"stringstream": "0.0.5",
|
||||
"tough-cookie": "2.3.2",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"uuid": "3.0.1"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "7.1.2"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.3.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"sntp": {
|
||||
"version": "1.0.9",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"hoek": "2.16.3"
|
||||
}
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.13.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"asn1": "0.2.3",
|
||||
"assert-plus": "1.0.0",
|
||||
"bcrypt-pbkdf": "1.0.1",
|
||||
"dashdash": "1.14.1",
|
||||
"ecc-jsbn": "0.1.1",
|
||||
"getpass": "0.1.7",
|
||||
"jodid25519": "1.0.2",
|
||||
"jsbn": "0.1.1",
|
||||
"tweetnacl": "0.14.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"code-point-at": "1.1.0",
|
||||
"is-fullwidth-code-point": "1.0.0",
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"block-stream": "0.0.9",
|
||||
"fstream": "1.0.11",
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"tar-pack": {
|
||||
"version": "3.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"debug": "2.6.8",
|
||||
"fstream": "1.0.11",
|
||||
"fstream-ignore": "1.0.5",
|
||||
"once": "1.4.0",
|
||||
"readable-stream": "2.2.9",
|
||||
"rimraf": "2.6.1",
|
||||
"tar": "2.2.1",
|
||||
"uid-number": "0.0.6"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.3.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"punycode": "1.4.1"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.3.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"extsprintf": "1.0.2"
|
||||
}
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"string-width": "1.0.2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
|
||||
|
@ -8940,6 +8040,15 @@
|
|||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
||||
"dev": true
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
|
@ -8967,15 +8076,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
|
||||
|
|
|
@ -36,6 +36,10 @@ const appRoutes: Routes = [
|
|||
path : 'apps/calendar',
|
||||
loadChildren: './main/content/apps/calendar/calendar.module#FuseCalendarModule'
|
||||
},
|
||||
{
|
||||
path : 'apps/e-commerce',
|
||||
loadChildren: './main/content/apps/e-commerce/e-commerce.module#FuseEcommerceModule'
|
||||
},
|
||||
{
|
||||
path : 'apps/todo',
|
||||
loadChildren: './main/content/apps/todo/todo.module#FuseTodoModule'
|
||||
|
|
|
@ -82,10 +82,12 @@ export class FuseUtils
|
|||
{
|
||||
function S4()
|
||||
{
|
||||
return (((1 + Math.random()) * 0x10000) || 0).toString(16).substring(1);
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
return (S4() + S4());
|
||||
return S4() + S4();
|
||||
}
|
||||
|
||||
public static toggleInArray(item, array)
|
||||
|
@ -99,4 +101,14 @@ export class FuseUtils
|
|||
array.splice(array.indexOf(item), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static handleize(text)
|
||||
{
|
||||
return text.toString().toLowerCase()
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||
.replace(/^-+/, '') // Trim - from start of text
|
||||
.replace(/-+$/, ''); // Trim - from end of text
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,6 +187,11 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
|
|||
// Generate material element colors
|
||||
// based on current contrast color
|
||||
@include generateMaterialElementColors($contrastColor);
|
||||
|
||||
&[disabled] {
|
||||
background-color: rgba($color, .12) !important;
|
||||
color: rgba($contrastColor, .26) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$colorName}#{$hue}-fg {
|
||||
|
|
|
@ -96,7 +96,7 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
|
|||
max-height: $carded-toolbar-height;
|
||||
}
|
||||
|
||||
.content {
|
||||
> .content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
|
3962
src/app/fuse-fake-db/e-commerce.ts
Normal file
3962
src/app/fuse-fake-db/e-commerce.ts
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,7 @@ import { ProjectsDashboardDb } from './projects-dashboard';
|
|||
import { ScrumboardFakeDb } from './scrumboard';
|
||||
import { FaqFakeDb } from './faq';
|
||||
import { KnowledgeBaseFakeDb } from './knowledge-base';
|
||||
import { ECommerceFakeDb } from './e-commerce';
|
||||
|
||||
export class FuseFakeDbService implements InMemoryDbService
|
||||
{
|
||||
|
@ -48,7 +49,10 @@ export class FuseFakeDbService implements InMemoryDbService
|
|||
'projects-dashboard-widgets' : ProjectsDashboardDb.widgets,
|
||||
'scrumboard-boards' : ScrumboardFakeDb.boards,
|
||||
'faq' : FaqFakeDb.data,
|
||||
'knowledge-base' : KnowledgeBaseFakeDb.data
|
||||
'knowledge-base' : KnowledgeBaseFakeDb.data,
|
||||
'e-commerce-dashboard' : ECommerceFakeDb.dashboard,
|
||||
'e-commerce-products' : ECommerceFakeDb.products,
|
||||
'e-commerce-orders' : ECommerceFakeDb.orders
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
<div id="e-commerce-dashboard" class="page-layout simple fullwidth">
|
||||
|
||||
<!-- CONTENT -->
|
||||
<div class="content p-24 w-100-p">
|
||||
|
||||
<!-- WIDGET GROUP -->
|
||||
<div class="widget-group" fxLayout="row" fxFlex="100" fxLayoutWrap fxLayoutAlign="start start" *fuseIfOnDom [@animateStagger]="{value:'50'}">
|
||||
|
||||
<!-- WIDGET 1 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<mat-form-field>
|
||||
<mat-select class="simplified font-size-16" [(ngModel)]="widgets.widget1.currentRange"
|
||||
aria-label="Change range">
|
||||
<mat-option *ngFor="let range of widgets.widget1.ranges | keys"
|
||||
[value]="range.key">
|
||||
{{range.value}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button mat-icon-button fuseWidgetToggle aria-label="more">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="light-blue-fg font-size-72 line-height-72">
|
||||
{{widgets.widget1.data.count[widgets.widget1.currentRange]}}
|
||||
</div>
|
||||
<div class="h3 secondary-text font-weight-500">{{widgets.widget1.data.label}}</div>
|
||||
</div>
|
||||
|
||||
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
|
||||
<span class="h4 secondary-text text-truncate">{{widgets.widget1.data.extra.label}}:</span>
|
||||
<span class="h4 ml-8">{{widgets.widget1.data.extra.count[widgets.widget1.currentRange]}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
<!-- Back -->
|
||||
<div class="fuse-widget-back p-16 pt-32 mat-white-bg mat-elevation-z2">
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button"
|
||||
aria-label="Flip widget">
|
||||
<mat-icon class="s-16">close</mat-icon>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
{{widgets.widget1.detail}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Back -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 1 -->
|
||||
|
||||
<!-- WIDGET 2 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div class="h3">{{widgets.widget2.title}}</div>
|
||||
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button" aria-label="more">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="red-fg font-size-72 line-height-72">
|
||||
{{widgets.widget2.data.count}}
|
||||
</div>
|
||||
<div class="h3 secondary-text font-weight-500">{{widgets.widget2.data.label}}</div>
|
||||
</div>
|
||||
|
||||
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
|
||||
<span class="h4 secondary-text text-truncate">{{widgets.widget2.data.extra.label}}:</span>
|
||||
<span class="h4 ml-8">{{widgets.widget2.data.extra.count}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
<!-- Back -->
|
||||
<div class="fuse-widget-back p-16 pt-32 mat-white-bg mat-elevation-z2">
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button"
|
||||
aria-label="Flip widget">
|
||||
<mat-icon class="s-16">close</mat-icon>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
{{widgets.widget2.detail}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Back -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 2 -->
|
||||
|
||||
<!-- WIDGET 3 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div class="h3">{{widgets.widget3.title}}</div>
|
||||
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button" aria-label="more">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="orange-fg font-size-72 line-height-72">
|
||||
{{widgets.widget3.data.count}}
|
||||
</div>
|
||||
<div class="h3 secondary-text font-weight-500">{{widgets.widget3.data.label}}</div>
|
||||
</div>
|
||||
|
||||
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
|
||||
<span class="h4 secondary-text text-truncate">{{widgets.widget3.data.extra.label}}:</span>
|
||||
<span class="h4 ml-8">{{widgets.widget3.data.extra.count}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
<!-- Back -->
|
||||
<div class="fuse-widget-back p-16 pt-32 mat-white-bg mat-elevation-z2">
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button"
|
||||
aria-label="Flip widget">
|
||||
<mat-icon class="s-16">close</mat-icon>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
{{widgets.widget3.detail}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Back -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 3 -->
|
||||
|
||||
<!-- WIDGET 4 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div class="h3">{{widgets.widget4.title}}</div>
|
||||
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button" aria-label="more">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="blue-grey-fg font-size-72 line-height-72">{{widgets.widget4.data.count}}
|
||||
</div>
|
||||
<div class="h3 secondary-text font-weight-500">{{widgets.widget4.data.label}}</div>
|
||||
</div>
|
||||
|
||||
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
|
||||
<span class="h4 secondary-text text-truncate">{{widgets.widget4.data.extra.label}}:</span>
|
||||
<span class="h4 ml-8">{{widgets.widget4.data.extra.count}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
<!-- Back -->
|
||||
<div class="fuse-widget-back p-16 pt-32 mat-white-bg mat-elevation-z2">
|
||||
<button mat-icon-button fuseWidgetToggle class="fuse-widget-flip-button"
|
||||
aria-label="Flip widget">
|
||||
<mat-icon class="s-16">close</mat-icon>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
{{widgets.widget4.detail}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Back -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 4 -->
|
||||
|
||||
|
||||
<!-- WIDGET 5 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="row" fxFlex="100">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
|
||||
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center" fxLayoutWrap>
|
||||
|
||||
<div fxFlex class="py-8 h3">{{widgets.widget5.title}}</div>
|
||||
|
||||
<div fxFlex="0 1 auto" class="py-8" fxLayout="row">
|
||||
<button mat-button class="px-16"
|
||||
*ngFor="let range of widgets.widget5.ranges | keys"
|
||||
(click)="widget5.currentRange = range.key"
|
||||
[disabled]="widget5.currentRange == range.key">
|
||||
{{range.value}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-420">
|
||||
<ngx-charts-bar-vertical-stacked
|
||||
*fuseIfOnDom
|
||||
[scheme]="widget5.scheme"
|
||||
[results]="this.widgets.widget5.mainChart[this.widget5.currentRange]"
|
||||
[gradient]="widget5.gradient"
|
||||
[xAxis]="widget5.xAxis"
|
||||
[yAxis]="widget5.yAxis"
|
||||
[legend]="widget5.legend"
|
||||
[showXAxisLabel]="widget5.showXAxisLabel"
|
||||
[showYAxisLabel]="widget5.showYAxisLabel"
|
||||
[xAxisLabel]="widget5.xAxisLabel"
|
||||
[yAxisLabel]="widget5.yAxisLabel"
|
||||
(select)="widget5.onSelect($event)">
|
||||
</ngx-charts-bar-vertical-stacked>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 5 -->
|
||||
|
||||
<!-- WIDGET 6 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column" fxFlex="100" fxFlex.gt-sm="50">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
|
||||
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div class="h3">{{widgets.widget6.title}}</div>
|
||||
<mat-form-field>
|
||||
<mat-select class="simplified" [(ngModel)]="widget6.currentRange" aria-label="Change range">
|
||||
<mat-option *ngFor="let range of widgets.widget6.ranges | keys"
|
||||
[value]="range.key">
|
||||
{{range.value}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="h-400">
|
||||
<ngx-charts-pie-chart
|
||||
*fuseIfOnDom
|
||||
[scheme]="widget6.scheme"
|
||||
[results]="widgets.widget6.mainChart[widget6.currentRange]"
|
||||
[legend]="widget6.showLegend"
|
||||
[explodeSlices]="widget6.explodeSlices"
|
||||
[labels]="widget6.labels"
|
||||
[doughnut]="widget6.doughnut"
|
||||
[gradient]="widget6.gradient"
|
||||
(select)="widget6.onSelect($event)">
|
||||
</ngx-charts-pie-chart>
|
||||
</div>
|
||||
|
||||
<div class="py-8 mh-16 border-top" fxLayout="row" fxLayoutAlign="start center" fxLayoutWrap>
|
||||
<div class="py-8 border-right" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" fxFlex.gt-sm="50">
|
||||
<span class="mat-display-1 mb-0">{{widgets.widget6.footerLeft.count[widget6.currentRange]}}</span>
|
||||
<span class="h4">{{widgets.widget6.footerLeft.title}}</span>
|
||||
</div>
|
||||
|
||||
<div class="py-8" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" fxFlex.gt-sm="50">
|
||||
<span class="mat-display-1 mb-0">{{widgets.widget6.footerRight.count[widget6.currentRange]}}</span>
|
||||
<span class="h4">{{widgets.widget6.footerRight.title}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 6 -->
|
||||
|
||||
<!-- WIDGET 7 -->
|
||||
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column" fxFlex="100" fxFlex.gt-sm="50">
|
||||
|
||||
<!-- Front -->
|
||||
<div class="fuse-widget-front mat-white-bg mat-elevation-z2">
|
||||
|
||||
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div class="h3">{{widgets.widget7.title}}</div>
|
||||
<mat-form-field>
|
||||
<mat-select class="simplified" [(ngModel)]="widget7.currentRange"
|
||||
aria-label="Change range">
|
||||
<mat-option *ngFor="let range of widgets.widget7.ranges | keys"
|
||||
[value]="range.key">
|
||||
{{range.value}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="p-16" fxLayout="row" fxLayoutAlign="space-between center"
|
||||
*ngFor="let customer of widgets.widget7.customers[widget7.currentRange]">
|
||||
<div>
|
||||
<div class="h3">{{customer.name}}</div>
|
||||
<div>
|
||||
<span *ngIf="customer.location">{{customer.location}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button mat-icon-button aria-label="More information">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- / Front -->
|
||||
|
||||
</fuse-widget>
|
||||
<!-- / WIDGET 7 -->
|
||||
|
||||
</div>
|
||||
<!-- / WIDGET GROUP -->
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT -->
|
||||
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
#e-commerce-dashboard {
|
||||
|
||||
.content {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { EcommerceDashboardService } from './dashboard.service';
|
||||
import * as shape from 'd3-shape';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-e-commerce-dashboard',
|
||||
templateUrl : './dashboard.component.html',
|
||||
styleUrls : ['./dashboard.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
animations : fuseAnimations
|
||||
})
|
||||
export class FuseEcommerceDashboardComponent implements OnInit, OnDestroy
|
||||
{
|
||||
projects: any[];
|
||||
selectedProject: any;
|
||||
|
||||
widgets: any;
|
||||
widget5: any = {};
|
||||
widget6: any = {};
|
||||
widget7: any = {};
|
||||
|
||||
dateNow = Date.now();
|
||||
|
||||
constructor(private projectsDashboardService: EcommerceDashboardService)
|
||||
{
|
||||
this.projects = this.projectsDashboardService.projects;
|
||||
|
||||
this.selectedProject = this.projects[0];
|
||||
|
||||
this.widgets = this.projectsDashboardService.widgets;
|
||||
|
||||
/**
|
||||
* Widget 5
|
||||
*/
|
||||
this.widget5 = {
|
||||
currentRange : 'TW',
|
||||
xAxis : true,
|
||||
yAxis : true,
|
||||
gradient : false,
|
||||
legend : false,
|
||||
showXAxisLabel: false,
|
||||
xAxisLabel : 'Days',
|
||||
showYAxisLabel: false,
|
||||
yAxisLabel : 'Isues',
|
||||
scheme : {
|
||||
domain: ['#42BFF7', '#C6ECFD', '#C7B42C', '#AAAAAA']
|
||||
},
|
||||
onSelect : (ev) => {
|
||||
console.log(ev);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Widget 6
|
||||
*/
|
||||
this.widget6 = {
|
||||
currentRange : 'TW',
|
||||
legend : false,
|
||||
explodeSlices: false,
|
||||
labels : true,
|
||||
doughnut : true,
|
||||
gradient : false,
|
||||
scheme : {
|
||||
domain: ['#f44336', '#9c27b0', '#03a9f4', '#e91e63']
|
||||
},
|
||||
onSelect : (ev) => {
|
||||
console.log(ev);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Widget 7
|
||||
*/
|
||||
this.widget7 = {
|
||||
currentRange: 'T'
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Injectable()
|
||||
export class EcommerceDashboardService implements Resolve<any>
|
||||
{
|
||||
projects: any[];
|
||||
widgets: any[];
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve
|
||||
* @param {ActivatedRouteSnapshot} route
|
||||
* @param {RouterStateSnapshot} state
|
||||
* @returns {Observable<any> | Promise<any> | any}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
|
||||
{
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
Promise.all([
|
||||
this.getProjects(),
|
||||
this.getWidgets()
|
||||
]).then(
|
||||
() => {
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getProjects(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/projects-dashboard-projects')
|
||||
.subscribe((response: any) => {
|
||||
this.projects = response;
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
getWidgets(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/e-commerce-dashboard')
|
||||
.subscribe((response: any) => {
|
||||
this.widgets = response;
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}
|
91
src/app/main/content/apps/e-commerce/e-commerce.module.ts
Normal file
91
src/app/main/content/apps/e-commerce/e-commerce.module.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||
import { FuseEcommerceDashboardComponent } from './dashboard/dashboard.component';
|
||||
import { EcommerceDashboardService } from './dashboard/dashboard.service';
|
||||
import { SharedModule } from '../../../../core/modules/shared.module';
|
||||
import { FuseWidgetModule } from '../../../../core/components/widget/widget.module';
|
||||
import { FuseEcommerceProductsComponent } from './products/products.component';
|
||||
import { EcommerceProductsService } from './products/products.service';
|
||||
import { FuseEcommerceProductComponent } from './product/product.component';
|
||||
import { EcommerceProductService } from './product/product.service';
|
||||
import { FuseEcommerceOrdersComponent } from './orders/orders.component';
|
||||
import { EcommerceOrdersService } from './orders/orders.service';
|
||||
import { FuseEcommerceOrderComponent } from './order/order.component';
|
||||
import { EcommerceOrderService } from './order/order.service';
|
||||
import { AgmCoreModule } from '@agm/core';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path : 'dashboard',
|
||||
component: FuseEcommerceDashboardComponent,
|
||||
resolve : {
|
||||
data: EcommerceDashboardService
|
||||
}
|
||||
},
|
||||
{
|
||||
path : 'products',
|
||||
component: FuseEcommerceProductsComponent,
|
||||
resolve : {
|
||||
data: EcommerceProductsService
|
||||
}
|
||||
},
|
||||
{
|
||||
path : 'products/:id',
|
||||
component: FuseEcommerceProductComponent,
|
||||
resolve : {
|
||||
data: EcommerceProductService
|
||||
}
|
||||
},
|
||||
{
|
||||
path : 'products/:id/:handle',
|
||||
component: FuseEcommerceProductComponent,
|
||||
resolve : {
|
||||
data: EcommerceProductService
|
||||
}
|
||||
},
|
||||
{
|
||||
path : 'orders',
|
||||
component: FuseEcommerceOrdersComponent,
|
||||
resolve : {
|
||||
data: EcommerceOrdersService
|
||||
}
|
||||
},
|
||||
{
|
||||
path : 'orders/:id',
|
||||
component: FuseEcommerceOrderComponent,
|
||||
resolve : {
|
||||
data: EcommerceOrderService
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports : [
|
||||
SharedModule,
|
||||
RouterModule.forChild(routes),
|
||||
FuseWidgetModule,
|
||||
NgxChartsModule,
|
||||
AgmCoreModule.forRoot({
|
||||
apiKey: 'AIzaSyD81ecsCj4yYpcXSLFcYU97PvRsE_X8Bx8'
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
FuseEcommerceDashboardComponent,
|
||||
FuseEcommerceProductsComponent,
|
||||
FuseEcommerceProductComponent,
|
||||
FuseEcommerceOrdersComponent,
|
||||
FuseEcommerceOrderComponent
|
||||
],
|
||||
providers : [
|
||||
EcommerceDashboardService,
|
||||
EcommerceProductsService,
|
||||
EcommerceProductService,
|
||||
EcommerceOrdersService,
|
||||
EcommerceOrderService
|
||||
]
|
||||
})
|
||||
export class FuseEcommerceModule
|
||||
{
|
||||
}
|
72
src/app/main/content/apps/e-commerce/order/order-statuses.ts
Normal file
72
src/app/main/content/apps/e-commerce/order/order-statuses.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
export const orderStatuses = [
|
||||
{
|
||||
'id' : 1,
|
||||
'name' : 'Awaiting check payment',
|
||||
'color': 'mat-blue-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 2,
|
||||
'name' : 'Payment accepted',
|
||||
'color': 'mat-green-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 3,
|
||||
'name' : 'Preparing the order',
|
||||
'color': 'mat-orange-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 4,
|
||||
'name' : 'Shipped',
|
||||
'color': 'mat-purple-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 5,
|
||||
'name' : 'Delivered',
|
||||
'color': 'mat-green-800-bg'
|
||||
},
|
||||
{
|
||||
'id' : 6,
|
||||
'name' : 'Canceled',
|
||||
'color': 'mat-pink-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 7,
|
||||
'name' : 'Refunded',
|
||||
'color': 'mat-red-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 8,
|
||||
'name' : 'Payment error',
|
||||
'color': 'mat-red-900-bg'
|
||||
},
|
||||
{
|
||||
'id' : 9,
|
||||
'name' : 'On pre-order (paid)',
|
||||
'color': 'mat-purple-300-bg'
|
||||
},
|
||||
{
|
||||
'id' : 10,
|
||||
'name' : 'Awaiting bank wire payment',
|
||||
'color': 'mat-blue-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 11,
|
||||
'name' : 'Awaiting PayPal payment',
|
||||
'color': 'mat-blue-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 12,
|
||||
'name' : 'Remote payment accepted',
|
||||
'color': 'mat-green-500-bg'
|
||||
},
|
||||
{
|
||||
'id' : 13,
|
||||
'name' : 'On pre-order (not paid)',
|
||||
'color': 'mat-purple-300-bg'
|
||||
},
|
||||
{
|
||||
'id' : 14,
|
||||
'name' : 'Awaiting Cash-on-delivery payment',
|
||||
'color': 'mat-blue-500-bg'
|
||||
}
|
||||
];
|
429
src/app/main/content/apps/e-commerce/order/order.component.html
Normal file
429
src/app/main/content/apps/e-commerce/order/order.component.html
Normal file
|
@ -0,0 +1,429 @@
|
|||
<div id="order" class="page-layout carded fullwidth" fusePerfectScrollbar>
|
||||
|
||||
<!-- TOP BACKGROUND -->
|
||||
<div class="top-bg mat-accent-bg"></div>
|
||||
<!-- / TOP BACKGROUND -->
|
||||
|
||||
<!-- CENTER -->
|
||||
<div class="center">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header white-fg"
|
||||
fxLayout="row" fxLayoutAlign="space-between center">
|
||||
|
||||
<!-- APP TITLE -->
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<button class="mr-16" mat-icon-button [routerLink]="'/apps/e-commerce/orders'">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
|
||||
<div fxLayout="column" fxLayoutAlign="start start"
|
||||
*fuseIfOnDom [@animate]="{value:'*',params:{delay:'100ms',x:'-25px'}}">
|
||||
<div class="h2">
|
||||
Order {{order.reference}}
|
||||
</div>
|
||||
<div class="subtitle secondary-text">
|
||||
<span>from</span>
|
||||
<span>{{order.customer.firstName}} {{order.customer.lastName}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / APP TITLE -->
|
||||
|
||||
</div>
|
||||
<!-- / HEADER -->
|
||||
|
||||
<!-- CONTENT CARD -->
|
||||
<div class="content-card mat-white-bg">
|
||||
|
||||
<!-- CONTENT -->
|
||||
<div class="content">
|
||||
|
||||
<mat-tab-group>
|
||||
|
||||
<mat-tab label="Order Details">
|
||||
|
||||
<div class="order-details tab-content p-24" fusePerfectScrollbar>
|
||||
|
||||
<div class="section pb-48">
|
||||
|
||||
<div class="pb-16" fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="m-0 mr-16">account_circle</mat-icon>
|
||||
<div class="h2 secondary-text">Customer</div>
|
||||
</div>
|
||||
|
||||
<div class="customer">
|
||||
<table class="simple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>Company</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<img class="avatar" [src]="order.customer.avatar">
|
||||
<span class="name text-truncate">{{order.customer.firstName}} {{order.customer.lastName}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="email text-truncate">{{order.customer.email}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="phone text-truncate">{{order.customer.phone}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="company text-truncate">{{order.customer.company}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<mat-tab-group class="addresses">
|
||||
|
||||
<mat-tab label="Shipping Address">
|
||||
<div fxFlex fxLayout="column" *fuseIfOnDom>
|
||||
<div class="address h4 py-24">{{order.customer.shippingAddress.address}}</div>
|
||||
<agm-map class="w-100-p h-400" [zoom]="15"
|
||||
[latitude]="order.customer.shippingAddress.lat"
|
||||
[longitude]="order.customer.shippingAddress.lng">
|
||||
<agm-marker [latitude]="order.customer.shippingAddress.lat"
|
||||
[longitude]="order.customer.shippingAddress.lng">
|
||||
</agm-marker>
|
||||
</agm-map>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Invoice Address" fxLayout="column">
|
||||
<div fxFlex fxLayout="column" *fuseIfOnDom>
|
||||
<div class="address h4 py-24">{{order.customer.invoiceAddress.address}}</div>
|
||||
<agm-map class="w-100-p h-400" [zoom]="15"
|
||||
[latitude]="order.customer.invoiceAddress.lat"
|
||||
[longitude]="order.customer.invoiceAddress.lng">
|
||||
<agm-marker [latitude]="order.customer.invoiceAddress.lat"
|
||||
[longitude]="order.customer.invoiceAddress.lng">
|
||||
</agm-marker>
|
||||
</agm-map>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
<div class="section pb-48">
|
||||
|
||||
<div class="pb-16" fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="m-0 mr-16">access_time</mat-icon>
|
||||
<div class="h2 secondary-text">Order Status</div>
|
||||
</div>
|
||||
|
||||
<table class="simple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Updated On</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr *ngFor="let status of order.status">
|
||||
<td>
|
||||
<span class="status h6 p-4" [ngClass]="status.color">
|
||||
{{status.name}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
{{status.date | date}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form class="update-status p-24"
|
||||
(ngSubmit)="updateStatus()"
|
||||
[formGroup]="statusForm"
|
||||
fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<mat-form-field class="mr-16" fxFlex>
|
||||
<mat-select formControlName="newStatus"
|
||||
placeholder="Select a status" required>
|
||||
<mat-option *ngFor="let status of orderStatuses"
|
||||
[value]="status.id">
|
||||
{{status.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-raised-button class="mat-accent"
|
||||
[disabled]="statusForm.invalid">
|
||||
Update Status
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="section pb-48">
|
||||
|
||||
<div class="pb-16" fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="m-0 mr-16">attach_money</mat-icon>
|
||||
<div class="h2 secondary-text">Payment</div>
|
||||
</div>
|
||||
|
||||
<table class="simple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>TransactionID</th>
|
||||
<th>Payment Method</th>
|
||||
<th>Amount</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="text-truncate">
|
||||
{{order.payment.transactionId}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-truncate">
|
||||
{{order.payment.method}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-truncate">
|
||||
{{order.payment.amount}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-truncate">
|
||||
{{order.payment.date | date}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section pb-48">
|
||||
|
||||
<div class="pb-16" fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="m-0 mr-16">local_shipping</mat-icon>
|
||||
<div class="h2 secondary-text">Shipping</div>
|
||||
</div>
|
||||
|
||||
<table class="simple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tracking Code</th>
|
||||
<th>Carrier</th>
|
||||
<th>Weight</th>
|
||||
<th>Fee</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let shipping of order.shippingDetails">
|
||||
<td class="tracking-code">
|
||||
{{shipping.tracking}}
|
||||
</td>
|
||||
<td>
|
||||
{{shipping.carrier}}
|
||||
</td>
|
||||
<td>
|
||||
{{shipping.weight}}
|
||||
</td>
|
||||
<td>
|
||||
{{shipping.fee}}
|
||||
</td>
|
||||
<td>
|
||||
{{shipping.date}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Products">
|
||||
<div class="products tab-content p-24" fusePerfectScrollbar>
|
||||
<table class="simple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Image</th>
|
||||
<th>Name</th>
|
||||
<th>Price</th>
|
||||
<th>Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="product-row"
|
||||
*ngFor="let product of order.products"
|
||||
[routerLink]="'/apps/e-commerce/products/'+product.id+'/'+product.handle">
|
||||
<td class="w-64">
|
||||
{{product.id}}
|
||||
</td>
|
||||
<td class="w-80">
|
||||
<img class="product-image" [src]="product.image">
|
||||
</td>
|
||||
<td>
|
||||
{{product.name}}
|
||||
</td>
|
||||
<td>
|
||||
{{product.price}}
|
||||
</td>
|
||||
<td>
|
||||
{{product.quantity}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Invoice">
|
||||
|
||||
<div class="invoice tab-content p-24" fusePerfectScrollbar>
|
||||
|
||||
<div id="invoice" class="compact page-layout blank" fxLayout="row" fusePerfectScrollbar>
|
||||
|
||||
<div class="invoice-container">
|
||||
|
||||
<!-- INVOICE -->
|
||||
<div class="card">
|
||||
|
||||
<div class="header">
|
||||
<div class="invoice-date">{{order.date}}</div>
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
<div class="client">
|
||||
<div class="invoice-number" fxLayout="row" fxLayoutAlign="start center">
|
||||
<span class="title">INVOICE</span>
|
||||
<span class="number">{{order.reference}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
{{order.customer.firstName}}
|
||||
{{order.customer.lastName}}
|
||||
</div>
|
||||
<div class="address">{{order.customer.invoiceAddress}}</div>
|
||||
<div class="phone">{{order.customer.phone}}</div>
|
||||
<div class="email">{{order.customer.email}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="issuer mat-accent-bg" fxLayout="row" fxLayoutAlign="start center">
|
||||
<div class="logo">
|
||||
<img src="assets/images/logos/fuse.svg">
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="title">FUSE INC.</div>
|
||||
<div class="address">2810 Country Club Road Cranford, NJ 07016</div>
|
||||
<div class="phone">+66 123 455 87</div>
|
||||
<div class="email">hello@fuseinc.com</div>
|
||||
<div class="website">www.fuseinc.com</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<table class="simple invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>PRODUCT</th>
|
||||
<th class="text-right">PRICE</th>
|
||||
<th class="text-right">QUANTITY</th>
|
||||
<th class="text-right">TOTAL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let product of order.products">
|
||||
<td>
|
||||
<div class="title">
|
||||
{{product.name}}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{{product.price | currency:'USD':true}}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{{product.quantity}}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{{product.total | currency:'USD':true}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="simple invoice-table-footer">
|
||||
<tbody>
|
||||
<tr class="subtotal">
|
||||
<td>SUBTOTAL</td>
|
||||
<td>{{order.subtotal | currency:'USD':true}}</td>
|
||||
</tr>
|
||||
<tr class="tax">
|
||||
<td>TAX</td>
|
||||
<td>{{order.tax | currency:'USD':true}}</td>
|
||||
</tr>
|
||||
<tr class="discount">
|
||||
<td>DISCOUNT</td>
|
||||
<td>-{{order.discount | currency:'USD':true}}</td>
|
||||
</tr>
|
||||
<tr class="total">
|
||||
<td>TOTAL</td>
|
||||
<td>{{order.total | currency:'USD':true}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="note">Please pay within 15 days. Thank you for your business.</div>
|
||||
<div fxLayout="row" fxLayoutAlign="start start">
|
||||
<div class="logo">
|
||||
<img src="assets/images/logos/fuse.svg">
|
||||
</div>
|
||||
<div class="small-note">
|
||||
In condimentum malesuada efficitur. Mauris volutpat placerat auctor. Ut ac congue dolor. Quisque
|
||||
scelerisque lacus sed feugiat fermentum. Cras aliquet facilisis pellentesque. Nunc hendrerit
|
||||
quam at leo commodo, a suscipit tellus dapibus. Etiam at felis volutpat est mollis lacinia.
|
||||
Mauris placerat sem sit amet velit mollis, in porttitor ex finibus. Proin eu nibh id libero
|
||||
tincidunt lacinia et eget eros.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / INVOICE -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
</mat-tab-group>
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT -->
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT CARD -->
|
||||
|
||||
</div>
|
||||
<!-- / CENTER -->
|
||||
</div>
|
395
src/app/main/content/apps/e-commerce/order/order.component.scss
Normal file
395
src/app/main/content/apps/e-commerce/order/order.component.scss
Normal file
|
@ -0,0 +1,395 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
#order {
|
||||
|
||||
.header {
|
||||
|
||||
.subtitle {
|
||||
margin: 6px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
.mat-tab-group,
|
||||
.mat-tab-body-wrapper,
|
||||
.tab-content {
|
||||
flex: 1 1 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
flex: 1 1 auto;
|
||||
|
||||
&.products {
|
||||
|
||||
.product-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&.invoice {
|
||||
|
||||
#invoice {
|
||||
|
||||
&.compact {
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
|
||||
.invoice-container {
|
||||
padding: 64px;
|
||||
|
||||
.card {
|
||||
width: 1020px;
|
||||
min-width: 1020px;
|
||||
max-width: 1020px;
|
||||
padding: 64px 88px;
|
||||
overflow: hidden;
|
||||
background: #FFFFFF;
|
||||
@include mat-elevation(7);
|
||||
|
||||
.header {
|
||||
|
||||
.invoice-date {
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.client {
|
||||
|
||||
.invoice-number {
|
||||
font-size: 18px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
.title {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
.number {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.due-date {
|
||||
font-size: 18px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
.title {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
.date {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.issuer {
|
||||
margin-right: -88px;
|
||||
padding-right: 66px;
|
||||
|
||||
.logo {
|
||||
width: 96px;
|
||||
padding: 0 8px;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
.invoice-table {
|
||||
margin-top: 64px;
|
||||
font-size: 15px;
|
||||
|
||||
thead {
|
||||
|
||||
tr {
|
||||
|
||||
th {
|
||||
|
||||
&:first-child {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
|
||||
tr {
|
||||
|
||||
td {
|
||||
|
||||
&:first-child {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
max-width: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice-table-footer {
|
||||
margin: 32px 0 72px 0;
|
||||
|
||||
tr {
|
||||
|
||||
td {
|
||||
text-align: right;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
border-bottom: none;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&.discount {
|
||||
|
||||
td {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&.total {
|
||||
|
||||
td {
|
||||
padding: 24px 8px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
.note {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
// IE10 fix
|
||||
.logo, .small-note {
|
||||
-ms-flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 32px;
|
||||
min-width: 32px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.small-note {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PRINT STYLES */
|
||||
@media print {
|
||||
|
||||
/* Invoice Specific Styles */
|
||||
#invoice {
|
||||
|
||||
&.compact {
|
||||
|
||||
.invoice-container {
|
||||
padding: 0;
|
||||
|
||||
.card {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
background: none;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
|
||||
.header {
|
||||
|
||||
.invoice-date {
|
||||
margin-bottom: 16pt;
|
||||
}
|
||||
|
||||
.issuer {
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
.invoice-table {
|
||||
margin-top: 16pt;
|
||||
|
||||
thead {
|
||||
|
||||
tr {
|
||||
|
||||
th {
|
||||
font-size: 10pt;
|
||||
max-width: 60pt;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
|
||||
tr {
|
||||
|
||||
td {
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.detail {
|
||||
margin-top: 4pt;
|
||||
font-size: 9pt;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice-table-footer {
|
||||
margin: 16pt 0;
|
||||
|
||||
tr {
|
||||
|
||||
td {
|
||||
font-size: 13pt;
|
||||
padding: 4pt 4pt;
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.discount {
|
||||
|
||||
td {
|
||||
padding-bottom: 16pt;
|
||||
}
|
||||
}
|
||||
|
||||
&.total {
|
||||
|
||||
td {
|
||||
padding: 16pt 4pt 0 4pt;
|
||||
font-size: 16pt;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
.note {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 8pt;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-right: 8pt;
|
||||
}
|
||||
|
||||
.small-note {
|
||||
font-size: 8pt;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-tab-body-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mat-tab-label {
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
import { Component, ElementRef, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { EcommerceOrderService } from './order.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/observable/merge';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Order } from './order.model';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Location } from '@angular/common';
|
||||
import { orderStatuses } from './order-statuses';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-e-commerce-order',
|
||||
templateUrl : './order.component.html',
|
||||
styleUrls : ['./order.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
animations : fuseAnimations
|
||||
})
|
||||
export class FuseEcommerceOrderComponent implements OnInit, OnDestroy
|
||||
{
|
||||
order = new Order();
|
||||
onOrderChanged: Subscription;
|
||||
statusForm: FormGroup;
|
||||
orderStatuses = orderStatuses;
|
||||
|
||||
constructor(
|
||||
private orderService: EcommerceOrderService,
|
||||
private formBuilder: FormBuilder,
|
||||
public snackBar: MatSnackBar,
|
||||
private location: Location
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
// Subscribe to update order on changes
|
||||
this.onOrderChanged =
|
||||
this.orderService.onOrderChanged
|
||||
.subscribe(order => {
|
||||
this.order = new Order(order);
|
||||
});
|
||||
|
||||
this.statusForm = this.formBuilder.group({
|
||||
newStatus: ['']
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus()
|
||||
{
|
||||
const newStatusId = Number.parseInt(this.statusForm.get('newStatus').value);
|
||||
|
||||
if ( !newStatusId )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const newStatus = this.orderStatuses.find((status) => {
|
||||
return status.id === newStatusId;
|
||||
});
|
||||
|
||||
newStatus['date'] = new Date().toString();
|
||||
|
||||
this.order.status.unshift(newStatus);
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
this.onOrderChanged.unsubscribe();
|
||||
}
|
||||
}
|
34
src/app/main/content/apps/e-commerce/order/order.model.ts
Normal file
34
src/app/main/content/apps/e-commerce/order/order.model.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
|
||||
export class Order
|
||||
{
|
||||
id: string;
|
||||
reference: string;
|
||||
subtotal: string;
|
||||
tax: string;
|
||||
discount: string;
|
||||
total: string;
|
||||
date: string;
|
||||
customer: any;
|
||||
products: any[];
|
||||
status: any[];
|
||||
payment: any;
|
||||
shippingDetails: any[];
|
||||
|
||||
constructor(order?)
|
||||
{
|
||||
order = order || {};
|
||||
this.id = order.id || FuseUtils.generateGUID();
|
||||
this.reference = order.reference || FuseUtils.generateGUID();
|
||||
this.subtotal = order.subtotal || 0;
|
||||
this.tax = order.tax || 0;
|
||||
this.discount = order.discount || 0;
|
||||
this.total = order.total || 0;
|
||||
this.date = order.date || '';
|
||||
this.customer = order.customer || {};
|
||||
this.products = order.products || [];
|
||||
this.status = order.status || [];
|
||||
this.payment = order.payment || {};
|
||||
this.shippingDetails = order.shippingDetails || [];
|
||||
}
|
||||
}
|
75
src/app/main/content/apps/e-commerce/order/order.service.ts
Normal file
75
src/app/main/content/apps/e-commerce/order/order.service.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
@Injectable()
|
||||
export class EcommerceOrderService implements Resolve<any>
|
||||
{
|
||||
routeParams: any;
|
||||
order: any;
|
||||
onOrderChanged: BehaviorSubject<any> = new BehaviorSubject({});
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve
|
||||
* @param {ActivatedRouteSnapshot} route
|
||||
* @param {RouterStateSnapshot} state
|
||||
* @returns {Observable<any> | Promise<any> | any}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
|
||||
{
|
||||
|
||||
this.routeParams = route.params;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
Promise.all([
|
||||
this.getOrder()
|
||||
]).then(
|
||||
() => {
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getOrder(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/e-commerce-orders/' + this.routeParams.id)
|
||||
.subscribe((response: any) => {
|
||||
this.order = response;
|
||||
this.onOrderChanged.next(this.order);
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
saveOrder(order)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.post('api/e-commerce-orders/' + order.id, order)
|
||||
.subscribe((response: any) => {
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
addOrder(order)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.post('api/e-commerce-orders/', order)
|
||||
.subscribe((response: any) => {
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
<div id="orders" class="page-layout carded fullwidth" fusePerfectScrollbar>
|
||||
|
||||
<!-- TOP BACKGROUND -->
|
||||
<div class="top-bg mat-accent-bg"></div>
|
||||
<!-- / TOP BACKGROUND -->
|
||||
|
||||
<!-- CENTER -->
|
||||
<div class="center">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header white-fg"
|
||||
fxLayout="column" fxLayoutAlign="center center"
|
||||
fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="space-between center">
|
||||
|
||||
<!-- APP TITLE -->
|
||||
<div class="logo"
|
||||
fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="logo-icon mr-16" *fuseIfOnDom [@animate]="{value:'*',params:{delay:'50ms',scale:'0.2'}}">shopping_basket</mat-icon>
|
||||
<span class="logo-text h1" *fuseIfOnDom [@animate]="{value:'*',params:{delay:'100ms',x:'-25px'}}">Orders</span>
|
||||
</div>
|
||||
<!-- / APP TITLE -->
|
||||
|
||||
<!-- SEARCH -->
|
||||
<div class="search-input-wrapper ml-8 m-sm-0"
|
||||
fxFlex="1 0 auto" fxLayout="row" fxLayoutAlign="start center">
|
||||
<label for="search" class="mr-8">
|
||||
<mat-icon class="secondary-text">search</mat-icon>
|
||||
</label>
|
||||
<mat-form-field floatPlaceholder="never" fxFlex="1 0 auto">
|
||||
<input id="search" matInput #filter placeholder="Search">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<!-- / SEARCH -->
|
||||
</div>
|
||||
<!-- / HEADER -->
|
||||
|
||||
<!-- CONTENT CARD -->
|
||||
<div class="content-card mat-white-bg">
|
||||
|
||||
<mat-table class="orders-table"
|
||||
#table [dataSource]="dataSource"
|
||||
matSort
|
||||
[@animateStagger]="{value:'50'}"
|
||||
fusePerfectScrollbar>
|
||||
|
||||
<!-- ID Column -->
|
||||
<ng-container cdkColumnDef="id">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header>ID</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order">
|
||||
<p class="text-truncate">{{order.id}}</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Reference Column -->
|
||||
<ng-container cdkColumnDef="reference">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-sm>Reference</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order" fxHide fxShow.gt-sm>
|
||||
<p class="text-truncate">{{order.reference}}</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container cdkColumnDef="customer">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Customer</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order">
|
||||
<p class="text-truncate">
|
||||
{{order.customer.firstName}}
|
||||
{{order.customer.lastName}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Total Price Column -->
|
||||
<ng-container cdkColumnDef="total">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-md>Total</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order" fxHide fxShow.gt-md>
|
||||
<p class="total-price text-truncate">
|
||||
{{order.total | currency:'USD':true}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Payment Column -->
|
||||
<ng-container cdkColumnDef="payment">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-sm>Payment</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order" fxHide fxShow.gt-sm>
|
||||
<p class="text-truncate">
|
||||
{{order.payment.method}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Status Column -->
|
||||
<ng-container cdkColumnDef="status">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header >Status</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order">
|
||||
<p class="status text-truncate h6 p-4" [ngClass]="order.status[0].color">
|
||||
{{order.status[0].name}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Date Column -->
|
||||
<ng-container cdkColumnDef="date">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-sm>Date</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let order" fxHide fxShow.gt-sm>
|
||||
<p class="text-truncate">
|
||||
{{order.date}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
|
||||
<mat-row *cdkRowDef="let order; columns: displayedColumns;"
|
||||
class="order"
|
||||
matRipple
|
||||
[routerLink]="'/apps/e-commerce/orders/'+order.id">
|
||||
</mat-row>
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator #paginator
|
||||
[length]="dataSource.filteredData.length"
|
||||
[pageIndex]="0"
|
||||
[pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 25, 100]">
|
||||
</mat-paginator>
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT CARD -->
|
||||
</div>
|
||||
<!-- / CENTER -->
|
||||
</div>
|
|
@ -0,0 +1,73 @@
|
|||
:host {
|
||||
|
||||
.header {
|
||||
|
||||
.search-input-wrapper {
|
||||
max-width: 480px;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-tab-group,
|
||||
.mat-tab-body-wrapper,
|
||||
.tab-content{
|
||||
flex: 1 1 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.orders-table {
|
||||
flex: 1 1 auto;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .12);
|
||||
|
||||
.mat-header-row {
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
.order {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 84px;
|
||||
}
|
||||
|
||||
.mat-cell {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mat-column-id {
|
||||
flex: 0 1 84px;
|
||||
}
|
||||
|
||||
.mat-column-image {
|
||||
flex: 0 1 84px;
|
||||
|
||||
.product-image {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border: 1px solid rgba(0, 0, 0, .12);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-column-buttons {
|
||||
flex: 0 1 80px;
|
||||
}
|
||||
|
||||
.quantity-indicator {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
|
||||
& + span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.active-icon {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
169
src/app/main/content/apps/e-commerce/orders/orders.component.ts
Normal file
169
src/app/main/content/apps/e-commerce/orders/orders.component.ts
Normal file
|
@ -0,0 +1,169 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { EcommerceOrdersService } from './orders.service';
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
import { MatPaginator, MatSort } from '@angular/material';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/observable/merge';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-e-commerce-orders',
|
||||
templateUrl: './orders.component.html',
|
||||
styleUrls : ['./orders.component.scss'],
|
||||
animations : fuseAnimations
|
||||
})
|
||||
export class FuseEcommerceOrdersComponent implements OnInit
|
||||
{
|
||||
dataSource: FilesDataSource | null;
|
||||
displayedColumns = ['id', 'reference', 'customer', 'total', 'payment', 'status', 'date'];
|
||||
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild('filter') filter: ElementRef;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
constructor(
|
||||
private ordersService: EcommerceOrdersService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.dataSource = new FilesDataSource(this.ordersService, this.paginator, this.sort);
|
||||
|
||||
Observable.fromEvent(this.filter.nativeElement, 'keyup')
|
||||
.debounceTime(150)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(() => {
|
||||
if ( !this.dataSource )
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.dataSource.filter = this.filter.nativeElement.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FilesDataSource extends DataSource<any>
|
||||
{
|
||||
_filterChange = new BehaviorSubject('');
|
||||
_filteredDataChange = new BehaviorSubject('');
|
||||
|
||||
get filteredData(): any
|
||||
{
|
||||
return this._filteredDataChange.value;
|
||||
}
|
||||
|
||||
set filteredData(value: any)
|
||||
{
|
||||
this._filteredDataChange.next(value);
|
||||
}
|
||||
|
||||
get filter(): string
|
||||
{
|
||||
return this._filterChange.value;
|
||||
}
|
||||
|
||||
set filter(filter: string)
|
||||
{
|
||||
this._filterChange.next(filter);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private ordersService: EcommerceOrdersService,
|
||||
private _paginator: MatPaginator,
|
||||
private _sort: MatSort
|
||||
)
|
||||
{
|
||||
super();
|
||||
this.filteredData = this.ordersService.orders;
|
||||
}
|
||||
|
||||
/** Connect function called by the table to retrieve one stream containing the data to render. */
|
||||
connect(): Observable<any[]>
|
||||
{
|
||||
const displayDataChanges = [
|
||||
this.ordersService.onOrdersChanged,
|
||||
this._paginator.page,
|
||||
this._filterChange,
|
||||
this._sort.sortChange
|
||||
];
|
||||
return Observable.merge(...displayDataChanges).map(() => {
|
||||
let data = this.ordersService.orders.slice();
|
||||
|
||||
data = this.filterData(data);
|
||||
|
||||
this.filteredData = [...data];
|
||||
|
||||
data = this.sortData(data);
|
||||
|
||||
// Grab the page's slice of data.
|
||||
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
|
||||
return data.splice(startIndex, this._paginator.pageSize);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
filterData(data)
|
||||
{
|
||||
if ( !this.filter )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
return FuseUtils.filterArrayByString(data, this.filter);
|
||||
}
|
||||
|
||||
sortData(data): any[]
|
||||
{
|
||||
if ( !this._sort.active || this._sort.direction === '' )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
return data.sort((a, b) => {
|
||||
let propertyA: number | string = '';
|
||||
let propertyB: number | string = '';
|
||||
|
||||
switch ( this._sort.active )
|
||||
{
|
||||
case 'id':
|
||||
[propertyA, propertyB] = [a.id, b.id];
|
||||
break;
|
||||
case 'reference':
|
||||
[propertyA, propertyB] = [a.reference, b.reference];
|
||||
break;
|
||||
case 'customer':
|
||||
[propertyA, propertyB] = [a.customer.firstName, b.customer.firstName];
|
||||
break;
|
||||
case 'total':
|
||||
[propertyA, propertyB] = [a.total, b.total];
|
||||
break;
|
||||
case 'payment':
|
||||
[propertyA, propertyB] = [a.payment.method, b.payment.method];
|
||||
break;
|
||||
case 'status':
|
||||
[propertyA, propertyB] = [a.status[0].name, b.status[0].name];
|
||||
break;
|
||||
case 'date':
|
||||
[propertyA, propertyB] = [a.date, b.date];
|
||||
break;
|
||||
}
|
||||
|
||||
const valueA = isNaN(+propertyA) ? propertyA : +propertyA;
|
||||
const valueB = isNaN(+propertyB) ? propertyB : +propertyB;
|
||||
|
||||
return (valueA < valueB ? -1 : 1) * (this._sort.direction === 'asc' ? 1 : -1);
|
||||
});
|
||||
}
|
||||
|
||||
disconnect()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
@Injectable()
|
||||
export class EcommerceOrdersService implements Resolve<any>
|
||||
{
|
||||
orders: any[];
|
||||
onOrdersChanged: BehaviorSubject<any> = new BehaviorSubject({});
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve
|
||||
* @param {ActivatedRouteSnapshot} route
|
||||
* @param {RouterStateSnapshot} state
|
||||
* @returns {Observable<any> | Promise<any> | any}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
|
||||
{
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
Promise.all([
|
||||
this.getOrders()
|
||||
]).then(
|
||||
() => {
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getOrders(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/e-commerce-orders')
|
||||
.subscribe((response: any) => {
|
||||
this.orders = response;
|
||||
this.onOrdersChanged.next(this.orders);
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
<div id="product" class="page-layout carded fullwidth" fusePerfectScrollbar>
|
||||
|
||||
<!-- TOP BACKGROUND -->
|
||||
<div class="top-bg mat-accent-bg"></div>
|
||||
<!-- / TOP BACKGROUND -->
|
||||
|
||||
<!-- CENTER -->
|
||||
<div class="center">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header white-fg" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
|
||||
<!-- APP TITLE -->
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<button class="mr-0 mr-sm-16" mat-icon-button [routerLink]="'/apps/e-commerce/products'">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
|
||||
<div class="product-image mr-8 mr-sm-16" *fuseIfOnDom [@animate]="{value:'*',params:{delay:'50ms',scale:'0.2'}}">
|
||||
<img *ngIf="product.images[0]" [src]="product.images[0].url">
|
||||
<img *ngIf="!product.images[0]" [src]="'assets/images/ecommerce/product-image-placeholder.png'">
|
||||
</div>
|
||||
|
||||
<div fxLayout="column" fxLayoutAlign="start start"
|
||||
*fuseIfOnDom [@animate]="{value:'*',params:{delay:'100ms',x:'-25px'}}">
|
||||
<div class="h2" *ngIf="pageType ==='edit'">
|
||||
{{product.name}}
|
||||
</div>
|
||||
<div class="h2" *ngIf="pageType ==='new'">
|
||||
New Product
|
||||
</div>
|
||||
<div class="subtitle secondary-text">
|
||||
<span>Product Detail</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / APP TITLE -->
|
||||
|
||||
<button mat-raised-button
|
||||
class="save-product-button mat-white-bg mt-16 mt-sm-0"
|
||||
[disabled]="productForm.invalid"
|
||||
*ngIf="pageType ==='new'" (click)="addProduct()">
|
||||
<span>ADD</span>
|
||||
</button>
|
||||
|
||||
<button mat-raised-button
|
||||
class="save-product-button mat-white-bg mt-16 mt-sm-0"
|
||||
[disabled]="productForm.invalid || productForm.pristine"
|
||||
*ngIf="pageType ==='edit'" (click)="saveProduct()">
|
||||
<span>SAVE</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- / HEADER -->
|
||||
|
||||
<!-- CONTENT CARD -->
|
||||
<div class="content-card mat-white-bg">
|
||||
|
||||
<!-- CONTENT -->
|
||||
<div class="content">
|
||||
|
||||
<form name="productForm" [formGroup]="productForm" class="product w-100-p" fxLayout="column" fxFlex>
|
||||
|
||||
<mat-tab-group>
|
||||
|
||||
<mat-tab label="Basic Info">
|
||||
<div class="tab-content p-24" fusePerfectScrollbar>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="name"
|
||||
formControlName="name"
|
||||
placeholder="Product Name"
|
||||
required>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<textarea matInput
|
||||
name="description"
|
||||
formControlName="description"
|
||||
placeholder="Product Description"
|
||||
rows="10">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<h3 class="mb-0">Categories</h3>
|
||||
<mat-form-field class="w-100-p" floatPlaceholder="never">
|
||||
<mat-chip-list matPrefix #categoryList
|
||||
name="categories"
|
||||
formControlName="categories">
|
||||
|
||||
<mat-chip *ngFor="let category of product.categories"
|
||||
removable="true" (remove)="product.removeCategory(category)">
|
||||
{{category}}
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
<input matInput
|
||||
matChipInputAddOnBlur="true"
|
||||
(matChipInputTokenEnd)="product.addCategory($event)"
|
||||
placeholder="Add category..."
|
||||
[matChipInputFor]="categoryList"/>
|
||||
</mat-form-field>
|
||||
|
||||
<h3 class="mb-0">Tags</h3>
|
||||
<mat-form-field class="w-100-p" floatPlaceholder="never">
|
||||
<mat-chip-list matPrefix #tagList
|
||||
name="tags"
|
||||
formControlName="tags">
|
||||
|
||||
<mat-chip *ngFor="let tag of product.tags"
|
||||
removable="true" (remove)="product.removeTag(tag)">
|
||||
{{tag}}
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
<input matInput
|
||||
matChipInputAddOnBlur="true"
|
||||
(matChipInputTokenEnd)="product.addTag($event)"
|
||||
placeholder="Add tag..."
|
||||
[matChipInputFor]="tagList"/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Product Images">
|
||||
<div class="tab-content p-24" fusePerfectScrollbar>
|
||||
<div fxLayout="row" fxLayoutAlign="start start" fxLayoutWrap>
|
||||
|
||||
<div *ngIf="product.images.length === 0"
|
||||
class="product-image" fxlayout="row" fxLayoutAlign="center center">
|
||||
<img class="media" [src]="'assets/images/ecommerce/product-image-placeholder.png'">
|
||||
</div>
|
||||
|
||||
<div *ngFor="let image of product.images">
|
||||
<div *ngIf="product.images.length > 0"
|
||||
class="product-image" fxlayout="row" fxLayoutAlign="center center">
|
||||
<img class="media" [src]="image.url">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Pricing">
|
||||
<div class="tab-content p-24" fusePerfectScrollbar>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="priceTaxExcl"
|
||||
formControlName="priceTaxExcl"
|
||||
placeholder="Tax Excluded Price" type="number">
|
||||
<span matPrefix>$ </span>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="priceTaxIncl"
|
||||
formControlName="priceTaxIncl"
|
||||
placeholder="Tax Included Price" type="number">
|
||||
<span matPrefix>$ </span>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="taxRate"
|
||||
formControlName="taxRate"
|
||||
placeholder="Tax Rate" type="number">
|
||||
<span matPrefix>%</span>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="comparedPrice"
|
||||
formControlName="comparedPrice"
|
||||
placeholder="Compared Price" type="number">
|
||||
<span matPrefix>$ </span>
|
||||
<mat-hint align="start">Add a compare price to show next to the real price</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Inventory">
|
||||
<div class="tab-content p-24" fusePerfectScrollbar>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="sku"
|
||||
formControlName="sku"
|
||||
placeholder="SKU">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="quantity"
|
||||
formControlName="quantity"
|
||||
placeholder="Quantity" type="number">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Shipping">
|
||||
<div class="tab-content p-24" fusePerfectScrollbar fxLayout="column">
|
||||
|
||||
<div class="py-16" fxFlex="1 0 auto" fxLayout="row">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput
|
||||
name="Width"
|
||||
formControlName="width"
|
||||
placeholder="Width">
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput
|
||||
name="Height"
|
||||
formControlName="height"
|
||||
placeholder="Height">
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput
|
||||
name="Depth"
|
||||
formControlName="depth"
|
||||
placeholder="Depth">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="Weight"
|
||||
formControlName="weight"
|
||||
placeholder="Weight">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100-p">
|
||||
<input matInput
|
||||
name="extraShippingFee"
|
||||
formControlName="extraShippingFee"
|
||||
placeholder="Extra Shipping Fee" type="number">
|
||||
<span matPrefix>$ </span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
</mat-tab-group>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT -->
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT CARD -->
|
||||
|
||||
</div>
|
||||
<!-- / CENTER -->
|
||||
</div>
|
|
@ -0,0 +1,57 @@
|
|||
#product {
|
||||
|
||||
.header {
|
||||
|
||||
.product-image {
|
||||
overflow: hidden;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 3px solid rgba(0, 0, 0, 0.12);
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 6px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
.mat-tab-group,
|
||||
.mat-tab-body-wrapper,
|
||||
.tab-content{
|
||||
flex: 1 1 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mat-tab-body-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mat-tab-label {
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
overflow: hidden;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
margin-right: 16px;
|
||||
margin-bottom: 16px;
|
||||
border: 3px solid rgba(0, 0, 0, 0.12);
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
import { Component, ElementRef, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { EcommerceProductService } from './product.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/observable/merge';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Product } from './product.model';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-e-commerce-product',
|
||||
templateUrl : './product.component.html',
|
||||
styleUrls : ['./product.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
animations : fuseAnimations
|
||||
})
|
||||
export class FuseEcommerceProductComponent implements OnInit, OnDestroy
|
||||
{
|
||||
product = new Product();
|
||||
onProductChanged: Subscription;
|
||||
pageType: string;
|
||||
productForm: FormGroup;
|
||||
|
||||
constructor(
|
||||
private productService: EcommerceProductService,
|
||||
private formBuilder: FormBuilder,
|
||||
public snackBar: MatSnackBar,
|
||||
private location: Location
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
// Subscribe to update product on changes
|
||||
this.onProductChanged =
|
||||
this.productService.onProductChanged
|
||||
.subscribe(product => {
|
||||
|
||||
if ( product )
|
||||
{
|
||||
this.product = new Product(product);
|
||||
this.pageType = 'edit';
|
||||
}
|
||||
else
|
||||
{
|
||||
this.pageType = 'new';
|
||||
this.product = new Product();
|
||||
}
|
||||
|
||||
this.productForm = this.createProductForm();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
createProductForm()
|
||||
{
|
||||
return this.formBuilder.group({
|
||||
id : [this.product.id],
|
||||
name : [this.product.name],
|
||||
handle : [this.product.handle],
|
||||
description : [this.product.description],
|
||||
categories : [this.product.categories],
|
||||
tags : [this.product.tags],
|
||||
images : [this.product.images],
|
||||
priceTaxExcl : [this.product.priceTaxExcl],
|
||||
priceTaxIncl : [this.product.priceTaxIncl],
|
||||
taxRate : [this.product.taxRate],
|
||||
comparedPrice : [this.product.comparedPrice],
|
||||
quantity : [this.product.quantity],
|
||||
sku : [this.product.sku],
|
||||
width : [this.product.width],
|
||||
height : [this.product.height],
|
||||
depth : [this.product.depth],
|
||||
weight : [this.product.weight],
|
||||
extraShippingFee: [this.product.extraShippingFee],
|
||||
active : [this.product.active]
|
||||
});
|
||||
}
|
||||
|
||||
saveProduct()
|
||||
{
|
||||
const data = this.productForm.getRawValue();
|
||||
data.handle = FuseUtils.handleize(data.name);
|
||||
this.productService.saveProduct(data)
|
||||
.then(() => {
|
||||
|
||||
// Trigger the subscription with new data
|
||||
this.productService.onProductChanged.next(data);
|
||||
|
||||
// Show the success message
|
||||
this.snackBar.open('Product saved', 'OK', {
|
||||
verticalPosition: 'top',
|
||||
duration : 2000
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
addProduct()
|
||||
{
|
||||
const data = this.productForm.getRawValue();
|
||||
data.handle = FuseUtils.handleize(data.name);
|
||||
this.productService.addProduct(data)
|
||||
.then(() => {
|
||||
|
||||
// Trigger the subscription with new data
|
||||
this.productService.onProductChanged.next(data);
|
||||
|
||||
// Show the success message
|
||||
this.snackBar.open('Product added', 'OK', {
|
||||
verticalPosition: 'top',
|
||||
duration : 2000
|
||||
});
|
||||
|
||||
// Change the location with new one
|
||||
this.location.go('apps/e-commerce/products/' + this.product.id + '/' + this.product.handle);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
this.onProductChanged.unsubscribe();
|
||||
}
|
||||
}
|
110
src/app/main/content/apps/e-commerce/product/product.model.ts
Normal file
110
src/app/main/content/apps/e-commerce/product/product.model.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
import { MatChipInputEvent } from '@angular/material';
|
||||
|
||||
export class Product
|
||||
{
|
||||
id: string;
|
||||
name: string;
|
||||
handle: string;
|
||||
description: string;
|
||||
categories: string[];
|
||||
tags: string[];
|
||||
images: {
|
||||
default: boolean,
|
||||
id: string,
|
||||
url: string,
|
||||
type: string
|
||||
}[];
|
||||
priceTaxExcl: number;
|
||||
priceTaxIncl: number;
|
||||
taxRate: number;
|
||||
comparedPrice: number;
|
||||
quantity: number;
|
||||
sku: string;
|
||||
width: string;
|
||||
height: string;
|
||||
depth: string;
|
||||
weight: string;
|
||||
extraShippingFee: number;
|
||||
active: boolean;
|
||||
|
||||
constructor(product?)
|
||||
{
|
||||
product = product || {};
|
||||
this.id = product.id || FuseUtils.generateGUID();
|
||||
this.name = product.name || '';
|
||||
this.handle = product.handle || FuseUtils.handleize(this.name);
|
||||
this.description = product.description || '';
|
||||
this.categories = product.categories || [];
|
||||
this.tags = product.tags || [];
|
||||
this.images = product.images || [];
|
||||
this.priceTaxExcl = product.priceTaxExcl || 0;
|
||||
this.priceTaxIncl = product.priceTaxIncl || 0;
|
||||
this.taxRate = product.taxRate || 0;
|
||||
this.comparedPrice = product.comparedPrice || 0;
|
||||
this.quantity = product.quantity || 0;
|
||||
this.sku = product.sku || 0;
|
||||
this.width = product.width || 0;
|
||||
this.height = product.height || 0;
|
||||
this.depth = product.depth || 0;
|
||||
this.weight = product.weight || 0;
|
||||
this.extraShippingFee = product.extraShippingFee || 0;
|
||||
this.active = product.active || true;
|
||||
}
|
||||
|
||||
addCategory(event: MatChipInputEvent): void
|
||||
{
|
||||
const input = event.input;
|
||||
const value = event.value;
|
||||
|
||||
// Add category
|
||||
if ( value )
|
||||
{
|
||||
this.categories.push(value);
|
||||
}
|
||||
|
||||
// Reset the input value
|
||||
if ( input )
|
||||
{
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
removeCategory(category)
|
||||
{
|
||||
const index = this.categories.indexOf(category);
|
||||
|
||||
if ( index >= 0 )
|
||||
{
|
||||
this.categories.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
addTag(event: MatChipInputEvent): void
|
||||
{
|
||||
const input = event.input;
|
||||
const value = event.value;
|
||||
|
||||
// Add tag
|
||||
if ( value )
|
||||
{
|
||||
this.tags.push(value);
|
||||
}
|
||||
|
||||
// Reset the input value
|
||||
if ( input )
|
||||
{
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
removeTag(tag)
|
||||
{
|
||||
const index = this.tags.indexOf(tag);
|
||||
|
||||
if ( index >= 0 )
|
||||
{
|
||||
this.tags.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
@Injectable()
|
||||
export class EcommerceProductService implements Resolve<any>
|
||||
{
|
||||
routeParams: any;
|
||||
product: any;
|
||||
onProductChanged: BehaviorSubject<any> = new BehaviorSubject({});
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve
|
||||
* @param {ActivatedRouteSnapshot} route
|
||||
* @param {RouterStateSnapshot} state
|
||||
* @returns {Observable<any> | Promise<any> | any}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
|
||||
{
|
||||
|
||||
this.routeParams = route.params;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
Promise.all([
|
||||
this.getProduct()
|
||||
]).then(
|
||||
() => {
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getProduct(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
if ( this.routeParams.id === 'new' )
|
||||
{
|
||||
this.onProductChanged.next(false);
|
||||
resolve(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.http.get('api/e-commerce-products/' + this.routeParams.id)
|
||||
.subscribe((response: any) => {
|
||||
this.product = response;
|
||||
this.onProductChanged.next(this.product);
|
||||
resolve(response);
|
||||
}, reject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveProduct(product)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.post('api/e-commerce-products/' + product.id, product)
|
||||
.subscribe((response: any) => {
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
addProduct(product)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.post('api/e-commerce-products/', product)
|
||||
.subscribe((response: any) => {
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<div id="products" class="page-layout carded fullwidth" fusePerfectScrollbar>
|
||||
|
||||
<!-- TOP BACKGROUND -->
|
||||
<div class="top-bg mat-accent-bg"></div>
|
||||
<!-- / TOP BACKGROUND -->
|
||||
|
||||
<!-- CENTER -->
|
||||
<div class="center">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header white-fg"
|
||||
fxLayout="column" fxLayoutAlign="center center"
|
||||
fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="space-between center">
|
||||
|
||||
<!-- APP TITLE -->
|
||||
<div class="logo my-12 m-sm-0"
|
||||
fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="logo-icon mr-16" *fuseIfOnDom [@animate]="{value:'*',params:{delay:'50ms',scale:'0.2'}}">shopping_basket</mat-icon>
|
||||
<span class="logo-text h1" *fuseIfOnDom [@animate]="{value:'*',params:{delay:'100ms',x:'-25px'}}">Products</span>
|
||||
</div>
|
||||
<!-- / APP TITLE -->
|
||||
|
||||
<!-- SEARCH -->
|
||||
<div class="search-input-wrapper mx-12 m-md-0"
|
||||
fxFlex="1 0 auto" fxLayout="row" fxLayoutAlign="start center">
|
||||
<label for="search" class="mr-8">
|
||||
<mat-icon class="secondary-text">search</mat-icon>
|
||||
</label>
|
||||
<mat-form-field floatPlaceholder="never" fxFlex="1 0 auto">
|
||||
<input id="search" matInput #filter placeholder="Search">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<!-- / SEARCH -->
|
||||
|
||||
<button mat-raised-button
|
||||
[routerLink]="'/apps/e-commerce/products/new'"
|
||||
class="add-product-button mat-white-bg my-12 mt-sm-0">
|
||||
<span>ADD NEW PRODUCT</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<!-- / HEADER -->
|
||||
|
||||
<!-- CONTENT CARD -->
|
||||
<div class="content-card mat-white-bg">
|
||||
|
||||
<mat-table class="products-table"
|
||||
#table [dataSource]="dataSource"
|
||||
matSort
|
||||
[@animateStagger]="{value:'50'}"
|
||||
fusePerfectScrollbar>
|
||||
|
||||
<!-- ID Column -->
|
||||
<ng-container cdkColumnDef="id">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header>ID</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product">
|
||||
<p class="text-truncate">{{product.id}}</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Image Column -->
|
||||
<ng-container cdkColumnDef="image">
|
||||
<mat-header-cell *cdkHeaderCellDef></mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product">
|
||||
<img class="product-image"
|
||||
*ngIf="product.images[0]" [alt]="product.name"
|
||||
[src]="product.images[0].url"/>
|
||||
<img *ngIf="!product.images[0]" [src]="'assets/images/ecommerce/product-image-placeholder.png'">
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container cdkColumnDef="name">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Name</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product">
|
||||
<p class="text-truncate">{{product.name}}</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Category Column -->
|
||||
<ng-container cdkColumnDef="category">
|
||||
<mat-header-cell *cdkHeaderCellDef fxHide mat-sort-header fxShow.gt-md>Category</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product" fxHide fxShow.gt-md>
|
||||
<p class="category text-truncate">
|
||||
{{product.categories[0]}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Price Column -->
|
||||
<ng-container cdkColumnDef="price">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-xs>Price</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product" fxHide fxShow.gt-xs>
|
||||
<p class="price text-truncate">
|
||||
{{product.priceTaxIncl | currency:'USD':true}}
|
||||
</p>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Quantity Column -->
|
||||
<ng-container cdkColumnDef="quantity">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-sm>Quantity</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product" fxHide fxShow.gt-sm>
|
||||
|
||||
<span class="quantity-indicator text-truncate"
|
||||
[ngClass]="{'mat-red-500-bg':product.quantity <= 5, 'mat-amber-500-bg':product.quantity > 5 && product.quantity <= 25,'mat-green-600-bg':product.quantity > 25}">
|
||||
</span>
|
||||
<span>
|
||||
{{product.quantity}}
|
||||
</span>
|
||||
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Active Column -->
|
||||
<ng-container cdkColumnDef="active">
|
||||
<mat-header-cell *cdkHeaderCellDef mat-sort-header fxHide fxShow.gt-xs>Active</mat-header-cell>
|
||||
<mat-cell *cdkCellDef="let product" fxHide fxShow.gt-xs>
|
||||
<mat-icon *ngIf="product.active" class="active-icon mat-green-600-bg s-16">check</mat-icon>
|
||||
<mat-icon *ngIf="!product.active" class="active-icon mat-red-500-bg s-16">close</mat-icon>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
|
||||
<mat-row *cdkRowDef="let product; columns: displayedColumns;"
|
||||
class="product"
|
||||
matRipple
|
||||
[routerLink]="'/apps/e-commerce/products/'+product.id+'/'+product.handle">
|
||||
</mat-row>
|
||||
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator #paginator
|
||||
[length]="dataSource.filteredData.length"
|
||||
[pageIndex]="0"
|
||||
[pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 25, 100]">
|
||||
</mat-paginator>
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT CARD -->
|
||||
</div>
|
||||
<!-- / CENTER -->
|
||||
</div>
|
|
@ -0,0 +1,80 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
|
||||
.header {
|
||||
|
||||
.search-input-wrapper {
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
height: 176px !important;
|
||||
min-height: 176px !important;
|
||||
max-height: 176px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bg {
|
||||
@include media-breakpoint-down(xs) {
|
||||
height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.products-table {
|
||||
flex: 1 1 auto;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .12);
|
||||
|
||||
.mat-header-row {
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
.product {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 84px;
|
||||
}
|
||||
|
||||
.mat-cell {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mat-column-id {
|
||||
flex: 0 1 84px;
|
||||
}
|
||||
|
||||
.mat-column-image {
|
||||
flex: 0 1 84px;
|
||||
|
||||
.product-image {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border: 1px solid rgba(0, 0, 0, .12);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-column-buttons {
|
||||
flex: 0 1 80px;
|
||||
}
|
||||
|
||||
.quantity-indicator {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
|
||||
& + span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.active-icon {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { EcommerceProductsService } from './products.service';
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
import { MatPaginator, MatSort } from '@angular/material';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/observable/merge';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-e-commerce-products',
|
||||
templateUrl: './products.component.html',
|
||||
styleUrls : ['./products.component.scss'],
|
||||
animations : fuseAnimations
|
||||
})
|
||||
export class FuseEcommerceProductsComponent implements OnInit
|
||||
{
|
||||
dataSource: FilesDataSource | null;
|
||||
displayedColumns = ['id', 'image', 'name', 'category', 'price', 'quantity', 'active'];
|
||||
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild('filter') filter: ElementRef;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
constructor(
|
||||
private productsService: EcommerceProductsService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.dataSource = new FilesDataSource(this.productsService, this.paginator, this.sort);
|
||||
Observable.fromEvent(this.filter.nativeElement, 'keyup')
|
||||
.debounceTime(150)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(() => {
|
||||
if ( !this.dataSource )
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.dataSource.filter = this.filter.nativeElement.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FilesDataSource extends DataSource<any>
|
||||
{
|
||||
_filterChange = new BehaviorSubject('');
|
||||
_filteredDataChange = new BehaviorSubject('');
|
||||
|
||||
get filteredData(): any
|
||||
{
|
||||
return this._filteredDataChange.value;
|
||||
}
|
||||
|
||||
set filteredData(value: any)
|
||||
{
|
||||
this._filteredDataChange.next(value);
|
||||
}
|
||||
|
||||
get filter(): string
|
||||
{
|
||||
return this._filterChange.value;
|
||||
}
|
||||
|
||||
set filter(filter: string)
|
||||
{
|
||||
this._filterChange.next(filter);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private productsService: EcommerceProductsService,
|
||||
private _paginator: MatPaginator,
|
||||
private _sort: MatSort
|
||||
)
|
||||
{
|
||||
super();
|
||||
this.filteredData = this.productsService.products;
|
||||
}
|
||||
|
||||
/** Connect function called by the table to retrieve one stream containing the data to render. */
|
||||
connect(): Observable<any[]>
|
||||
{
|
||||
const displayDataChanges = [
|
||||
this.productsService.onProductsChanged,
|
||||
this._paginator.page,
|
||||
this._filterChange,
|
||||
this._sort.sortChange
|
||||
];
|
||||
|
||||
return Observable.merge(...displayDataChanges).map(() => {
|
||||
let data = this.productsService.products.slice();
|
||||
|
||||
data = this.filterData(data);
|
||||
|
||||
this.filteredData = [...data];
|
||||
|
||||
data = this.sortData(data);
|
||||
|
||||
// Grab the page's slice of data.
|
||||
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
|
||||
return data.splice(startIndex, this._paginator.pageSize);
|
||||
});
|
||||
}
|
||||
|
||||
filterData(data)
|
||||
{
|
||||
if ( !this.filter )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
return FuseUtils.filterArrayByString(data, this.filter);
|
||||
}
|
||||
|
||||
sortData(data): any[]
|
||||
{
|
||||
if ( !this._sort.active || this._sort.direction === '' )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
return data.sort((a, b) => {
|
||||
let propertyA: number | string = '';
|
||||
let propertyB: number | string = '';
|
||||
|
||||
switch ( this._sort.active )
|
||||
{
|
||||
case 'id':
|
||||
[propertyA, propertyB] = [a.id, b.id];
|
||||
break;
|
||||
case 'name':
|
||||
[propertyA, propertyB] = [a.name, b.name];
|
||||
break;
|
||||
case 'categories':
|
||||
[propertyA, propertyB] = [a.categories[0], b.categories[0]];
|
||||
break;
|
||||
case 'price':
|
||||
[propertyA, propertyB] = [a.priceTaxIncl, b.priceTaxIncl];
|
||||
break;
|
||||
case 'quantity':
|
||||
[propertyA, propertyB] = [a.quantity, b.quantity];
|
||||
break;
|
||||
case 'active':
|
||||
[propertyA, propertyB] = [a.active, b.active];
|
||||
break;
|
||||
}
|
||||
|
||||
const valueA = isNaN(+propertyA) ? propertyA : +propertyA;
|
||||
const valueB = isNaN(+propertyB) ? propertyB : +propertyB;
|
||||
|
||||
return (valueA < valueB ? -1 : 1) * (this._sort.direction === 'asc' ? 1 : -1);
|
||||
});
|
||||
}
|
||||
|
||||
disconnect()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
@Injectable()
|
||||
export class EcommerceProductsService implements Resolve<any>
|
||||
{
|
||||
products: any[];
|
||||
onProductsChanged: BehaviorSubject<any> = new BehaviorSubject({});
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve
|
||||
* @param {ActivatedRouteSnapshot} route
|
||||
* @param {RouterStateSnapshot} state
|
||||
* @returns {Observable<any> | Promise<any> | any}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
|
||||
{
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
Promise.all([
|
||||
this.getProducts()
|
||||
]).then(
|
||||
() => {
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getProducts(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/e-commerce-products')
|
||||
.subscribe((response: any) => {
|
||||
this.products = response;
|
||||
this.onProductsChanged.next(this.products);
|
||||
resolve(response);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -32,6 +32,32 @@ export class NavigationModel
|
|||
'icon' : 'today',
|
||||
'url' : '/apps/calendar'
|
||||
},
|
||||
{
|
||||
'id' : 'e-commerce',
|
||||
'title' : 'E-Commerce',
|
||||
'type' : 'collapse',
|
||||
'icon' : 'shopping_cart',
|
||||
'children': [
|
||||
{
|
||||
'id' : 'dashboard',
|
||||
'title': 'Dashboard',
|
||||
'type' : 'item',
|
||||
'url' : '/apps/e-commerce/dashboard'
|
||||
},
|
||||
{
|
||||
'id' : 'dashboard',
|
||||
'title': 'Products',
|
||||
'type' : 'item',
|
||||
'url' : '/apps/e-commerce/products'
|
||||
},
|
||||
{
|
||||
'id' : 'dashboard',
|
||||
'title': 'Orders',
|
||||
'type' : 'item',
|
||||
'url' : '/apps/e-commerce/orders'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id' : 'mail',
|
||||
'title': 'Mail',
|
||||
|
@ -807,3 +833,4 @@ export class NavigationModel
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user