├── .babelrc
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── README.md
├── config.js
├── deploy.config.js
├── manifest.json
├── package.json
├── server.js
├── src
├── css
│ ├── index.styl
│ ├── page
│ │ └── SAT.styl
│ └── variable.styl
├── favicon.ico
├── image
│ └── favicon-144x144.png
├── index.ejs
├── index.js
├── js
│ ├── actions
│ │ ├── SAT.js
│ │ ├── Session.js
│ │ └── index.js
│ ├── api
│ │ ├── Base.js
│ │ ├── Firebase.js
│ │ └── index.js
│ ├── components
│ │ ├── App.jsx
│ │ ├── App.styl
│ │ ├── Base.jsx
│ │ ├── FirebaseTest.jsx
│ │ ├── Login.jsx
│ │ ├── Login.styl
│ │ ├── NotFound.jsx
│ │ ├── SAT.jsx
│ │ ├── chart.jsx
│ │ ├── chart.styl
│ │ ├── common
│ │ │ ├── Footer.jsx
│ │ │ ├── Nav.jsx
│ │ │ └── index.js
│ │ └── index.js
│ ├── containers
│ │ ├── App.js
│ │ ├── Base.js
│ │ ├── Chart.js
│ │ ├── DevTools.js
│ │ ├── FirebaseTest.js
│ │ ├── Login.js
│ │ ├── SAT.js
│ │ ├── common
│ │ │ ├── Nav.js
│ │ │ └── index.js
│ │ └── index.js
│ ├── reducers
│ │ ├── Base.js
│ │ ├── SAT.js
│ │ ├── Session.js
│ │ └── index.js
│ ├── routes.js
│ ├── stores
│ │ └── Store.js
│ └── utils
│ │ └── Base.js
└── service-worker.js
├── sw-precache-config.js
├── webpack.config.js
└── webpack.production.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "react",
4 | "es2015",
5 | "stage-2"
6 | ],
7 | "env": {
8 | "development": {
9 | "plugins": [
10 | "transform-decorators-legacy",
11 | [
12 | "react-transform",
13 | {
14 | "transforms": [
15 | {
16 | "transform": "react-transform-hmr",
17 | "imports": [
18 | "react"
19 | ],
20 | "locals": [
21 | "module"
22 | ]
23 | },
24 | {
25 | "transform": "react-transform-catch-errors",
26 | "imports": [
27 | "react",
28 | "redbox-react"
29 | ]
30 | }
31 | ]
32 | }
33 | ]
34 | ]
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/*
2 | node_modules/*
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | parserOptions: {
5 | sourceType: 'module'
6 | },
7 | extends: "standard",
8 | plugins: ["react"],
9 | env: {
10 | 'browser': true
11 | },
12 | rules: {
13 | "react/jsx-uses-react": "error",
14 | "react/jsx-uses-vars": "error",
15 | 'strict': 0,
16 | 'arrow-parens': 0,
17 | 'indent': [ 2, 4, { 'SwitchCase': 1 } ],
18 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | dist/
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### React-Redux-Template
2 | ---
3 | - React
4 | - Webpack
5 | - Babel
6 | - ES6
7 | - Alt (flux implementation)
8 |
9 | ### How to use
10 | ---
11 | - npm install
12 | - npm run dev
13 |
14 | ### Production
15 | ---
16 | - npm install
17 | - npm run build
18 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | publicPath: ''
3 | }
4 | module.exports = config
5 |
--------------------------------------------------------------------------------
/deploy.config.js:
--------------------------------------------------------------------------------
1 |
SAT.me
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-pwa-boilerplate",
3 | "name": "react-pwa-boilerplate",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "144x144",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./",
12 | "display": "standalone"
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-boilerplate",
3 | "version": "1.0.0",
4 | "description": "--- - React - Webpack - Babel - ES6 - Alt (flux implementation)",
5 | "main": "index.js",
6 | "dependencies": {
7 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
8 | "bootstrap": "^3.3.7",
9 | "chart.js": "^2.4.0",
10 | "file-loader": "^0.9.0",
11 | "firebase": "^3.6.1",
12 | "history": "^4.3.0",
13 | "immutable": "^3.8.1",
14 | "js-sorting": "^3.0.4",
15 | "query-string": "^4.2.3",
16 | "react": "^15.4.1",
17 | "react-chartjs": "^0.8.0",
18 | "react-chartjs-2": "^1.5.1",
19 | "react-cookie": "^1.0.3",
20 | "react-css-modules": "^4.0.3",
21 | "react-dom": "^15.4.1",
22 | "react-facebook-login": "^3.3.3",
23 | "react-router": "^3.0.0",
24 | "react-router-redux": "^4.0.6",
25 | "reactfire": "^1.0.0",
26 | "redux-actions": "^0.12.0",
27 | "redux-promise": "^0.5.3",
28 | "redux-react-firebase": "^2.3.3",
29 | "redux-thunk": "^2.1.0",
30 | "sorted-array": "^2.0.1",
31 | "sw-precache": "^4.2.3",
32 | "sweetalert-react": "^0.4.6",
33 | "url-join": "^1.1.0",
34 | "url-loader": "^0.5.7"
35 | },
36 | "devDependencies": {
37 | "autoprefixer": "^6.5.1",
38 | "babel-core": "^6.18.0",
39 | "babel-eslint": "^7.1.0",
40 | "babel-loader": "^6.2.7",
41 | "babel-plugin-react-transform": "^2.0.2",
42 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
43 | "babel-plugin-transform-runtime": "^6.15.0",
44 | "babel-preset-es2015": "^6.18.0",
45 | "babel-preset-react": "^6.16.0",
46 | "babel-preset-react-hmre": "^1.1.1",
47 | "babel-preset-stage-2": "^6.18.0",
48 | "babel-register": "^6.18.0",
49 | "babel-root-import": "^4.1.3",
50 | "babel-runtime": "^6.18.0",
51 | "connect-history-api-fallback": "^1.3.0",
52 | "css-loader": "^0.25.0",
53 | "eslint": "^3.8.1",
54 | "eslint-config-standard": "^6.2.1",
55 | "eslint-friendly-formatter": "^2.0.6",
56 | "eslint-loader": "^1.6.0",
57 | "eslint-plugin-html": "^1.5.5",
58 | "eslint-plugin-promise": "^3.3.0",
59 | "eslint-plugin-react": "^6.4.1",
60 | "eslint-plugin-standard": "^2.0.1",
61 | "express": "^4.14.0",
62 | "extract-text-webpack-plugin": "^1.0.1",
63 | "file": "^0.2.2",
64 | "html-webpack-plugin": "^2.24.0",
65 | "http-proxy-middleware": "^0.17.2",
66 | "node-sass": "^3.10.1",
67 | "react-transform-catch-errors": "^1.0.2",
68 | "redbox-react": "^1.3.2",
69 | "redux-devtools": "^3.3.1",
70 | "redux-devtools-dock-monitor": "^1.1.1",
71 | "redux-devtools-log-monitor": "^1.1.1",
72 | "sass-loader": "^4.0.2",
73 | "style-loader": "^0.13.1",
74 | "stylus": "^0.54.5",
75 | "stylus-loader": "^2.3.1",
76 | "webpack": "^1.13.3",
77 | "webpack-dev-middleware": "^1.8.4",
78 | "webpack-hot-middleware": "^2.13.1",
79 | "webpack-notifier": "^1.4.1"
80 | },
81 | "scripts": {
82 | "start": "node server.js",
83 | "lint": "eslint src",
84 | "build": "NODE_ENV=production webpack -p --progress --config ./webpack.production.config.js && sw-precache --config=sw-precache-config.js",
85 | "dev": "NODE_ENV=development node server.js"
86 | },
87 | "repository": {
88 | "type": "git",
89 | "url": "git+https://github.com/allenwhale/react-redux-boilerplate.git"
90 | },
91 | "author": "allenwhale",
92 | "license": "MIT",
93 | "bugs": {
94 | "url": "https://github.com/allenwhale/react-redux-boilerplate/issues"
95 | },
96 | "homepage": "https://github.com/allenwhale/react-redux-boilerplate#readme"
97 | }
98 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var express = require('express')
3 | var webpack = require('webpack')
4 | var config = require('./webpack.config')
5 | var app = express()
6 | var compiler = webpack(config)
7 | const port = process.env.PORT || 3000
8 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
9 | stats: {
10 | colors: true,
11 | hash: false,
12 | timings: true,
13 | chunks: false,
14 | chunkModules: false,
15 | modules: false
16 | },
17 | publicPath: config.output.publicPath
18 | })
19 |
20 | var hotMiddleware = require('webpack-hot-middleware')(compiler)
21 | compiler.plugin('compilation', function (compilation) {
22 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
23 | hotMiddleware.publish({ action: 'reload' })
24 | cb()
25 | })
26 | })
27 | app.use(require('connect-history-api-fallback')())
28 | app.use(devMiddleware)
29 | app.use(hotMiddleware)
30 |
31 | app.get('*', function (req, res) {
32 | res.sendFile(path.join(__dirname, '/src/index.html'))
33 | })
34 |
35 | app.listen(port, '0.0.0.0', function (err) {
36 | if (err) {
37 | console.log(err)
38 | return
39 | }
40 | console.log(`Listening at http://0.0.0.0:${port}`)
41 | })
42 |
--------------------------------------------------------------------------------
/src/css/index.styl:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/earlyaccess/notosanstc.css')
2 | @import url('https://fonts.googleapis.com/css?family=Open+Sans')
3 | $black = #50514F
4 | $blue = #D8EAF9
5 | $white = #F0F0F0
6 | $fb_color = rgba(#3b5998, 0.8)
7 | $bg-color= #ff8888 //start color
8 | $stops= 100 //smoothness
9 | $time= 20s //duration of animation
10 | $hue-range= 20 //of 360deg
11 |
12 | *
13 | font-family 'Noto Sans TC', sans-serif
14 | h1.not_login_logo
15 | color $white !important
16 | font-size: 2.5em !important
17 | body
18 | margin 0px
19 | background-color $blue
20 | font-family 'Open Sans', sans-serif
21 | #app
22 | background-color $blue
23 | color $black
24 | h1
25 | font-size 36px
26 | .control-label
27 | font-size 24px
28 | font-weight 100
29 | .form-control
30 | height 50px
31 | color $black
32 | font-size 20px
33 | background transparent
34 | border-color darken($blue, 20%)
35 | border-width 2px
36 |
37 | .testFunction
38 | button
39 | padding 5px 10px
40 | margin 0 10px
41 | font-size 16px
42 | background $blue
43 | color $black
44 | border 2px solid $black
45 | border-radius 2.5px
46 |
47 | h1
48 | color $black
49 | font-weight 400
50 | font-size 3em !important
51 |
52 | h2
53 | color $black
54 | font-weight 400
55 | font-size 2.5em !important
56 | position relative
57 | padding-bottom 20px
58 | &:after
59 | content ""
60 | width 50px
61 | height 5px
62 | background-color $black
63 | border-radius 1px
64 | position absolute
65 | bottom 10px
66 | left 0
67 |
68 | .sat_btn
69 | color $black
70 | font-size 1.25em
71 | font-weight 400
72 | border 2px solid rgba(80,80,80, 0.75)
73 | border-radius 2.5px
74 | padding 5px 10px
75 | margin 10px 10px 0 0
76 | cursor pointer
77 | background transparent
78 | &:hover
79 | cursor pointer
80 |
81 | .fb_btn
82 | border 2px solid $fb_color
83 | color $fb_color
84 | .save_btn
85 | border none
86 | color: $white
87 | font-size: 1em
88 | font-weight: 400
89 | span
90 | color $fb_color
91 | .sweet-alert
92 | h2
93 | &:after
94 | display: none
95 | p
96 | max-height: 320px
97 | overflow: scroll
98 |
--------------------------------------------------------------------------------
/src/css/page/SAT.styl:
--------------------------------------------------------------------------------
1 | // $black = #50514F
2 | // .form-control
3 | // height 50px
4 | // font-size 16px
5 | // background transparent
6 | // border-width 2px
7 | // color red
8 |
--------------------------------------------------------------------------------
/src/css/variable.styl:
--------------------------------------------------------------------------------
1 | $black = #50514F
2 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lichin-lin/react-SAT/aeb16bfe80240aea5f7b9d06af2bce27ba77e7f3/src/favicon.ico
--------------------------------------------------------------------------------
/src/image/favicon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lichin-lin/react-SAT/aeb16bfe80240aea5f7b9d06af2bce27ba77e7f3/src/image/favicon-144x144.png
--------------------------------------------------------------------------------
/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
23 |
24 |
25 |
26 |
27 |
35 |
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { Provider } from 'react-redux'
4 | import RootRouter from './js/routes'
5 | import store from './js/stores/Store'
6 | import { chr, ord, mapArrayToObject, mapObject } from './js/utils/Base'
7 | require('bootstrap/dist/css/bootstrap.min.css')
8 | require('./css/index.styl')
9 |
10 | if (typeof (document) !== 'undefined' && window) {
11 | window.chr = chr
12 | window.ord = ord
13 | window.mapArrayToObject = mapArrayToObject
14 | window.mapObject = mapObject
15 | window.onload = () => {
16 | return render(
17 |
18 |
19 | ,
20 | document.getElementById('app')
21 | )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/js/actions/SAT.js:
--------------------------------------------------------------------------------
1 | import { createAction } from 'redux-actions'
2 | import Api from 'js/api'
3 |
4 | export default {
5 | getScoreData: createAction('getScoreData', Api.Firebase.getScoreData),
6 | getYearData: createAction('getYearData', Api.Firebase.getYearData),
7 | updateUserAvg: createAction('updateUserAvg', Api.Firebase.updateUserAvg),
8 | getUserTotalYearData: createAction('getUserTotalYearData', Api.Firebase.getUserTotalYearData),
9 | updateUserScore: createAction('updateUserScore', Api.Firebase.updateUserScore)
10 | }
11 |
--------------------------------------------------------------------------------
/src/js/actions/Session.js:
--------------------------------------------------------------------------------
1 | import { createAction } from 'redux-actions'
2 | import Api from 'js/api'
3 | export default {
4 | FBLogin: createAction('FBLogin', Api.Firebase.FBLogin),
5 | FBLogout: createAction('FBLogout', Api.Firebase.FBLogout),
6 | CookieLogin: createAction('CookieLogin', function (data) {
7 | return new Promise(function (resolve, reject) {
8 | resolve(data)
9 | })
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/src/js/actions/index.js:
--------------------------------------------------------------------------------
1 | import Session from './Session'
2 | import SAT from './SAT'
3 | export default {
4 | Session,
5 | SAT
6 | }
7 |
--------------------------------------------------------------------------------
/src/js/api/Base.js:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-fetch'
2 | import qs from 'query-string'
3 |
4 | const combinePayload = (res: Promise, payload?: { [key: string]: any }) => {
5 | return Promise.resolve(
6 | res.json().then((r) => ({
7 | ...r,
8 | payload
9 | }))
10 | )
11 | }
12 |
13 | export default (url: string, options?: { [key: string]: any }={}) => {
14 | const URL = url
15 | let body = {}
16 | options.credentials = 'includes'
17 | if (options.method && options.method.toLowerCase() !== 'get') {
18 | body = options.body || {}
19 | } else {
20 | const element = document.createElement('a')
21 | element.href = URL
22 | body = qs.parse(element.search)
23 | }
24 |
25 | return fetch(URL, options).then((res) => {
26 | if (res.status >= 200 && res.status < 300) {
27 | return res.json()
28 | } else {
29 | return Promise.reject(combinePayload(res.json(), body))
30 | }
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/src/js/api/Firebase.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase'
2 | // import ApiFetch from './Base'
3 | // import urlJoin from 'url-join'
4 |
5 | export default {
6 | FBLogin: function (data) {
7 | var provider = new firebase.auth.FacebookAuthProvider()
8 | return firebase.auth().signInWithPopup(provider)
9 | },
10 | FBLogout: function (data) {
11 | return firebase.auth().signOut()
12 | },
13 | getScoreData: function (index) {
14 | var userId = firebase.auth().currentUser.uid
15 | console.log('in api, get score', userId, index)
16 | return firebase.database().ref('/users/' + userId + '/init/' + index).once('value').then(function (snapshot) {
17 | return snapshot.val()
18 | })
19 | },
20 | getYearData: function (index) {
21 | // let data = '1'
22 | return firebase.database().ref('/table/' + index).once('value').then(function (snapshot) {
23 | return snapshot.val()
24 | })
25 | },
26 | getUserTotalYearData: function () {
27 | var userId = firebase.auth().currentUser.uid
28 | console.log(userId)
29 | return firebase.database().ref('/users/' + userId).once('value').then(function (snapshot) {
30 | return snapshot.val()
31 | })
32 | },
33 | updateUserScore: function (path, index, data) {
34 | var userId = firebase.auth().currentUser.uid
35 | firebase.database().ref('users/' + userId + '/' + path + '/' + index).set({
36 | Chinese: data.Chinese,
37 | English: data.English,
38 | Math: data.Math,
39 | Society: data.Society,
40 | Science: data.Science
41 | })
42 | },
43 | updateUserAvg: function (avg) {
44 | console.log('cout avg: ', avg)
45 | var userId = firebase.auth().currentUser.uid
46 | firebase.database().ref('users/' + userId + '/').update({
47 | avg: avg
48 | })
49 | },
50 | isUserLogin: function () {
51 | var user = firebase.auth().currentUser
52 | console.log('user:', user)
53 | if (user) {
54 | console.log('From firebase api: current user: ', user)
55 | return user
56 | } else {
57 | console.log('From firebase api: no current user')
58 | return 0
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/js/api/index.js:
--------------------------------------------------------------------------------
1 | import Firebase from './Firebase'
2 |
3 | export default {
4 | Firebase
5 | }
6 |
--------------------------------------------------------------------------------
/src/js/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Containers from 'js/containers'
3 | // import Components from 'js/components'
4 | import CSSModules from 'react-css-modules'
5 |
6 | export default CSSModules(class extends Component {
7 | componentDidMount () {
8 | }
9 | render () {
10 | return (
11 |
12 |
13 |
14 | { this.props.children }
15 |
16 | { process.env.NODE_ENV !== 'production' ?
: null }
17 |
18 | )
19 | }
20 | }, require('./App.styl'))
21 |
--------------------------------------------------------------------------------
/src/js/components/App.styl:
--------------------------------------------------------------------------------
1 | .app
2 | margin 0px
3 | padding-top 50px
4 | display flex
5 | min-height 100vh
6 | flex-direction column
7 | .body
8 | flex 1
9 | .margin-top
10 | margin-top: 12px
11 | .margin-bottom
12 | margin-bottom: 12px
13 |
--------------------------------------------------------------------------------
/src/js/components/Base.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import {
3 | Grid,
4 | Row
5 | } from 'react-bootstrap'
6 |
7 | export default class Base extends Component {
8 | static propTypes = {
9 | requiredProps: PropTypes.string.isRequired,
10 | defaultProps: PropTypes.string
11 | };
12 | static defaultProps = {
13 | defaultProps: 'default props'
14 | };
15 | render () {
16 | return (
17 |
18 |
19 |
20 |
關於 學測:
21 |
22 | 大學學科能力測驗 (General Scholastic Ability Test)
23 | 是一種用於測驗高中學生,
24 | 是否具備基本的知識,
25 | 以進入台灣各大學就讀的大型考試,
26 | 由大學入學考試中心(大考中心)負責統籌舉辦。
27 |
28 |
29 |
30 |
31 |
32 |
關於 級分計算機:
33 |
34 | 這是一個單純的學測級分計算機,
35 | 此服務並非由教育部任何相關機構維護,
36 | 只是提供一個個人學習上練習的作品,
37 | 其資料也不會有其他任何用途的使用,
38 | 若是有任何疑問、建議歡迎透過以下方式聯絡:
39 |
vic20087cjimlin@gmail.com
40 |
41 |
42 |
43 |
44 |
45 |
關於 本練習:
46 |
47 | 本練習使用 react-redux 框架製作而成,
48 | 將資料放在 firebase 上面並做了權限處理,
49 | 以防止資料被任意更改。未來會再加上離線功能以及手機 app 版本(主要使用 PWA),
50 | 讓使用更加方便。如果對於此作品有興趣或這是需要一些範例,
51 | 歡迎到
github 上給我個
星星並使用看看,謝謝。
52 |
53 |
54 |
55 |
56 |
57 |
關於 作者:
58 |
59 | 作者目前就讀大學,平常沒事喜歡看一些設計、繪畫,
60 | 並製作些小作品給大家使用當做自我的練習,
61 | 如果對於我的作品感興趣的人歡迎到下面看看 :D
62 |
63 |
個人網頁
64 |
Github
65 |
一些小設計
66 |
67 |
68 |
69 |
70 | )
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/js/components/FirebaseTest.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react'
2 | import {
3 | Grid,
4 | Row,
5 | Col,
6 | ListGroup
7 | } from 'react-bootstrap'
8 | // import {connect} from 'react-redux'
9 | // import {firebase, helpers} from 'redux-react-firebase'
10 | // import FacebookLogin from 'react-facebook-login'
11 | // import _ from 'lodash'
12 | // const {isLoaded, isEmpty, dataToJS} = helpers
13 |
14 | export default class extends Component {
15 | static propTypes = {
16 | FBLogin: PropTypes.func.isRequired,
17 | getUserData: PropTypes.func.isRequired,
18 | currentUser: PropTypes.object.isRequired
19 | };
20 | static defaultProps = {
21 | };
22 | constructor (props) {
23 | super(props)
24 | this.state = {
25 | message: ''
26 | }
27 | console.log(this.state)
28 | this.FBLogin = this.FBLogin.bind(this)
29 | this.getUserData = this.getUserData.bind(this)
30 | this.getYearData = this.getYearData.bind(this)
31 | this.onFormSubmit = this.onFormSubmit.bind(this)
32 | }
33 | onFormSubmit (event) {
34 | event.preventDefault()
35 | }
36 | FBLogin () {
37 | console.log(this.props)
38 | this.props.FBLogin()
39 | }
40 | getUserData () {
41 | console.log('get user data...')
42 | this.props.getUserData()
43 | }
44 | getYearData () {
45 | console.log('get Year data...')
46 | this.props.getYearData(97)
47 | }
48 | componentDidMount () {
49 | console.log('====here====')
50 | console.log(this.props)
51 | }
52 | componentWillMount () {
53 | console.log('---- here ----')
54 | this.props.FBLogin()
55 | this.props.getYearData(97)
56 | console.log(this.props)
57 | }
58 | render () {
59 | // current user data not load in.
60 | if (Object.keys(this.props.currentUser).length === 0) {
61 | return Not login yet, please login
62 | }
63 | return (
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | {/* {window.mapObject(this.props.currentUser, (infomation) => (
80 |
81 | { infomation }
82 |
83 | ))} */}
84 |
85 |
86 |
87 |
88 |
89 |
90 | )
91 | }
92 | }
93 |
94 | // @firebase()
95 | // class TableItem extends Component {
96 | // render () {
97 | // const {name} = this.props
98 | // // console.log(name)
99 | // return (
100 | //
101 | //
102 | //
103 | // chniese: {name['chniese'][1]}
104 | //
105 | // english: {name['english'][1]}
106 | //
107 | //
108 | // )
109 | // }
110 | // }
111 | //
112 | // @firebase([
113 | // '/table' // if list is too large you can use ['/table']
114 | // ])
115 | // @connect(
116 | // ({firebase}) => ({
117 | // table: dataToJS(firebase, '/table')
118 | // })
119 | // )
120 | // ------------ in render -------------
121 | // const {firebase, table} = this.props
122 |
123 | // const handleAdd = () => {
124 | // const {newTodo} = this.refs
125 | // firebase.push('/table', {text: newTodo.value, done: false})
126 | // newTodo.value = ''
127 | // }
128 | // const tableList = (!isLoaded(table))
129 | // ? 'Loading' : (isEmpty(table))
130 | // ? 'Todo list is empty' : _.map(table, (name, id) => ())
131 |
132 | // const responseFacebook = (response) => {
133 | // console.log(response)
134 | // }
135 | /*
136 |
137 |
138 |
139 | { window.mapObject(this.props.currentUser.chniese, (problem) => (
140 |
141 | {window.chr(window.ord('A') + problem.id - 1)} . {problem.title}
142 | { problem }
143 |
144 | ))}
145 |
146 |
147 |
148 | */
149 |
--------------------------------------------------------------------------------
/src/js/components/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import { browserHistory } from 'react-router'
3 | import SweetAlert from 'sweetalert-react'
4 | import cookie from 'react-cookie'
5 | import CSSModules from 'react-css-modules'
6 | import 'sweetalert/dist/sweetalert.css'
7 | export default CSSModules(class Base extends Component {
8 | static propTypes = {
9 | FBLogin: PropTypes.func.isRequired,
10 | currentUser: PropTypes.object.isRequired
11 | };
12 | constructor (props) {
13 | super(props)
14 | this.FBLogin = this.FBLogin.bind(this)
15 | this.state = {
16 | fakeNum: 50,
17 | userId: '',
18 | show: false,
19 | text: `非常歡迎您光臨「學測.大平台」(以下簡稱本網站),為了讓您能夠安心使用本網站的各項服務與資訊,特此向您說明本網站的隱私權保護政策,以保障您的權益,請您詳閱下列內容:
20 |
一、隱私權保護政策的適用範圍
21 |
隱私權保護政策內容,包括本網站如何處理在您使用網站服務時收集到的個人識別資料。隱私權保護政策不適用於本網站以外的相關連結網站,也不適用於非本網站所委託或參與管理的人員。
22 |
二、個人資料的蒐集、處理及利用方式
23 |
當您造訪本網站或使用本網站所提供之功能服務時,我們將視該服務功能性質,請您提供必要的個人資料,並在該特定目的範圍內處理及利用您的個人資料;非經您書面同意,本網站不會將個人資料用於其他用途。
24 | 本網站在您使用服務信箱、問卷調查等互動性功能時,會保留您所提供的姓名、電子郵件地址、聯絡方式及使用時間等。
25 | 於一般瀏覽時,伺服器會自行記錄相關行徑,包括您使用連線設備的IP位址、使用時間、使用的瀏覽器、瀏覽及點選資料記錄等,做為我們增進網站服務的參考依據,此記錄為內部應用,決不對外公佈。
26 | 為提供精確的服務,我們會將收集的問卷調查內容進行統計與分析,分析結果之統計數據或說明文字呈現,除供內部研究外,我們會視需要公佈統計數據及說明文字,但不涉及特定個人之資料。
27 |
三、資料之保護
28 |
本網站主機均設有防火牆、防毒系統等相關的各項資訊安全設備及必要的安全防護措施,加以保護網站及您的個人資料採用嚴格的保護措施,只由經過授權的人員才能接觸您的個人資料,相關處理人員皆簽有保密合約,如有違反保密義務者,將會受到相關的法律處分。
29 | 如因業務需要有必要委託其他單位提供服務時,本網站亦會嚴格要求其遵守保密義務,並且採取必要檢查程序以確定其將確實遵守。
30 |
四、網站對外的相關連結
31 |
本網站的網頁提供其他網站的網路連結,您也可經由本網站所提供的連結,點選進入其他網站。但該連結網站不適用本網站的隱私權保護政策,您必須參考該連結網站中的隱私權保護政策。
32 |
五、與第三人共用個人資料之政策
33 |
本網站絕不會提供、交換、出租或出售任何您的個人資料給其他個人、團體、私人企業或公務機關,但有法律依據或合約義務者,不在此限。
34 | 前項但書之情形包括不限於:
35 | 經由您書面同意。
36 | 法律明文規定。
37 | 為免除您生命、身體、自由或財產上之危險。
38 | 與公務機關或學術研究機構合作,基於公共利益為統計或學術研究而有必要,且資料經過提供者處理或蒐集著依其揭露方式無從識別特定之當事人。
39 | 當您在網站的行為,違反服務條款或可能損害或妨礙網站與其他使用者權益或導致任何人遭受損害時,經網站管理單位研析揭露您的個人資料是為了辨識、聯絡或採取法律行動所必要者。
40 | 有利於您的權益。
41 | 本網站委託廠商協助蒐集、處理或利用您的個人資料時,將對委外廠商或個人善盡監督管理之責。
42 |
六、Cookie之使用
43 |
為了提供您最佳的服務,本網站會在您的電腦中放置並取用我們的Cookie,若您不願接受Cookie的寫入,您可在您使用的瀏覽器功能項中設定隱私權等級為高,即可拒絕Cookie的寫入,但可能會導至網站某些功能無法正常執行 。
44 |
七、隱私權保護政策之修正
45 |
本網站隱私權保護政策將因應需求隨時進行修正,修正後的條款將刊登於網站上。`
46 | }
47 | }
48 | FBLogin () {
49 | this.props.FBLogin().then((state) => {
50 | cookie.save('user', state.payload)
51 | browserHistory.push('/SAT')
52 | })
53 | }
54 | componentWillMount () {
55 | let user = cookie.load('user')
56 | // console.log(user)
57 | if (user === undefined) {
58 | // not login yet.
59 | } else {
60 | this.props.CookieLogin(user).then(() => {
61 | browserHistory.push('/SAT')
62 | })
63 | }
64 | }
65 | render () {
66 | return (
67 |
68 |
69 |
學測.大平台
70 |
歷屆試題級分計算機
71 |
72 |
73 |
74 | {/*
已有 {this.state.fakeNum} 位考生使用
*/}
75 |
一起加入奮鬥的行列吧!
76 |
77 |
78 |
79 | this.setState({ show: false })}
86 | onConfirm={() => this.setState({ show: false })}
87 | />
88 |
89 |
90 | )
91 | }
92 | }, require('./Login.styl'))
93 |
--------------------------------------------------------------------------------
/src/js/components/Login.styl:
--------------------------------------------------------------------------------
1 | $black = #50514F
2 | $blue = #D8EAF9
3 | $white = #F0F0F0
4 | $fb_color = rgba(#3b5998, 0.8)
5 | $bg-color= #ff8888 //start color
6 | $stops= 100 //smoothness
7 | $time= 20s //duration of animation
8 | $hue-range= 20 //of 360deg
9 | animation_bg()
10 | background: linear-gradient(230deg,#6a67ce, #6a67ce, #6a67ce, #fc636b, #ffb900, #53d769, #53d769, #53d769) //, #ffdd00
11 | background-size: 1000% 1000%
12 |
13 | -webkit-animation: AnimationName 35s ease infinite;
14 | -moz-animation: AnimationName 35s ease infinite;
15 | animation: AnimationName 35s ease infinite;
16 |
17 | @-webkit-keyframes AnimationName {
18 | 0%{background-position:0% 2.5%}
19 | 50%{background-position:100% 97.5%}
20 | 100%{background-position:0% 2.5%}
21 | }
22 | @-moz-keyframes AnimationName {
23 | 0%{background-position:0% 2.5%}
24 | 50%{background-position:100% 97.5%}
25 | 100%{background-position:0% 2.5%}
26 | }
27 | @keyframes AnimationName {
28 | 0%{background-position:0% 2.5%}
29 | 50%{background-position:100% 97.5%}
30 | 100%{background-position:0% 2.5%}
31 | }
32 | .fb_btn
33 | color $fb_color
34 | border-color $fb_color
35 |
36 | .not_login
37 | width 100vw
38 | height 100vh
39 | animation_bg()
40 | position absolute
41 | top 0
42 | left 0
43 | z-index 9999
44 | display flex
45 | flex-direction column
46 | justify-content center
47 | align-items center
48 | .not_login_title
49 | text-align center
50 | position absolute
51 | bottom 10px
52 | left 0px
53 | width 100%
54 | color $white !important
55 | p
56 | letter-spacing 2px
57 | color $white !important
58 | .not_login_section
59 | max-width 480px
60 | text-align center
61 | position relative
62 | p
63 | color $white
64 | font-size 1.5em
65 |
66 |
67 | .info_section
68 | margin 20px 0
69 | padding 0 20px
70 | .info_content
71 | letter-spacing 2px
72 | font-size 1.5em
73 | color $black
74 | .web
75 | margin-right 10px
76 | .star
77 | color rgba(#fcb314, 0.75)
78 | a
79 | color $black
80 | position relative
81 | text-decoration none
82 | font-size 0.75em
83 | cursor pointer
84 | &:after
85 | content ""
86 | width 100%
87 | height 5px
88 | background-color rgba($fb_color, 0.35)
89 | border-radius 1px
90 | position absolute
91 | bottom 0px
92 | left 0
93 |
--------------------------------------------------------------------------------
/src/js/components/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | export default class NotFound extends Component {
4 |
5 | render () {
6 | return (
7 |
8 | NotFound
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/js/components/SAT.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | // import reactDOM from 'react-dom'
3 | import {
4 | Grid,
5 | FormGroup,
6 | ControlLabel,
7 | FormControl
8 | } from 'react-bootstrap'
9 | require('./../../css/page/SAT.styl')
10 | export default class extends Component {
11 | static propTypes = {
12 | getScoreData: PropTypes.func.isRequired,
13 | getYearData: PropTypes.func.isRequired,
14 | updateUserScore: PropTypes.func.isRequired,
15 | userData: PropTypes.object.isRequired
16 | };
17 | static defaultProps = {
18 | };
19 | constructor (props) {
20 | super(props)
21 | this.state = {
22 | message: '',
23 | selectedYear: 96,
24 | scoreResult: '',
25 | Chinese: 0,
26 | English: 0,
27 | Math: 0,
28 | Society: 0,
29 | Science: 0
30 | }
31 | this.printResult = this.printResult.bind(this)
32 | this.onFormSubmit = this.onFormSubmit.bind(this)
33 | this.handleSelectChange = this.handleSelectChange.bind(this)
34 | this.handleInputChange = this.handleInputChange.bind(this)
35 | }
36 | onFormSubmit (event) {
37 | event.preventDefault()
38 | var mappingTable = ['chniese', 'english', 'math', 'science', 'social']
39 | var mappingValue = [15, 15, 15, 15, 15]
40 | var OriYear = 'SAT' + (1911 + parseInt(this.state.selectedYear))
41 | var OriData = {
42 | Chinese: parseInt(this.state.Chinese), // parseInt(reactDOM.findDOMNode(this.refs.Chinese).value),
43 | English: parseInt(this.state.English), // parseInt(reactDOM.findDOMNode(this.refs.English).value),
44 | Math: parseInt(this.state.Math), // parseInt(reactDOM.findDOMNode(this.refs.Math).value),
45 | Science: parseInt(this.state.Science),
46 | Society: parseInt(this.state.Society)
47 | }
48 | this.props.updateUserScore('init', OriYear, OriData)
49 |
50 | // trans to score.
51 | var calcData = OriData
52 | let mIndex = 0
53 | for (var key in OriData) {
54 | // skip loop if the property is from prototype
55 | if (!OriData.hasOwnProperty(key)) continue
56 | // get Original score, then do transfer.
57 | var score = OriData[key]
58 | // console.log(this.props)
59 | var compareList = this.props.userData.MeasureScore[mappingTable[mIndex]]
60 |
61 | if (score === 0) {
62 | mappingValue[mIndex] = 0
63 | } else {
64 | // console.log('here=======', compareList)
65 | for (let k = 0; k <= 15; k++) {
66 | if (score < compareList[k]) {
67 | mappingValue[mIndex] = k
68 | break
69 | }
70 | }
71 | }
72 | mIndex += 1
73 | }
74 | // update Object then ready to send back;
75 | mIndex = 0
76 | for (var calckey in calcData) {
77 | if (!calcData.hasOwnProperty(calckey)) continue
78 | calcData[calckey] = mappingValue[mIndex]
79 | mIndex += 1
80 | }
81 | // print in result input box
82 | // write back to user`s databse
83 | this.printResult(mappingValue)
84 | this.props.updateUserScore('score', OriYear, calcData)
85 | }
86 |
87 | printResult (mappingValue) {
88 | var newScoreResult = ''
89 | for (let i = 0; i < 5; i++) {
90 | newScoreResult += mappingValue[i]
91 | if (i < 4) newScoreResult += ' / '
92 | }
93 | this.setState({scoreResult: newScoreResult})
94 | }
95 |
96 | handleSelectChange (event) {
97 | var selectedYear = event.target.value
98 | this.setState({selectedYear: selectedYear})
99 | var yearIndex = 'SAT' + (1911 + parseInt(selectedYear))
100 | this.props.getYearData(yearIndex)
101 | this.props.getScoreData(yearIndex)
102 | }
103 |
104 | handleInputChange (event) {
105 | var tmp = {}
106 | tmp[event.target.name] = event.target.value
107 | this.setState(tmp)
108 | }
109 |
110 | componentWillReceiveProps (nextProps) {
111 | // console.log('receive', nextProps)
112 | this.setState({
113 | ...nextProps.userData.StudentScore
114 | })
115 | }
116 |
117 | componentDidMount () {
118 | }
119 |
120 | componentWillMount () {
121 | }
122 | render () {
123 | return (
124 |
125 |
126 |
171 |
172 |
173 |
174 | )
175 | }
176 | }
177 |
178 | // icon , name
179 | // chart, with total score
180 | // setting, style
181 |
--------------------------------------------------------------------------------
/src/js/components/chart.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import _ from 'lodash'
3 | // import { mergeSort } from 'js-sorting'
4 | import {
5 | Line
6 | } from 'react-chartjs-2'
7 | import {
8 | Grid,
9 | Row,
10 | Col
11 | } from 'react-bootstrap'
12 | import CSSModules from 'react-css-modules'
13 | export default CSSModules(class extends Component {
14 | static propTypes = {
15 | FBLogin: PropTypes.func.isRequired,
16 | getYearData: PropTypes.func.isRequired,
17 | // updateUserAvg: PropTypes.func.isRequired,
18 | getUserTotalYearData: PropTypes.func.isRequired,
19 | userData: PropTypes.object.isRequired,
20 | currentUser: PropTypes.object.isRequired
21 | };
22 | constructor (props) {
23 | super(props)
24 | this.state = {
25 | counter: 0,
26 | fakeNum: 50,
27 | avg: 0,
28 | height: 400,
29 | width: 400,
30 | totalChartData: {
31 | labels: ['96', '97', '98', '99', '100', '101', '102', '103', '104', '105'],
32 | datasets: [
33 | {
34 | borderWidth: 2,
35 | backgroundColor: 'rgba(164,211,250,0.25)',
36 | borderColor: 'rgba(164,211,250,1)',
37 | pointColor: 'rgba(164,211,250,1)',
38 | pointStrokeColor: '#fff',
39 | pointHighlightFill: '#fff',
40 | pointHighlightStroke: 'rgba(220,220,220,1)',
41 | data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
42 |
43 | }
44 | ]
45 | },
46 | singleChartData: {
47 | labels: ['96', '97', '98', '99', '100', '101', '102', '103', '104', '105'],
48 | datasets: [
49 | {
50 | label: 'Chinese',
51 | borderWidth: 2,
52 | backgroundColor: 'rgba(255,90,100,0.05)',
53 | borderColor: 'rgba(255,90,100,1)',
54 | pointColor: 'rgba(255,90,100,1)',
55 | pointStrokeColor: '#fff',
56 | pointHighlightFill: '#fff',
57 | pointHighlightStroke: 'rgba(220,220,220,1)',
58 | data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
59 | },
60 | {
61 | label: 'English',
62 | borderWidth: 2,
63 | backgroundColor: 'rgba(46,135,170,0.05)',
64 | borderColor: 'rgba(46,135,170,1)',
65 | pointColor: 'rgba(46,135,170,1)',
66 | pointStrokeColor: '#fff',
67 | pointHighlightFill: '#fff',
68 | pointHighlightStroke: 'rgba(220,220,220,1)',
69 | data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
70 | },
71 | {
72 | label: 'Math',
73 | borderWidth: 2,
74 | backgroundColor: 'rgba(245,210,95,0.05)',
75 | borderColor: 'rgba(245,210,95,1)',
76 | pointColor: 'rgba(245,210,95,1)',
77 | pointStrokeColor: '#fff',
78 | pointHighlightFill: '#fff',
79 | pointHighlightStroke: 'rgba(220,220,220,1)',
80 | data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
81 | },
82 | {
83 | label: 'Society',
84 | borderWidth: 2,
85 | backgroundColor: 'rgba(105,150,90,0.05)',
86 | borderColor: 'rgba(105,150,90,1)',
87 | pointColor: 'rgba(105,150,90,1)',
88 | pointStrokeColor: '#fff',
89 | pointHighlightFill: '#fff',
90 | pointHighlightStroke: 'rgba(220,220,220,1)',
91 | data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
92 | },
93 | {
94 | label: 'Science',
95 | borderWidth: 2,
96 | backgroundColor: 'rgba(80,80,80,0.05)',
97 | borderColor: 'rgba(80,80,80,1)',
98 | pointColor: 'rgba(80,80,80,1)',
99 | pointStrokeColor: '#fff',
100 | pointHighlightFill: '#fff',
101 | pointHighlightStroke: 'rgba(220,220,220,1)',
102 | data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
103 | }
104 | ]
105 | },
106 | chartOptions: {
107 | title: {
108 | display: false,
109 | text: 'SAT score every year / 年份對照'
110 | },
111 | legend: {
112 | display: false
113 | }
114 | // responsive: true,
115 | // maintainAspectRatio: true,
116 | // bezierCurveTension: 0.25,
117 | // scaleOverride: true,
118 | // // scaleSteps: 5,
119 | // // scaleStepWidth: 15,
120 | // scaleStartValue: 0,
121 | // legendTemplate: '-legend\'><% for (var i=0; i- \'><%if(datasets[i].label){%><%=datasets[i].label%><%}%>級分
<%}%>
'
122 | },
123 | singleChartOptions: {
124 | legend: {
125 | position: 'bottom',
126 | labels: {
127 | boxWidth: 12
128 | }
129 | }
130 | // responsive: true,
131 | // maintainAspectRatio: true,
132 | // bezierCurveTension: 0.25,
133 | // scaleOverride: true,
134 | // scaleSteps: 15,
135 | // scaleStepWidth: 1,
136 | // scaleStartValue: 0,
137 | // legendTemplate: '-legend\'><% for (var i=0; i- \'><%if(datasets[i].label){%><%=datasets[i].label%><%}%>級分
<%}%>
'
138 | }
139 | }
140 | this.changedata = this.changedata.bind(this)
141 | this.updateUserAvg = this.updateUserAvg.bind(this)
142 | this.getUserTotalYearData = this.getUserTotalYearData.bind(this)
143 | this.updateChartSize = this.updateChartSize.bind(this)
144 | }
145 |
146 | changedata () {
147 | let totalDataArray = new Array(10)
148 | let singleDataArray = new Array(5)
149 | // init single data
150 | for (let i = 0; i < 5; i++) {
151 | singleDataArray[i] = new Array(10)
152 | for (let j = 0; j < 10; j++) {
153 | singleDataArray[i][j] = 0
154 | }
155 | }
156 | // init avg params
157 | var avgCounter = 0
158 | var avgNum = 0
159 | // init total data
160 | for (let i = 0; i < 10; i++) {
161 | totalDataArray[i] = 0
162 | }
163 | var chartDataObj = this.props.userData.TotalScore.score
164 | for (var key in chartDataObj) {
165 | var yearTotalScore = 0
166 | // SAT2007 -> 2007 -> 0
167 | var mappingIndex = parseInt(key.slice(3)) - 2007
168 | let subjectCount = 0
169 | for (var subject in chartDataObj[key]) {
170 | // console.log('index: ', mappingIndex, ', sub: ', subjectCount, 'score: ', chartDataObj[key][subject])
171 | yearTotalScore += chartDataObj[key][subject]
172 | singleDataArray[subjectCount][mappingIndex] = chartDataObj[key][subject]
173 | subjectCount += 1
174 | }
175 | totalDataArray[mappingIndex] = yearTotalScore
176 | if (yearTotalScore > 0) {
177 | avgCounter += 1
178 | avgNum += yearTotalScore
179 | }
180 | }
181 | this.props.updateUserAvg(avgNum / avgCounter)
182 | this.setState({avg: (avgNum / avgCounter).toFixed(2)})
183 |
184 | // put total data back to state.
185 | let newArray = _.extend({}, this.state.totalChartData)
186 | newArray.datasets[0].data = totalDataArray
187 | this.setState({totalChartData: newArray})
188 |
189 | // put single data back to state.
190 | for (let i = 0; i < 5; i++) {
191 | let newArray = _.extend({}, this.state.singleChartData)
192 | newArray.datasets[i].data = singleDataArray[i]
193 | this.setState({singleChartData: newArray})
194 | }
195 | this.setState({counter: this.state.counter + 1})
196 | }
197 | updateChartSize () {
198 | var w = window
199 | var d = document
200 | var documentElement = d.documentElement
201 | var body = d.getElementsByTagName('body')[0]
202 | var width = w.innerWidth || documentElement.clientWidth || body.clientWidth
203 | width *= 0.9
204 | var height = width * 3 / 4
205 | // console.log('resize', width, height)
206 | this.setState({width: width, height: height})
207 | // console.log('yo', this.state.width, this.state.height)
208 | }
209 | getUserTotalYearData () {
210 | this.props.getUserTotalYearData()
211 | }
212 | updateUserAvg () {
213 | this.props.updateUserAvg()
214 | }
215 | componentWillMount () {
216 | this.props.getUserTotalYearData()
217 | // var tmp = mergeSort([5, 3, 2, 4, 1, 7, 4, 10])
218 | // var l = tmp.length
219 | // var input = 7
220 | // var result = l
221 | // for (var i = 0; i < l; i++) {
222 | // if (input < tmp[i]) {
223 | // result = i
224 | // break
225 | // }
226 | // }
227 | }
228 | componentDidMount () {
229 | window.addEventListener('resize', this.updateChartSize)
230 | }
231 | render () {
232 | return (
233 |
234 |
235 |
236 |
237 | 你好,{this.props.currentUser.AuthData.user.displayName}
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 | 總覽
249 |
250 |
251 |
252 | 單科
253 |
254 |
255 |
256 | 平均
257 | {this.state.avg}
258 |
259 |
260 | 排名
261 | 功能尚未推出,請耐心等待
262 |
263 |
264 |
265 |
266 | )
267 | }
268 | }, require('./chart.styl'))
269 |
270 | // icon , name
271 | // chart, with total score
272 | // setting, style
273 |
--------------------------------------------------------------------------------
/src/js/components/chart.styl:
--------------------------------------------------------------------------------
1 | @import "./App.styl"
2 | $black = #50514F
3 | $blue = #D8EAF9
4 | $white = #F0F0F0
5 | $fb_color = rgba(#3b5998, 0.8)
6 | $bg-color= #ff8888 //start color
7 | $stops= 100 //smoothness
8 | $time= 20s //duration of animation
9 | $hue-range= 20 //of 360deg
10 | .chart_card_contain
11 | padding 0 15px
12 | .chart_card
13 | padding 5px 15px 10px
14 | margin 15px 0
15 | border-radius 5px
16 | background white
17 | box-shadow 3px 4px 5px rgba($black, 0.25)
18 | .chart_titleText
19 | font-size 2.5em
20 | .chart_contentText
21 | font-size 1.25em
22 |
--------------------------------------------------------------------------------
/src/js/components/common/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | export default class Footer extends Component {
4 | render () {
5 | return (
6 |
9 | )
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/js/components/common/Nav.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import {
3 | Navbar,
4 | NavItem,
5 | Nav
6 | } from 'react-bootstrap'
7 | import { IndexLinkContainer, LinkContainer } from 'react-router-bootstrap'
8 | export default class extends Component {
9 | render () {
10 | return (
11 |
12 |
13 |
14 |
15 | 學測.大平台
16 |
17 |
18 |
19 |
20 |
21 |
32 |
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/js/components/common/index.js:
--------------------------------------------------------------------------------
1 | import Nav from './Nav'
2 | import Footer from './Footer'
3 | export default {
4 | Nav,
5 | Footer
6 | }
7 |
--------------------------------------------------------------------------------
/src/js/components/index.js:
--------------------------------------------------------------------------------
1 | import common from './common'
2 | import App from './App'
3 | import Base from './Base'
4 | import Chart from './Chart'
5 | import FirebaseTest from './FirebaseTest'
6 | import Login from './Login'
7 | import NotFound from './NotFound'
8 | import SAT from './SAT'
9 | export default {
10 | common,
11 | App,
12 | Base,
13 | Chart,
14 | FirebaseTest,
15 | Login,
16 | NotFound,
17 | SAT
18 | }
19 |
--------------------------------------------------------------------------------
/src/js/containers/App.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 | import Action from 'js/actions'
4 |
5 | const mapStateToProps = (state) => ({
6 | })
7 | const mapDispatchToProps = (dispatch) => ({
8 | FBLogout: (data) => dispatch(Action.Session.FBLogout(data)),
9 | FBLogin: (data) => dispatch(Action.Session.FBLogin(data)),
10 | getUserData: () => dispatch(Action.User.getUserData())
11 | })
12 | export default connect(mapStateToProps, mapDispatchToProps)(Components.App)
13 |
--------------------------------------------------------------------------------
/src/js/containers/Base.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 |
4 | const mapStateToProps = (state, ownProps) => ({
5 | ...ownProps
6 | })
7 |
8 | const mapDispatchToProps = (dispatch) => ({
9 | })
10 |
11 | export default connect(mapStateToProps, mapDispatchToProps)(Components.Base)
12 |
--------------------------------------------------------------------------------
/src/js/containers/Chart.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 | import Action from 'js/actions'
4 | const mapStateToProps = (state) => ({
5 | currentUser: state.Session,
6 | userData: state.SAT
7 | })
8 |
9 | const mapDispatchToProps = (dispatch) => ({
10 | FBLogin: () => dispatch(Action.Session.FBLogin()),
11 | isUserLogin: () => dispatch(Action.Session.isUserLogin()),
12 | getYearData: (year) => dispatch(Action.SAT.getYearData(year)),
13 | updateUserAvg: (avg) => dispatch(Action.SAT.updateUserAvg(avg)),
14 | getUserTotalYearData: () => dispatch(Action.SAT.getUserTotalYearData())
15 | })
16 |
17 | export default connect(mapStateToProps, mapDispatchToProps)(Components.Chart)
18 |
--------------------------------------------------------------------------------
/src/js/containers/DevTools.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createDevTools } from 'redux-devtools'
3 | import LogMonitor from 'redux-devtools-log-monitor'
4 | import DockMonitor from 'redux-devtools-dock-monitor'
5 |
6 | export default createDevTools(
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/src/js/containers/FirebaseTest.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 | import Action from 'js/actions'
4 | const mapStateToProps = (state) => ({
5 | currentUser: state.Session,
6 | userData: state.Score
7 | })
8 |
9 | const mapDispatchToProps = (dispatch) => ({
10 | FBLogout: () => dispatch(Action.Session.FBLogout()),
11 | FBLogin: () => dispatch(Action.Session.FBLogin()),
12 | getUserData: () => dispatch(Action.Score.getUserData()),
13 | getYearData: (year) => dispatch(Action.Score.getYearData(year))
14 | })
15 |
16 | export default connect(mapStateToProps, mapDispatchToProps)(Components.FirebaseTest)
17 |
--------------------------------------------------------------------------------
/src/js/containers/Login.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 | import Action from 'js/actions'
4 | const mapStateToProps = (state) => ({
5 | currentUser: state.Session
6 | })
7 |
8 | const mapDispatchToProps = (dispatch) => ({
9 | FBLogin: () => dispatch(Action.Session.FBLogin()),
10 | CookieLogin: (data) => dispatch(Action.Session.CookieLogin(data))
11 | })
12 |
13 | export default connect(mapStateToProps, mapDispatchToProps)(Components.Login)
14 |
--------------------------------------------------------------------------------
/src/js/containers/SAT.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 | import Action from 'js/actions'
4 | const mapStateToProps = (state) => ({
5 | userData: state.SAT
6 | })
7 |
8 | const mapDispatchToProps = (dispatch) => ({
9 | getScoreData: (year) => dispatch(Action.SAT.getScoreData(year)),
10 | getYearData: (year) => dispatch(Action.SAT.getYearData(year)),
11 | updateUserScore: (path, index, data) => dispatch(Action.SAT.updateUserScore(path, index, data))
12 | })
13 |
14 | export default connect(mapStateToProps, mapDispatchToProps)(Components.SAT)
15 |
--------------------------------------------------------------------------------
/src/js/containers/common/Nav.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import Components from 'js/components'
3 |
4 | const mapStateToProps = (store) => ({})
5 | const mapDispatchToProps = (dispatch) => ({})
6 |
7 | export default connect(mapStateToProps, mapDispatchToProps)(Components.common.Nav)
8 |
--------------------------------------------------------------------------------
/src/js/containers/common/index.js:
--------------------------------------------------------------------------------
1 | import Nav from './Nav'
2 |
3 | export default {
4 | Nav
5 | }
6 |
--------------------------------------------------------------------------------
/src/js/containers/index.js:
--------------------------------------------------------------------------------
1 | import common from './common'
2 | import App from './App'
3 | import Base from './Base'
4 | import Chart from './Chart'
5 | import DevTools from './DevTools'
6 | import FirebaseTest from './FirebaseTest'
7 | import Login from './Login'
8 | import SAT from './SAT'
9 | export default {
10 | common,
11 | App,
12 | Base,
13 | Chart,
14 | DevTools,
15 | FirebaseTest,
16 | Login,
17 | SAT
18 | }
19 |
--------------------------------------------------------------------------------
/src/js/reducers/Base.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 |
3 | const initialState = {}
4 |
5 | export default handleActions({
6 |
7 | ACTION: {
8 | next (state, action) {
9 | return {
10 | ...state
11 | }
12 | },
13 | throw (state, action) {
14 | return {
15 | ...state
16 | }
17 | }
18 | },
19 |
20 | default: (state, action) => {
21 | return {
22 | ...state
23 | }
24 | }
25 | }, initialState)
26 |
--------------------------------------------------------------------------------
/src/js/reducers/SAT.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 |
3 | const initialState = {
4 | MeasureScore: {},
5 | StudentScore: {},
6 | TotalScore: {}
7 | }
8 |
9 | export default handleActions({
10 |
11 | getScoreData: {
12 | next (state, action) {
13 | console.log('get score', action.payload)
14 | return {
15 | ...state,
16 | StudentScore: action.payload
17 | }
18 | },
19 | throw (state, action) {
20 | return {
21 | ...state
22 | }
23 | }
24 | },
25 |
26 | getYearData: {
27 | next (state, action) {
28 | console.log('get year', action.payload)
29 | return {
30 | ...state,
31 | MeasureScore: action.payload
32 | }
33 | },
34 | throw (state, action) {
35 | return {
36 | ...state
37 | }
38 | }
39 | },
40 |
41 | getUserTotalYearData: {
42 | next (state, action) {
43 | console.log(action.payload)
44 | return {
45 | ...state,
46 | TotalScore: action.payload
47 | }
48 | },
49 | throw (state, action) {
50 | return {
51 | ...state
52 | }
53 | }
54 | },
55 |
56 | updateUserScore: {
57 | next (state, action) {
58 | return {
59 | ...state,
60 | ...action.payload
61 | }
62 | },
63 | throw (state, action) {
64 | return {
65 | ...state
66 | }
67 | }
68 | },
69 |
70 | updateUserAvg: {
71 | next (state, action) {
72 | return {
73 | ...state,
74 | ...action.payload
75 | }
76 | },
77 | throw (state, action) {
78 | return {
79 | ...state
80 | }
81 | }
82 | },
83 |
84 | default: (state, action) => {
85 | return {
86 | ...state
87 | }
88 | }
89 | }, initialState)
90 |
--------------------------------------------------------------------------------
/src/js/reducers/Session.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 |
3 | const initialState = {
4 | AuthData: {}
5 | }
6 |
7 | export default handleActions({
8 |
9 | FBLogin: {
10 | next (state, action) {
11 | return {
12 | ...state,
13 | AuthData: action.payload
14 | }
15 | },
16 | throw (state, action) {
17 | return {
18 | AuthData: {}
19 | }
20 | }
21 | },
22 |
23 | FBLogout: {
24 | next (state, action) {
25 | return {
26 | ...state,
27 | AuthData: action.payload
28 | }
29 | },
30 | throw (state, action) {
31 | return {
32 | ...state
33 | }
34 | }
35 | },
36 |
37 | CookieLogin: {
38 | next (state, action) {
39 | return {
40 | ...state,
41 | AuthData: action.payload
42 | }
43 | }
44 | },
45 |
46 | default: (state, action) => {
47 | return {
48 | ...state
49 | }
50 | }
51 | }, initialState)
52 |
--------------------------------------------------------------------------------
/src/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { routerReducer as routing } from 'react-router-redux'
3 | import { firebaseStateReducer } from 'redux-react-firebase'
4 | import base from './Base'
5 | import Session from './Session'
6 | import SAT from './SAT'
7 |
8 | export default combineReducers({
9 | base,
10 | Session,
11 | SAT,
12 | firebase: firebaseStateReducer,
13 | routing
14 | })
15 |
--------------------------------------------------------------------------------
/src/js/routes.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Router, Route, IndexRoute, browserHistory } from 'react-router'
3 | import store from './stores/Store'
4 | import { syncHistoryWithStore } from 'react-router-redux'
5 | const history = syncHistoryWithStore(browserHistory, store)
6 |
7 | // import Components from 'js/components'
8 | import Containers from 'js/containers'
9 |
10 | const checkLogin = (next) => {
11 | if (Object.keys(store.getState().Session.AuthData).length === 0) {
12 | browserHistory.push('/login')
13 | }
14 | }
15 |
16 | export default class Root extends Component {
17 | render () {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/js/stores/Store.js:
--------------------------------------------------------------------------------
1 | // import { createStore, compose, applyMiddleware } from 'redux'
2 | import { createStore, compose, applyMiddleware } from 'redux'
3 | import {reduxReactFirebase} from 'redux-react-firebase'
4 | import thunk from 'redux-thunk'
5 | import promiseMiddleware from 'redux-promise'
6 | import rootReducer from '../reducers'
7 | import DevTools from '../containers/DevTools'
8 |
9 | function configureStore () {
10 | const middleware = [thunk, promiseMiddleware]
11 |
12 | const config = {
13 | apiKey: 'AIzaSyDBaa3EU9oHINKc4iVzqG8TfIcKvoUk3KM',
14 | authDomain: 'sat-transtable.firebaseapp.com',
15 | databaseURL: 'https://sat-transtable.firebaseio.com',
16 | storageBucket: 'sat-transtable.appspot.com',
17 | messagingSenderId: '564068329530'
18 | }
19 |
20 | const finalCreateStore = compose(
21 | reduxReactFirebase(config),
22 | applyMiddleware(...middleware),
23 | DevTools.instrument(),
24 | window.devToolsExtensio ? window.devToolsExtension() : f => f
25 | )(createStore)
26 |
27 | let store = finalCreateStore(rootReducer)
28 |
29 | if (module.hot) {
30 | module.hot.accept('../reducers', () => {
31 | const nextRootReducer = require('../reducers')
32 | store.replaceReducer(nextRootReducer)
33 | })
34 | }
35 |
36 | return store
37 | }
38 |
39 | export default configureStore()
40 |
--------------------------------------------------------------------------------
/src/js/utils/Base.js:
--------------------------------------------------------------------------------
1 | export const mapArrayToObject = (arr: Array