├── .babelrc
├── .editorconfig
├── .env
├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── background.js
├── ico128.png
├── ico16.png
├── ico48.png
├── index.html
├── logo.f73b2fe8.png
├── manifest.json
└── src.bd191f83.js
├── package.json
├── releases
└── ghtk-ext-0.0.1.zip
├── sc1.png
├── sc2.png
├── src
├── components
│ ├── App
│ │ ├── App.js
│ │ └── index.js
│ ├── Main
│ │ ├── Main.js
│ │ ├── actions.js
│ │ └── index.js
│ ├── Setup
│ │ ├── Setup.js
│ │ ├── actions.js
│ │ └── index.js
│ └── ShipmentFee
│ │ ├── ShipmentFee.js
│ │ ├── actions.js
│ │ └── index.js
├── core
│ ├── assets
│ │ └── images
│ │ │ └── logo.png
│ ├── components
│ │ └── Container
│ │ │ ├── Container.js
│ │ │ └── index.js
│ ├── constants.js
│ └── services
│ │ ├── ghtk.js
│ │ ├── ghtk.spec.js
│ │ └── setting.js
├── index.html
├── index.js
├── public
│ ├── background.js
│ ├── ico128.png
│ ├── ico16.png
│ ├── ico48.png
│ └── manifest.json
├── reducers
│ ├── index.js
│ ├── setting.js
│ └── shipmentFee.js
├── routes.js
└── store.js
├── test
└── setup.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env", "react"],
3 | "plugins": [
4 | "react-hot-loader/babel",
5 | "transform-class-properties",
6 | "transform-object-rest-spread"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | max_line_length = 80
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | max_line_length = 0
15 | trim_trailing_whitespace = false
16 |
17 | [COMMIT_EDITMSG]
18 | max_line_length = 0
19 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/.env
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "parserOptions": {
5 | "ecmaVersion": 2017,
6 | "sourceType": "module"
7 | },
8 | "env": {
9 | "browser": true,
10 | "jest": true
11 | },
12 | "rules": {
13 | "semi": ["off"],
14 | "object-curly-spacing": ["off"],
15 | "react/jsx-filename-extension": ["off"],
16 | "comma-dangle": ["off"],
17 | "space-before-function-paren": ["off"],
18 | "arrow-parens": ["off"],
19 | "function-paren-newline": ["off"],
20 |
21 | "import/no-extraneous-dependencies": ["off"],
22 |
23 | "react/jsx-closing-bracket-location": ["off"]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | .tern*
4 | .cache
5 | coverage
6 | *.DS_Store*
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Nguyễn Nhật Hoàng
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Giao hàng tiết kiệm Extension
2 |
3 | [Chrome Web Store](https://chrome.google.com/webstore/detail/dnccpnibbfhjbhjhhoodjbkkljodpckg)
4 |
5 | Calculate shipment fee of giaohangtietkiem.com service (a local Vietnamese logistic service).
6 |
7 | 
8 |
9 | 
10 |
--------------------------------------------------------------------------------
/dist/background.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | const token = localStorage.getItem('token')
4 | const targetPage = 'https://services.giaohangtietkiem.vn/*'
5 | const addTokenHeader = (e) => {
6 | e.requestHeaders.push({name: 'Token', value: token})
7 | return {requestHeaders: e.requestHeaders}
8 | }
9 |
10 | browser.webRequest.onBeforeSendHeaders.addListener(
11 | addTokenHeader,
12 | {urls: [targetPage]},
13 | ['blocking', 'requestHeaders']
14 | )
15 |
--------------------------------------------------------------------------------
/dist/ico128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/dist/ico128.png
--------------------------------------------------------------------------------
/dist/ico16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/dist/ico16.png
--------------------------------------------------------------------------------
/dist/ico48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/dist/ico48.png
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
Giao Hang Tiet Kiem Extension
--------------------------------------------------------------------------------
/dist/logo.f73b2fe8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/dist/logo.f73b2fe8.png
--------------------------------------------------------------------------------
/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Giao hàng tiết kiệm",
4 | "description": "Giao hàng tiết kiệm",
5 | "version": "0.0.1",
6 | "author": "Hoang Nhat Nguyen ",
7 | "browser_action": {
8 | "default_popup": "index.html",
9 | "default_title": "Open the popup"
10 | },
11 | "icons": {
12 | "16": "ico16.png",
13 | "48": "ico48.png",
14 | "128": "ico128.png"
15 | },
16 | "permissions": [
17 | "webRequest",
18 | "webRequestBlocking",
19 | "https://services.giaohangtietkiem.vn/"
20 | ],
21 | "background": {
22 | "scripts": ["background.js"]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ghtk-ext",
3 | "version": "0.0.1",
4 | "description": "Giao Hang Tiet Kiem Extension",
5 | "author": "Hoang Nhat Nguyen ",
6 | "license": "MIT",
7 | "keywords": [
8 | "reactjs",
9 | "redux",
10 | "react-router",
11 | "parcel-bundler"
12 | ],
13 | "scripts": {
14 | "build": "NODE_ENV=production parcel build src/index.html -d dist --detailed-report --no-source-maps",
15 | "clean": "rimraf dist",
16 | "precommit": "lint-staged",
17 | "prebuild": "yarn run clean && mkdir dist && cp -R src/public/* dist",
18 | "start": "parcel ./src/index.html",
19 | "test": "jest",
20 | "test:watch": "jest --watch",
21 | "test:coverage": "jest --coverage"
22 | },
23 | "lint-staged": {
24 | "*.js": [
25 | "eslint src"
26 | ]
27 | },
28 | "jest": {
29 | "setupTestFrameworkScriptFile": "/test/setup.js",
30 | "testEnvironment": "node"
31 | },
32 | "dependencies": {
33 | "@material-ui/core": "^1.3.1",
34 | "@material-ui/icons": "^1.1.0",
35 | "axios": "^0.18.0",
36 | "babel-polyfill": "^6.26.0",
37 | "history": "^4.7.2",
38 | "prop-types": "^15.6.1",
39 | "react": ">=16.0.0",
40 | "react-dom": ">=16.0.0",
41 | "react-redux": "^5.0.7",
42 | "react-router": "^4.3.1",
43 | "react-router-dom": "^4.3.1",
44 | "react-router-redux": "^5.0.0-alpha.9",
45 | "redux": "^4.0.0",
46 | "redux-thunk": "^2.3.0",
47 | "styled-components": "^3.3.3"
48 | },
49 | "devDependencies": {
50 | "babel-eslint": "^8.2.3",
51 | "babel-jest": "^23.0.1",
52 | "babel-plugin-transform-class-properties": "^6.24.1",
53 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
54 | "babel-preset-env": "^1.7.0",
55 | "babel-preset-react": "^6.24.1",
56 | "enzyme": "^3.3.0",
57 | "enzyme-adapter-react-16": "^1.1.1",
58 | "eslint": "^4.19.1",
59 | "eslint-config-airbnb": "^16.1.0",
60 | "eslint-plugin-import": "^2.12.0",
61 | "eslint-plugin-jsx-a11y": "^6.0.3",
62 | "eslint-plugin-react": "^7.9.1",
63 | "husky": "^0.14.3",
64 | "jest": "^23.1.0",
65 | "jest-cli": "^23.1.0",
66 | "lint-staged": "^7.2.0",
67 | "parcel-bundler": "^1.9.0",
68 | "react-addons-test-utils": "^15.6.2",
69 | "react-hot-loader": "^4.3.3",
70 | "react-test-renderer": "^16.4.1",
71 | "redux-devtools-extension": "^2.13.5",
72 | "redux-mock-store": "^1.5.3",
73 | "rimraf": "^2.6.2"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/releases/ghtk-ext-0.0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/releases/ghtk-ext-0.0.1.zip
--------------------------------------------------------------------------------
/sc1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/sc1.png
--------------------------------------------------------------------------------
/sc2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/sc2.png
--------------------------------------------------------------------------------
/src/components/App/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | import {MuiThemeProvider, createMuiTheme} from '@material-ui/core/styles'
6 |
7 | import LogoImage from '../../core/assets/images/logo.png'
8 |
9 | const theme = createMuiTheme()
10 |
11 | const Wrapper = styled.div`
12 | padding: 10px;
13 | `
14 | const Logo = styled.img`
15 | height: 50px;
16 | `
17 |
18 | const App = ({children}) => (
19 |
20 |
21 |
22 | {children}
23 |
24 |
25 | )
26 |
27 | App.propTypes = {
28 | children: PropTypes.node
29 | }
30 |
31 | App.defaultProps = {
32 | children: null
33 | }
34 |
35 | export default App
36 |
--------------------------------------------------------------------------------
/src/components/App/index.js:
--------------------------------------------------------------------------------
1 | import App from './App'
2 |
3 | export default App
4 |
--------------------------------------------------------------------------------
/src/components/Main/Main.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import Setup from '../Setup'
5 | import ShipmentFee from '../ShipmentFee'
6 |
7 | export default class Main extends Component {
8 | static propTypes = {
9 | setting: PropTypes.shape({
10 | defaultSender: PropTypes.string,
11 | token: PropTypes.string
12 | }).isRequired,
13 | initApp: PropTypes.func.isRequired
14 | }
15 |
16 | constructor(props) {
17 | super(props)
18 |
19 | props.initApp()
20 | }
21 |
22 | render() {
23 | const {defaultSender, token} = this.props.setting
24 | const isSettingOk = defaultSender && token
25 |
26 | return (
27 |
28 | {isSettingOk ? : }
29 |
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/Main/actions.js:
--------------------------------------------------------------------------------
1 | import {ActionTypes} from '../../core/constants'
2 | import settingService from '../../core/services/setting'
3 |
4 | export const initApp = () => {
5 | const isSettingOk = settingService.isSettingOk()
6 |
7 | if (isSettingOk) {
8 | return {
9 | type: ActionTypes.INIT_APP_SUCCESS,
10 | payload: settingService.getSetting()
11 | }
12 | }
13 |
14 | return {
15 | type: ActionTypes.INIT_APP_FAILED
16 | }
17 | }
18 |
19 | export default {
20 | initApp
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/Main/index.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux'
2 |
3 | import Main from './Main'
4 | import {initApp} from './actions'
5 |
6 | export default connect(
7 | ({setting}) => ({setting}),
8 | {initApp}
9 | )(Main)
10 |
--------------------------------------------------------------------------------
/src/components/Setup/Setup.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | import TextField from '@material-ui/core/TextField'
6 | import Button from '@material-ui/core/Button'
7 |
8 | const InputWrapper = styled.div`
9 | margin-top: 10px;
10 | `
11 | const ActionsWrapper = styled.div`
12 | margin-top: 10px;
13 | `
14 |
15 | export default class Setup extends Component {
16 | static propTypes = {
17 | setting: PropTypes.shape({
18 | defaultSender: PropTypes.string,
19 | token: PropTypes.string
20 | }).isRequired,
21 | saveSetting: PropTypes.func.isRequired
22 | }
23 |
24 | constructor(props) {
25 | super(props)
26 |
27 | const {defaultSender, token} = props.setting
28 |
29 | this.state = {
30 | defaultSender,
31 | token
32 | }
33 | }
34 |
35 | handleChange = (name) => (event) => {
36 | this.setState({
37 | [name]: event.target.value
38 | })
39 | }
40 |
41 | handleClick = () => {
42 | const {defaultSender, token} = this.state
43 |
44 | this.props.saveSetting({
45 | defaultSender,
46 | token
47 | })
48 | }
49 |
50 | render() {
51 | return (
52 |
53 |
54 |
61 |
62 |
63 |
70 |
71 |
72 |
78 |
79 |
80 | )
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/components/Setup/actions.js:
--------------------------------------------------------------------------------
1 | import {ActionTypes} from '../../core/constants'
2 | import settingService from '../../core/services/setting'
3 |
4 | export const saveSetting = ({defaultSender, token}) => {
5 | settingService.saveSetting({defaultSender, token})
6 |
7 | return {
8 | type: ActionTypes.SAVE_SETTING,
9 | payload: {
10 | defaultSender,
11 | token
12 | }
13 | }
14 | }
15 |
16 | export default {
17 | saveSetting
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/Setup/index.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux'
2 |
3 | import Setup from './Setup'
4 | import {saveSetting} from './actions'
5 |
6 | export default connect(
7 | ({setting}) => ({setting}),
8 | {saveSetting}
9 | )(Setup)
10 |
--------------------------------------------------------------------------------
/src/components/ShipmentFee/ShipmentFee.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | import TextField from '@material-ui/core/TextField'
6 | import Typography from '@material-ui/core/Typography'
7 |
8 | const InputWrapper = styled.div`
9 | margin-top: 10px;
10 | `
11 | const ShipmentFeeWrapper = styled.div`
12 | margin-top: 20px;
13 | `
14 |
15 | export default class ShipmentFee extends Component {
16 | static propTypes = {
17 | setting: PropTypes.shape({
18 | defaultSender: PropTypes.string,
19 | token: PropTypes.string
20 | }).isRequired,
21 | shipmentFee: PropTypes.number,
22 | getShipmentFee: PropTypes.func.isRequired,
23 | resetShipmentFee: PropTypes.func.isRequired
24 | }
25 |
26 | static defaultProps = {
27 | shipmentFee: null
28 | }
29 |
30 | constructor(props) {
31 | super(props)
32 |
33 | const {defaultSender} = props.setting
34 |
35 | this.state = {
36 | sender: defaultSender,
37 | receiver: ''
38 | }
39 | }
40 |
41 | handleChange = (name) => (event) => {
42 | this.props.resetShipmentFee()
43 | this.setState({
44 | [name]: event.target.value
45 | })
46 | }
47 |
48 | handleKeyPress = (name) => (event) => {
49 | if (event.charCode === 13) {
50 | switch (name) {
51 | case 'receiver': {
52 | const {sender, receiver} = this.state
53 |
54 | this.props.getShipmentFee(sender, receiver)
55 |
56 | break
57 | }
58 | default:
59 | break
60 | }
61 | }
62 | }
63 |
64 | render() {
65 | const {shipmentFee} = this.props
66 |
67 | return (
68 |
69 |
70 |
77 |
78 |
79 |
87 |
88 |
89 |
90 | {`Phí ship tạm tính: ${shipmentFee ? `${shipmentFee} VND` : '-'}`}
91 |
92 |
93 |
94 | )
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/components/ShipmentFee/actions.js:
--------------------------------------------------------------------------------
1 | import {ActionTypes} from '../../core/constants'
2 | import ghtkService from '../../core/services/ghtk'
3 |
4 | export const getShipmentFee = (sender, receiver) => async (dispatch) => {
5 | const [pickDistrict, pickProvince] = sender
6 | .split(',')
7 | .map((str) => str.trim())
8 | const [district, province] = receiver.split(',').map((str) => str.trim())
9 | const shipmentFee = await ghtkService.getShipmentFee({
10 | pickDistrict,
11 | pickProvince,
12 | district,
13 | province
14 | })
15 |
16 | dispatch({
17 | type: ActionTypes.GET_SHIPMENT_FEE_SUCCESS,
18 | payload: shipmentFee
19 | })
20 | }
21 |
22 | export const resetShipmentFee = () => ({
23 | type: ActionTypes.RESET_SHIPMENT_FEE
24 | })
25 |
26 | export default {
27 | getShipmentFee
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/ShipmentFee/index.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux'
2 |
3 | import ShipmentFee from './ShipmentFee'
4 | import {getShipmentFee, resetShipmentFee} from './actions'
5 |
6 | export default connect(
7 | ({shipmentFee}) => ({shipmentFee}),
8 | {getShipmentFee, resetShipmentFee}
9 | )(ShipmentFee)
10 |
--------------------------------------------------------------------------------
/src/core/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/src/core/assets/images/logo.png
--------------------------------------------------------------------------------
/src/core/components/Container/Container.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Provider} from 'react-redux'
3 | import {ConnectedRouter} from 'react-router-redux'
4 |
5 | import {historyShape, storeShape} from '../../constants'
6 | import createRoutes from '../../../routes'
7 |
8 | export default class Container extends Component {
9 | static propTypes = {
10 | history: historyShape.isRequired,
11 | store: storeShape.isRequired
12 | }
13 |
14 | getRoutes = () => {
15 | if (!this.routes) {
16 | this.routes = createRoutes()
17 | }
18 |
19 | return this.routes
20 | }
21 |
22 | render() {
23 | const {store, history} = this.props
24 |
25 | return (
26 |
27 | {this.getRoutes()}
28 |
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/core/components/Container/index.js:
--------------------------------------------------------------------------------
1 | import Container from './Container'
2 |
3 | export default Container
4 |
--------------------------------------------------------------------------------
/src/core/constants.js:
--------------------------------------------------------------------------------
1 | import {func, object, shape, string, number, bool} from 'prop-types'
2 |
3 | export const ActionTypes = {
4 | GET_SHIPMENT_FEE_SUCCESS: 'GET_SHIPMENT_FEE_SUCCESS',
5 | RESET_SHIPMENT_FEE: 'RESET_SHIPMENT_FEE',
6 | SAVE_SETTING: 'SAVE_SETTING',
7 | INIT_APP_SUCCESS: 'INIT_APP_SUCCESS',
8 | INIT_APP_FAILED: 'INIT_APP_FAILED'
9 | }
10 |
11 | export const locationShape = shape({
12 | pathname: string.isRequired,
13 | search: string.isRequired,
14 | hash: string.isRequired,
15 | state: object
16 | })
17 |
18 | export const historyShape = shape({
19 | action: string.isRequired,
20 | block: func.isRequired,
21 | createHref: func.isRequired,
22 | go: func.isRequired,
23 | goBack: func.isRequired,
24 | goForward: func.isRequired,
25 | listen: func.isRequired,
26 | length: number.isRequired,
27 | push: func.isRequired,
28 | replace: func.isRequired,
29 | location: locationShape.isRequired
30 | })
31 |
32 | export const matchShape = shape({
33 | isexact: bool.isrequired,
34 | path: string.isrequired,
35 | url: string.isrequired,
36 | params: object
37 | })
38 |
39 | export const storeShape = shape({
40 | routing: object,
41 | counter: number
42 | })
43 |
--------------------------------------------------------------------------------
/src/core/services/ghtk.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | import settingService from './setting'
4 |
5 | const GHTK_API_URL = 'https://services.giaohangtietkiem.vn'
6 | const GET_SHIPMENT_FEE = '/services/shipment/fee'
7 |
8 | axios.defaults.baseURL = GHTK_API_URL
9 | axios.defaults.headers.common.Token = settingService.getSetting().token
10 |
11 | const getShipmentFee = async ({
12 | pickProvince,
13 | pickDistrict,
14 | province,
15 | district
16 | }) => {
17 | const {data} = await axios({
18 | url: GET_SHIPMENT_FEE,
19 | method: 'get',
20 | params: {
21 | pick_province: pickProvince,
22 | pick_district: pickDistrict,
23 | province,
24 | district
25 | }
26 | })
27 |
28 | return data.fee.fee
29 | }
30 |
31 | export default {
32 | getShipmentFee
33 | }
34 |
--------------------------------------------------------------------------------
/src/core/services/ghtk.spec.js:
--------------------------------------------------------------------------------
1 | import ghtkService from './ghtk'
2 |
3 | describe('ghtkService', () => {
4 | test('getShipmentFee should return', async () => {
5 | const shipmentFee = await ghtkService.getShipmentFee({
6 | pickProvince: 'Ho Chi Minh',
7 | pickDistrict: 'Binh Thanh',
8 | province: 'Ho Chi Minh',
9 | district: 'Quan 11'
10 | })
11 |
12 | expect(shipmentFee).toBeDefined()
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/src/core/services/setting.js:
--------------------------------------------------------------------------------
1 | const saveSetting = ({defaultSender, token}) => {
2 | localStorage.setItem('defaultSender', defaultSender)
3 | localStorage.setItem('token', token)
4 | }
5 |
6 | const getSetting = () => {
7 | const defaultSender = localStorage.getItem('defaultSender')
8 | const token = localStorage.getItem('token')
9 |
10 | return {
11 | defaultSender,
12 | token
13 | }
14 | }
15 |
16 | const isSettingOk = () => {
17 | const defaultSender = localStorage.getItem('defaultSender')
18 | const token = localStorage.getItem('token')
19 |
20 | return defaultSender && token
21 | }
22 |
23 | export default {
24 | getSetting,
25 | isSettingOk,
26 | saveSetting
27 | }
28 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Giao Hang Tiet Kiem Extension
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill'
2 |
3 | import React from 'react'
4 | import ReactDOM from 'react-dom'
5 | import createHistory from 'history/createBrowserHistory'
6 |
7 | import {configureStore} from './store'
8 | import Container from './core/components/Container'
9 |
10 | const root = document.getElementById('root')
11 | const history = createHistory()
12 | const store = configureStore({history})
13 | const render = (Component, props = {}) => {
14 | ReactDOM.render(, root)
15 | }
16 |
17 | render(Container, {history, store})
18 |
--------------------------------------------------------------------------------
/src/public/background.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | const token = localStorage.getItem('token')
4 | const targetPage = 'https://services.giaohangtietkiem.vn/*'
5 | const addTokenHeader = (e) => {
6 | e.requestHeaders.push({name: 'Token', value: token})
7 | return {requestHeaders: e.requestHeaders}
8 | }
9 |
10 | browser.webRequest.onBeforeSendHeaders.addListener(
11 | addTokenHeader,
12 | {urls: [targetPage]},
13 | ['blocking', 'requestHeaders']
14 | )
15 |
--------------------------------------------------------------------------------
/src/public/ico128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/src/public/ico128.png
--------------------------------------------------------------------------------
/src/public/ico16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/src/public/ico16.png
--------------------------------------------------------------------------------
/src/public/ico48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeaholicguy/ghtk-ext/472679924deb591c9da59505e0df093b8d048c77/src/public/ico48.png
--------------------------------------------------------------------------------
/src/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Giao hàng tiết kiệm",
4 | "description": "Giao hàng tiết kiệm",
5 | "version": "0.0.1",
6 | "author": "Hoang Nhat Nguyen ",
7 | "browser_action": {
8 | "default_popup": "index.html",
9 | "default_title": "Open the popup"
10 | },
11 | "icons": {
12 | "16": "ico16.png",
13 | "48": "ico48.png",
14 | "128": "ico128.png"
15 | },
16 | "permissions": [
17 | "webRequest",
18 | "webRequestBlocking",
19 | "https://services.giaohangtietkiem.vn/"
20 | ],
21 | "background": {
22 | "scripts": ["background.js"]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import {combineReducers} from 'redux'
2 | import {routerReducer} from 'react-router-redux'
3 |
4 | import shipmentFee from './shipmentFee'
5 | import setting from './setting'
6 |
7 | export default combineReducers({
8 | routing: routerReducer,
9 | shipmentFee,
10 | setting
11 | })
12 |
--------------------------------------------------------------------------------
/src/reducers/setting.js:
--------------------------------------------------------------------------------
1 | import {ActionTypes} from '../core/constants'
2 |
3 | const initialState = {
4 | defaultSender: '',
5 | token: ''
6 | }
7 |
8 | export default function(state = initialState, {type, payload}) {
9 | switch (type) {
10 | case ActionTypes.SAVE_SETTING:
11 | case ActionTypes.INIT_APP_SUCCESS:
12 | return {...state, ...payload}
13 | case ActionTypes.INIT_APP_FAILED:
14 | return initialState
15 | default:
16 | return state
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/reducers/shipmentFee.js:
--------------------------------------------------------------------------------
1 | import {ActionTypes} from '../core/constants'
2 |
3 | const initialState = null
4 |
5 | export default function(state = initialState, {type, payload}) {
6 | switch (type) {
7 | case ActionTypes.GET_SHIPMENT_FEE_SUCCESS:
8 | return payload
9 | case ActionTypes.RESET_SHIPMENT_FEE:
10 | return initialState
11 | default:
12 | return state
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {BrowserRouter as Router, Switch, Route} from 'react-router-dom'
3 |
4 | import App from './components/App'
5 | import Main from './components/Main'
6 |
7 | const createRoutes = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | )
16 |
17 | export default createRoutes
18 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import {createStore, applyMiddleware} from 'redux'
2 | import {routerMiddleware} from 'react-router-redux'
3 | import thunk from 'redux-thunk'
4 | import {composeWithDevTools} from 'redux-devtools-extension'
5 |
6 | import rootReducer from './reducers'
7 |
8 | export function configureStore({initialState, history}) {
9 | const router = routerMiddleware(history)
10 | const middlewares = [thunk, router]
11 |
12 | let createStoreWithMiddleware
13 | if (process.env.NODE_ENV === 'production') {
14 | createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore)
15 | } else {
16 | createStoreWithMiddleware = composeWithDevTools(
17 | applyMiddleware(...middlewares)
18 | )(createStore)
19 | }
20 | const store = createStoreWithMiddleware(rootReducer, initialState)
21 |
22 | return store
23 | }
24 |
25 | export default {
26 | configureStore
27 | }
28 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | import {configure} from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 |
4 | configure({adapter: new Adapter()})
5 |
--------------------------------------------------------------------------------