Commit 16815a3805fc895501269ffeadeaec239e376687

Authored by Galileo Sanchez
1 parent 8a54ad09

al fin terminamos

Showing 38 changed files with 663 additions and 117 deletions
... ... @@ -30,9 +30,6 @@ moment.locale('es-ES')
30 30
31 31 export default class App extends Component {
32 32
33   - componentWillMount() {
34   - }
35   -
36 33 render() {
37 34 // buscar solicitudes
38 35 // estadisticas
... ... @@ -57,6 +54,9 @@ export default class App extends Component {
57 54 BuscarSolicitudes: { screen: BuscarSolicitudes },
58 55 DetalleSolicitud: { screen: DetalleSolicitud }
59 56 })
  57 + const AyudaNavigator = createStackNavigator({
  58 + Ayuda: { screen: Ayuda },
  59 + })
60 60
61 61 const DrawerNavigator = createDrawerNavigator({
62 62 MisSolicitudes: {
... ... @@ -84,7 +84,12 @@ export default class App extends Component {
84 84 }),
85 85 },
86 86 Estadisticas: { screen: Estadisticas },
87   - Ayuda: { screen: Ayuda },
  87 + AyudaNavigator: {
  88 + screen: AyudaNavigator,
  89 + navigationOptions: () => ({
  90 + title: 'Ayuda',
  91 + })
  92 + }
88 93 }, {
89 94 contentComponent: CustomDrawerContentComponent
90 95 })
... ...
  1 +import React, { Component } from 'react'
  2 +import {
  3 + View,
  4 +} from 'react-native'
  5 +import { connect } from 'react-redux'
  6 +import { Button } from 'react-native-elements'
  7 +
  8 +import { postValoracion } from '../../redux/actions/valoraciones'
  9 +import { postReporte } from '../../redux/actions/reportes'
  10 +
  11 +import css from './style'
  12 +
  13 +const SATISFECHO = 'SATISFECHO'
  14 +const NO_SATISFECHO = 'NO SATISFECHO'
  15 +const INFORMACION_NO_PUBLICADA = 'CONFIDENCIAL'
  16 +class BotonValoracion extends Component {
  17 +
  18 + render() {
  19 + if (this.props.mostrar) {
  20 + const solicitudId = this.props.solicitud.id
  21 + const usuario = this.props.usuario
  22 + const likeName = this.props.valoracion.data === SATISFECHO
  23 + ? 'ios-thumbs-up'
  24 + : 'ios-thumbs-up-outline'
  25 +
  26 + const dislikeName = this.props.valoracion.data === NO_SATISFECHO
  27 + ? 'ios-thumbs-down'
  28 + : 'ios-thumbs-down-outline'
  29 +
  30 + const reportName = this.props.reporte.data === INFORMACION_NO_PUBLICADA
  31 + ? 'ios-megaphone'
  32 + : 'ios-megaphone-outline'
  33 +
  34 + return (
  35 + <View style={[css.buttonFooter]}>
  36 + <Button
  37 + onPress={() => this.props.postReporte(solicitudId, usuario, INFORMACION_NO_PUBLICADA)}
  38 + backgroundColor="white"
  39 + icon={{
  40 + name: reportName,
  41 + size: 27,
  42 + color: 'red',
  43 + type: 'ionicon'
  44 + }}
  45 + />
  46 + <Button
  47 + backgroundColor="white"
  48 + onPress={() => this.props.postValoracion(solicitudId, usuario, SATISFECHO)}
  49 + icon={{
  50 + name: likeName,
  51 + size: 27,
  52 + color: 'red',
  53 + type: 'ionicon'
  54 + }}
  55 + />
  56 + <Button
  57 + onPress={() => this.props.postValoracion(solicitudId, usuario, NO_SATISFECHO)}
  58 + backgroundColor="white"
  59 + icon={{
  60 + name: dislikeName,
  61 + size: 27,
  62 + color: 'red',
  63 + type: 'ionicon'
  64 + }}
  65 + />
  66 + </View>
  67 + )
  68 + }
  69 + return <View />
  70 + }
  71 +}
  72 +
  73 +const mapStateToProps = (state) => ({
  74 + solicitud: state.solicitudes.current,
  75 + usuario: state.usuario.datos,
  76 + valoracion: state.valoracion,
  77 + reporte: state.reporte,
  78 +})
  79 +
  80 +const mapDispatchToProps = (dispatch) => ({
  81 + postValoracion: (solicitud, usuario, valoracion) =>
  82 + dispatch(postValoracion(solicitud, usuario, valoracion)),
  83 + postReporte: (solicitud, usuario, valoracion) =>
  84 + dispatch(postReporte(solicitud, usuario, valoracion))
  85 +})
  86 +export default connect(mapStateToProps, mapDispatchToProps)(BotonValoracion)
... ...
  1 +import { StyleSheet } from 'react-native'
  2 +import { merge } from 'ramda'
  3 +
  4 +import styles from '../../common/styles'
  5 +
  6 +export default merge(StyleSheet.create({
  7 + buttonFooter: {
  8 + flexDirection: 'row',
  9 + justifyContent: 'space-evenly'
  10 + }
  11 +}), styles)
... ...
... ... @@ -3,10 +3,12 @@ import { View, Image, Linking, TouchableOpacity } from 'react-native'
3 3
4 4 import css from './style'
5 5
  6 +const innovandoImg = require('../../assets/logos/innovando.png')
  7 +const senaticsImg = require('../../assets/logos/senatics.png')
  8 +
6 9 class FooterSenatics extends Component {
7 10
8 11 myHandlePress = (link) => () => {
9   - console.log('pressed link')
10 12 Linking.openURL(link)
11 13 }
12 14
... ... @@ -15,12 +17,12 @@ class FooterSenatics extends Component {
15 17 <View style={css.container}>
16 18 <View style={css.card} >
17 19 <TouchableOpacity onPress={this.myHandlePress('http://www.innovando.gov.py')}>
18   - <Image style={css.img} source={require('../../assets/logos/innovando.png')} />
  20 + <Image style={css.img} source={innovandoImg} />
19 21 </TouchableOpacity>
20 22 </View>
21 23 <View style={css.card} >
22 24 <TouchableOpacity onPress={this.myHandlePress('https://www.senatics.gov.py')}>
23   - <Image style={css.img} source={require('../../assets/logos/senatics.png')} />
  25 + <Image style={css.img} source={senaticsImg} />
24 26 </TouchableOpacity>
25 27 </View>
26 28 </View>)
... ...
... ... @@ -8,7 +8,6 @@ const imgSrc = require('../../assets/logos/jaikuaamina.png')
8 8 class FooterSenatics extends Component {
9 9
10 10 myHandlePress = (link) => () => {
11   - console.log('pressed link')
12 11 Linking.openURL(link)
13 12 }
14 13 resizeMode='center'
... ...
... ... @@ -3,19 +3,16 @@ import { Constants } from 'expo'
3 3 export const STATUS_BAR_HEIGHT = Constants.statusBarHeight
4 4
5 5 // PROD
6   -export const AUTH_TOKEN = '730e9175-c76c-418a-a051-7e2834ed7fd7'
7   -export const CLIENT_SECRET = 'deb7aa26038d0f44a35cd3e1d2f60b5c0e11192ddd0f9430c0d64a1101daa11f'
8   -export const SENATICS_URL = 'https://informacionpublica.paraguay.gov.py:443/portal-core/rest/api'
9   -
  6 +// export const SENATICS_URL = 'https://informacionpublica.paraguay.gov.py:443/portal-core/rest/api'
10 7 // DEV
11   -// export const AUTH_TOKEN = '0496164f-8de1-449c-9ce9-af0e0a6ce9fe'
12   -// export const CLIENT_SECRET = 'a66a1a98eb7059a5c3b446183e214cc2661326c2994b811cb06dd9d3d873a9e9'
13   -// export const SENATICS_URL = 'http://192.168.0.26:2128/api'
14   -export const BACKEND_URL = 'http://192.168.0.26:3000/api'
15   -export const RENEW_INTERVAL = 1 * 1000 * 60
16   -export const TOKEN_EXPIRATION = 15 * 1000 * 60
  8 +export const SENATICS_URL = 'http://192.168.0.26:2128/api'
  9 +
  10 +export const BACKEND_URL = 'http://192.168.0.26:3000'
17 11 export const NOTIFICATION_ICON = 'http://ayuda.tesis-jmgo.cnc.una.py/img/jaikuaamina.icon.png'
18 12 export const INFO_PY_URL = 'https://informacionpublica.paraguay.gov.py/portal/'
  13 +export const PORTAL_DENUNCIAS_URL = 'http://www.denuncias.gov.py'
  14 +export const TUTORIAL_AIF_URL = 'https://youtu.be/W8jG3md7YFg'
  15 +export const TUTORIAL_APP = `${BACKEND_URL}/index.html`
19 16
20 17 export const ESTADOS_SOLICITUDES = {
21 18 finalizados: [
... ...
... ... @@ -2629,13 +2629,12 @@
2629 2629 }
2630 2630 },
2631 2631 "buffer": {
2632   - "version": "4.9.1",
2633   - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
2634   - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
  2632 + "version": "5.1.0",
  2633 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz",
  2634 + "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==",
2635 2635 "requires": {
2636 2636 "base64-js": "^1.0.2",
2637   - "ieee754": "^1.1.4",
2638   - "isarray": "^1.0.0"
  2637 + "ieee754": "^1.1.4"
2639 2638 }
2640 2639 },
2641 2640 "buffer-alloc": {
... ... @@ -5157,8 +5156,7 @@
5157 5156 },
5158 5157 "safe-buffer": {
5159 5158 "version": "5.1.1",
5160   - "bundled": true,
5161   - "optional": true
  5159 + "bundled": true
5162 5160 },
5163 5161 "safer-buffer": {
5164 5162 "version": "2.1.2",
... ... @@ -5250,8 +5248,7 @@
5250 5248 },
5251 5249 "yallist": {
5252 5250 "version": "3.0.2",
5253   - "bundled": true,
5254   - "optional": true
  5251 + "bundled": true
5255 5252 }
5256 5253 }
5257 5254 },
... ... @@ -9890,6 +9887,18 @@
9890 9887 "html-entities": "^1.2.0",
9891 9888 "htmlparser2": "^3.9.0",
9892 9889 "stream": "0.0.2"
  9890 + },
  9891 + "dependencies": {
  9892 + "buffer": {
  9893 + "version": "4.9.1",
  9894 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
  9895 + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
  9896 + "requires": {
  9897 + "base64-js": "^1.0.2",
  9898 + "ieee754": "^1.1.4",
  9899 + "isarray": "^1.0.0"
  9900 + }
  9901 + }
9893 9902 }
9894 9903 },
9895 9904 "react-native-safe-area-view": {
... ... @@ -12613,9 +12622,9 @@
12613 12622 "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
12614 12623 },
12615 12624 "uuid": {
12616   - "version": "3.0.1",
12617   - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
12618   - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE="
  12625 + "version": "3.2.1",
  12626 + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
  12627 + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
12619 12628 },
12620 12629 "uuid-js": {
12621 12630 "version": "0.7.5",
... ... @@ -12833,6 +12842,13 @@
12833 12842 "pegjs": "^0.10.0",
12834 12843 "simple-plist": "^0.2.1",
12835 12844 "uuid": "3.0.1"
  12845 + },
  12846 + "dependencies": {
  12847 + "uuid": {
  12848 + "version": "3.0.1",
  12849 + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
  12850 + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE="
  12851 + }
12836 12852 }
12837 12853 },
12838 12854 "xdl": {
... ...
... ... @@ -20,6 +20,7 @@
20 20 "preset": "jest-expo"
21 21 },
22 22 "dependencies": {
  23 + "buffer": "^5.1.0",
23 24 "elliptic": "^6.4.0",
24 25 "expo": "^27.0.1",
25 26 "moment": "^2.22.2",
... ... @@ -35,6 +36,7 @@
35 36 "redux": "^4.0.0",
36 37 "redux-observable": "^0.19.0",
37 38 "redux-thunk": "^2.3.0",
38   - "rxjs": "^5.0.0"
  39 + "rxjs": "^5.0.0",
  40 + "uuid": "^3.2.1"
39 41 }
40 42 }
... ...
  1 +import EC from 'elliptic'
1 2 import { tap } from 'ramda'
2   -import { SENATICS_URL } from '../../constants'
  3 +import uuid from 'uuid/v1'
  4 +import { Buffer } from 'buffer/'
  5 +import { ownPrivate } from '../../constants/keys'
  6 +import { BACKEND_URL } from '../../constants'
  7 +
  8 +const ec = new EC.ec('secp256k1')
  9 +const clientPriv = ec.keyFromPrivate(ownPrivate)
3 10
4 11 export const ACCESS_TOKEN_REQUESTED = 'ACCESS_TOKEN_REQUESTED'
5 12 export const accessTokenRequested = () => ({
... ... @@ -17,24 +24,19 @@ export const accessTokenReceived = (accessToken) => ({
17 24 })
18 25
19 26
20   -const ACCESS_TOKEN_URL = `${SENATICS_URL}/auth/token`
21   -export const fetchToken = (token, secret) => (dispatch) => {
  27 +export const fetchToken = () => (dispatch) => {
22 28 dispatch(accessTokenRequested())
23   -
24   - const payload = {
25   - clientSecret: secret
26   - }
27   -
  29 + const nonce = uuid()
  30 + const firma = new Buffer(clientPriv.sign(nonce).toDER()).toString('hex')
28 31 const options = {
29   - method: 'POST',
30   - body: JSON.stringify(payload),
  32 + method: 'GET',
31 33 headers: {
32 34 Accept: 'application/json',
33 35 'Content-Type': 'application/json',
34   - Authorization: `Basic ${token}`
35 36 },
36 37 }
37 38
  39 + const ACCESS_TOKEN_URL = `${BACKEND_URL}/token/${nonce}/${firma}`
38 40 const internal = () => fetch(ACCESS_TOKEN_URL, options)
39 41 .then(response => response.text())
40 42 .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
... ...
... ... @@ -6,9 +6,9 @@ import { fetchTipoRespuestas } from './tipoRespuestas'
6 6 import { comprobarExistenciaUsuarioLocal } from './usuario'
7 7
8 8 export const INICIALIZAR_DATOS = 'INICIALIZAR_DATOS'
9   -export const inicializarDatos = (authToken, secret) =>
  9 +export const inicializarDatos = () =>
10 10 (dispatch, getState) =>
11   - dispatch(fetchToken(authToken, secret))
  11 + dispatch(fetchToken())
12 12 .then(() => {
13 13 const accessToken = getState().autenticacion.token
14 14 return Promise.all([
... ...
  1 +import EC from 'elliptic'
1 2 import { Permissions, Location } from 'expo'
  3 +import { tap } from 'ramda'
  4 +import { Buffer } from 'buffer/'
  5 +import { ownPrivate } from '../../constants/keys'
  6 +import { BACKEND_URL } from '../../constants'
  7 +
  8 +const ec = new EC.ec('secp256k1')
  9 +const clientPriv = ec.keyFromPrivate(ownPrivate)
2 10
3 11 export const LOCATION_PERMISSION_REQUESTED = 'LOCATION_PERMISSION_REQUESTED'
4 12 export const locationPermissionRequested = () => ({
... ... @@ -6,8 +14,9 @@ export const locationPermissionRequested = () => ({
6 14 })
7 15
8 16 export const LOCATION_PERMISSION_GRANTED = 'LOCATION_PERMISSION_GRANTED'
9   -export const locationPermissionGranted = () => ({
10   - type: LOCATION_PERMISSION_GRANTED
  17 +export const locationPermissionGranted = (solicitud) => ({
  18 + type: LOCATION_PERMISSION_GRANTED,
  19 + solicitud
11 20 })
12 21
13 22 export const LOCATION_PERMISSION_DENIED = 'LOCATION_PERMISSION_DENIED'
... ... @@ -21,12 +30,12 @@ export const locationPermissionFailed = (error) => ({
21 30 error
22 31 })
23 32
24   -export const requestLocationPermission = () => (dispatch) => {
  33 +export const requestLocationPermission = (solicitud) => (dispatch) => {
25 34 dispatch(locationPermissionRequested())
26 35 return Permissions.askAsync(Permissions.LOCATION)
27 36 .then(({ status }) => {
28 37 if (status === 'granted') {
29   - return dispatch(locationPermissionGranted())
  38 + return dispatch(locationPermissionGranted(solicitud))
30 39 }
31 40 return dispatch(locationPermissionDenied())
32 41 })
... ... @@ -40,9 +49,10 @@ export const locationRequested = () => ({
40 49 })
41 50
42 51 export const LOCATION_RECEIVED = 'LOCATION_RECEIVED'
43   -export const locationReceived = (location) => ({
  52 +export const locationReceived = ({ location, solicitud }) => ({
44 53 type: LOCATION_RECEIVED,
45   - location
  54 + location,
  55 + solicitud
46 56 })
47 57
48 58 export const LOCATION_FAILED = 'LOCATION_FAILED'
... ... @@ -51,9 +61,53 @@ export const locationFailed = (error) => ({
51 61 error
52 62 })
53 63
54   -export const requestLocation = () => (dispatch) => {
  64 +export const requestLocation = (solicitud) => (dispatch) => {
55 65 dispatch(locationRequested())
56 66 Location.getCurrentPositionAsync({})
57   - .then(location => dispatch(locationReceived(location)))
  67 + .then(location => dispatch(locationReceived({ location, solicitud })))
58 68 .catch(error => dispatch(locationFailed(error)))
59 69 }
  70 +
  71 +
  72 +export const LOCATION_POSTED = 'LOCATION_POSTED'
  73 +export const locationPosted = () => ({
  74 + type: LOCATION_POSTED
  75 +})
  76 +export const LOCATION_POST_FAILED = 'LOCATION_POST_FAILED'
  77 +export const locationPostFailed = (error) => ({
  78 + type: LOCATION_POST_FAILED,
  79 + error
  80 +})
  81 +export const LOCATION_POST_SUCCESS = 'LOCATION_POST_SUCCESS'
  82 +export const locationPostSuccess = (result) => ({
  83 + type: LOCATION_POST_SUCCESS,
  84 + result
  85 +})
  86 +
  87 +
  88 +export const postLocation = (location, solicitud) => (dispatch) => {
  89 + dispatch(locationPosted())
  90 + const message = JSON.stringify({ location, solicitud })
  91 + const firma = new Buffer(clientPriv.sign(message).toDER()).toString('hex')
  92 + const options = {
  93 + method: 'POST',
  94 + body: message,
  95 + headers: {
  96 + Accept: 'application/json',
  97 + 'Content-Type': 'application/json',
  98 + },
  99 + }
  100 +
  101 + const url = `${BACKEND_URL}/solicitudes/ubicacion/${firma}`
  102 +
  103 + const internal = () => fetch(url, options)
  104 + .then(response => response.text())
  105 + .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
  106 + .then(JSON.parse)
  107 + .then(json => {
  108 + if (json.error) return dispatch(locationPostFailed(json.error))
  109 + return dispatch(locationPostSuccess(json))
  110 + })
  111 + .catch(error => dispatch(locationPostFailed(error)))
  112 + return internal()
  113 +}
... ...
  1 +import EC from 'elliptic'
  2 +import { tap } from 'ramda'
  3 +import { Buffer } from 'buffer/'
  4 +import { BACKEND_URL } from '../../constants'
  5 +import { ownPrivate } from '../../constants/keys'
  6 +
  7 +const ec = new EC.ec('secp256k1')
  8 +const clientPriv = ec.keyFromPrivate(ownPrivate)
  9 +
  10 +export const REPORTE_REQUESTED = 'REPORTE_REQUESTED'
  11 +export const reporteRequested = () => ({
  12 + type: REPORTE_REQUESTED
  13 +})
  14 +
  15 +export const REPORTE_RECEIVED = 'REPORTE_RECEIVED'
  16 +export const reporteReceived = ({ reporte }) => ({
  17 + type: REPORTE_RECEIVED,
  18 + reporte,
  19 +})
  20 +
  21 +export const REPORTE_FAILED = 'REPORTE_FAILED'
  22 +export const reporteFailed = (error) => ({
  23 + type: REPORTE_FAILED,
  24 + error
  25 +})
  26 +
  27 +export const requestReporte = (id, email) => (dispatch) => {
  28 + dispatch(reporteRequested())
  29 + const url = `${BACKEND_URL}/reportes/solicitud/${id}/usuario/${email}/`
  30 + const options = {
  31 + method: 'GET',
  32 + headers: {
  33 + Accept: 'application/json',
  34 + },
  35 + }
  36 +
  37 + return fetch(url, options)
  38 + .then(response => response.text())
  39 + .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
  40 + .then(JSON.parse)
  41 + .then(json => dispatch(reporteReceived(json)))
  42 + .catch(error => dispatch(reporteFailed(error)))
  43 +}
  44 +
  45 +
  46 +export const REPORTE_POSTED = 'REPORTE_POSTED'
  47 +export const reportePosted = (reporte) => ({
  48 + type: REPORTE_POSTED,
  49 + reporte
  50 +})
  51 +export const REPORTE_POST_FAILED = 'REPORTE_POST_FAILED'
  52 +export const reportePostFailed = () => ({
  53 + type: REPORTE_POST_FAILED
  54 +})
  55 +export const REPORTE_POST_SUCCESS = 'REPORTE_POST_SUCCESS'
  56 +export const reportePostSuccess = (result) => ({
  57 + type: REPORTE_POST_SUCCESS,
  58 + result
  59 +})
  60 +
  61 +
  62 +export const postReporte = (solicitudId, usuario, reporte) => (dispatch) => {
  63 + dispatch(reportePosted(reporte))
  64 + const message = JSON.stringify({ reporte, usuario })
  65 + const firma = new Buffer(clientPriv.sign(message).toDER()).toString('hex')
  66 +
  67 + const options = {
  68 + method: 'POST',
  69 + body: message,
  70 + headers: {
  71 + Accept: 'application/json',
  72 + 'Content-Type': 'application/json',
  73 + },
  74 + }
  75 +
  76 +
  77 + const url = `${BACKEND_URL}/solicitudes/${solicitudId}/reporte/${firma}`
  78 +
  79 + const internal = () => fetch(url, options)
  80 + .then(response => response.text())
  81 + .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
  82 + .then(JSON.parse)
  83 + .then(json => {
  84 + if (json.error) return dispatch(reportePostFailed(json.error))
  85 + return dispatch(reportePostSuccess(json))
  86 + })
  87 + .catch(error => dispatch(reportePostFailed(error)))
  88 + return internal()
  89 +}
... ...
... ... @@ -100,8 +100,9 @@ export const fetchFlujo = (token, solicitudId) => (
100 100
101 101
102 102 export const SOLICITUD_POSTED = 'SOLICITUD_POSTED'
103   -export const solicitudPosted = () => ({
104   - type: SOLICITUD_POSTED
  103 +export const solicitudPosted = (solicitud) => ({
  104 + type: SOLICITUD_POSTED,
  105 + solicitud
105 106 })
106 107 export const SOLICITUD_POST_FAILED = 'SOLICITUD_POST_FAILED'
107 108 export const solicitudPostFailed = () => ({
... ... @@ -124,7 +125,7 @@ export const notifySolicitud = ({ titulo, institucion }) => () =>
124 125 })
125 126
126 127 export const postSolicitud = (token, solicitud) => (dispatch) => {
127   - dispatch(solicitudPosted())
  128 + dispatch(solicitudPosted(solicitud))
128 129 const options = {
129 130 method: 'POST',
130 131 body: JSON.stringify(solicitud),
... ... @@ -142,7 +143,10 @@ export const postSolicitud = (token, solicitud) => (dispatch) => {
142 143 .then(response => response.text())
143 144 .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
144 145 .then(JSON.parse)
145   - .then(json => dispatch(solicitudPostSuccess(json)))
  146 + .then(json => {
  147 + if (json.error) return dispatch(solicitudPostFailed(json.error))
  148 + return dispatch(solicitudPostSuccess(json))
  149 + })
146 150 .catch(error => dispatch(solicitudPostFailed(error)))
147 151 return internal()
148 152 }
... ...
  1 +import EC from 'elliptic'
  2 +import { tap } from 'ramda'
  3 +import { Buffer } from 'buffer/'
  4 +import { BACKEND_URL } from '../../constants'
  5 +import { ownPrivate } from '../../constants/keys'
  6 +
  7 +const ec = new EC.ec('secp256k1')
  8 +const clientPriv = ec.keyFromPrivate(ownPrivate)
  9 +
  10 +export const VALORACION_REQUESTED = 'VALORACION_REQUESTED'
  11 +export const valoracionRequested = () => ({
  12 + type: VALORACION_REQUESTED
  13 +})
  14 +
  15 +export const VALORACION_RECEIVED = 'VALORACION_RECEIVED'
  16 +export const valoracionReceived = ({ valoracion }) => ({
  17 + type: VALORACION_RECEIVED,
  18 + valoracion,
  19 +})
  20 +
  21 +export const VALORACION_FAILED = 'VALORACION_FAILED'
  22 +export const valoracionFailed = (error) => ({
  23 + type: VALORACION_FAILED,
  24 + error
  25 +})
  26 +
  27 +export const requestValoracion = (id, email) => (dispatch) => {
  28 + dispatch(valoracionRequested())
  29 + const url = `${BACKEND_URL}/valoraciones/solicitud/${id}/usuario/${email}/`
  30 + const options = {
  31 + method: 'GET',
  32 + headers: {
  33 + Accept: 'application/json',
  34 + },
  35 + }
  36 +
  37 + return fetch(url, options)
  38 + .then(response => response.text())
  39 + .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
  40 + .then(JSON.parse)
  41 + .then(json => dispatch(valoracionReceived(json)))
  42 + .catch(error => dispatch(valoracionFailed(error)))
  43 +}
  44 +
  45 +
  46 +export const VALORACION_POSTED = 'VALORACION_POSTED'
  47 +export const valoracionPosted = (valoracion) => ({
  48 + type: VALORACION_POSTED,
  49 + valoracion
  50 +})
  51 +export const VALORACION_POST_FAILED = 'VALORACION_POST_FAILED'
  52 +export const valoracionPostFailed = () => ({
  53 + type: VALORACION_POST_FAILED
  54 +})
  55 +export const VALORACION_POST_SUCCESS = 'VALORACION_POST_SUCCESS'
  56 +export const valoracionPostSuccess = (result) => ({
  57 + type: VALORACION_POST_SUCCESS,
  58 + result
  59 +})
  60 +
  61 +
  62 +export const postValoracion = (solicitudId, usuario, valoracion) => (dispatch) => {
  63 + dispatch(valoracionPosted(valoracion))
  64 + const message = JSON.stringify({ valoracion, usuario })
  65 + const firma = new Buffer(clientPriv.sign(message).toDER()).toString('hex')
  66 +
  67 + const options = {
  68 + method: 'POST',
  69 + body: message,
  70 + headers: {
  71 + Accept: 'application/json',
  72 + 'Content-Type': 'application/json',
  73 + },
  74 + }
  75 +
  76 +
  77 + const url = `${BACKEND_URL}/solicitudes/${solicitudId}/valoracion/${firma}`
  78 +
  79 + const internal = () => fetch(url, options)
  80 + .then(response => response.text())
  81 + .then(tap(text => dispatch({ type: 'TEXT_RECEIVED', text })))
  82 + .then(JSON.parse)
  83 + .then(json => {
  84 + if (json.error) return dispatch(valoracionPostFailed(json.error))
  85 + return dispatch(valoracionPostSuccess(json))
  86 + })
  87 + .catch(error => dispatch(valoracionPostFailed(error)))
  88 + return internal()
  89 +}
... ...
  1 +import {
  2 + ACCESS_TOKEN_RECEIVED,
  3 + fetchToken
  4 +} from '../actions/autenticacion'
  5 +
  6 +export const renewToken = (action$) =>
  7 + action$.ofType(ACCESS_TOKEN_RECEIVED)
  8 + .delay(60000)
  9 + .map(fetchToken)
... ...
... ... @@ -11,6 +11,10 @@ import {
11 11 } from './logger'
12 12
13 13 import {
  14 + renewToken
  15 +} from './autenticacion'
  16 +
  17 +import {
14 18 askLocationOnPost,
15 19 getLocationOnGrant,
16 20 postLocationOnReceive,
... ... @@ -28,5 +32,6 @@ export default combineEpics(
28 32 postLocationOnReceive,
29 33 searchDebounce,
30 34 changePage,
31   - getUserIfExists
  35 + getUserIfExists,
  36 + renewToken
32 37 )
... ...
... ... @@ -5,19 +5,19 @@ import {
5 5 import {
6 6 requestLocation,
7 7 requestLocationPermission,
  8 + postLocation,
8 9 LOCATION_PERMISSION_GRANTED,
9 10 LOCATION_RECEIVED
10 11 } from '../actions/location'
11 12
12 13 export const askLocationOnPost = (action$) =>
13 14 action$.ofType(SOLICITUD_POSTED)
14   - .map(requestLocationPermission)
  15 + .map(action => requestLocationPermission(action.solicitud))
15 16
16 17 export const getLocationOnGrant = (action$) =>
17 18 action$.ofType(LOCATION_PERMISSION_GRANTED)
18   - .map(requestLocation)
  19 + .map(action => requestLocation(action.solicitud))
19 20
20 21 export const postLocationOnReceive = (action$) =>
21 22 action$.ofType(LOCATION_RECEIVED)
22   - .do(console.log)
23   - .ignoreElements()
  23 + .map(action => postLocation(action.location, action.solicitud))
... ...
... ... @@ -29,8 +29,3 @@ export const searchDebounce = (action$, store) =>
29 29 export const changePage = (action$, store) =>
30 30 action$.ofType(CAMBIAR_PAGINA_SOLICITUD)
31 31 .map(() => fetchSolicitudes(getToken(store), getBusqueda(store)))
32   -
33   -export const logAll = (action$) =>
34   - action$
35   - .do(console.log)
36   - .ignoreElements()
... ...
... ... @@ -78,4 +78,3 @@ export const getUserIfExists = (action$, store) =>
78 78 seleccionarDepartamento(distrito.departamento.id)
79 79 ])
80 80 })
81   - .do(console.log)
... ...
... ... @@ -3,6 +3,7 @@ import { merge } from 'ramda'
3 3 import {
4 4 REQUEST_FORMATOS,
5 5 RECEIVE_FORMATOS,
  6 + ERROR_FORMATOS
6 7 } from '../actions/formatos'
7 8
8 9
... ... @@ -22,6 +23,10 @@ export default (state, action) => {
22 23 return merge(
23 24 state,
24 25 { loading: false, data: action.formatos, pages: action.pages })
  26 + case ERROR_FORMATOS:
  27 + return merge(
  28 + state,
  29 + { loading: false, data: [], pages: null })
25 30 default:
26 31 return state || formatos
27 32 }
... ...
... ... @@ -9,6 +9,8 @@ import tipoRespuestas from './tipoRespuestas'
9 9 import usuario from './usuario'
10 10 import events from './events'
11 11 import auxiliares from './auxiliares'
  12 +import valoracion from './valoraciones'
  13 +import reporte from './reportes'
12 14
13 15
14 16 export default combineReducers({
... ... @@ -21,5 +23,7 @@ export default combineReducers({
21 23 tipoRespuestas,
22 24 usuario,
23 25 events,
24   - auxiliares
  26 + auxiliares,
  27 + reporte,
  28 + valoracion
25 29 })
... ...
  1 +import { merge } from 'ramda'
  2 +
  3 +import {
  4 + REPORTE_REQUESTED,
  5 + REPORTE_RECEIVED,
  6 + REPORTE_FAILED,
  7 + REPORTE_POST_SUCCESS,
  8 + REPORTE_POSTED,
  9 +} from '../actions/reportes'
  10 +
  11 +
  12 +const reporte = {
  13 + data: [],
  14 + loading: true,
  15 + error: null
  16 +}
  17 +
  18 +export default (state, action) => {
  19 + switch (action.type) {
  20 + case REPORTE_REQUESTED:
  21 + return merge(state, reporte)
  22 + case REPORTE_RECEIVED:
  23 + return merge(state, { loading: false, error: null, data: action.reporte })
  24 + case REPORTE_POSTED:
  25 + return merge(state, { data: action.reporte })
  26 + case REPORTE_POST_SUCCESS:
  27 + return merge(state, { data: action.result.reporte })
  28 + case REPORTE_FAILED:
  29 + return merge(state, { loading: false, data: null, error: action.error })
  30 + default:
  31 + return state || reporte
  32 + }
  33 +}
... ...
1   -import { merge, mergeAll, filter, contains, mergeDeepRight } from 'ramda'
  1 +import {
  2 + contains,
  3 + filter,
  4 + merge,
  5 + mergeDeepRight,
  6 + mergeAll,
  7 +} from 'ramda'
2 8
3 9 import {
4 10 SOLICITUDES_REQUESTED,
5 11 SOLICITUDES_RECEIVED,
6 12 SOLICITUDES_FAILED,
7 13 VER_DETALLE_SOLICITUD,
8   - FLUJOS_RECEIVED, FLUJOS_REQUESTED,
  14 + FLUJOS_RECEIVED, FLUJOS_REQUESTED, FLUJOS_FAILED,
9 15 SOLICITUD_POST_SUCCESS,
10 16 SOLICITUD_POST_FAILED,
11 17 SOLICITUD_POSTED,
... ... @@ -84,6 +90,9 @@ export default (state, action) => {
84 90 case FLUJOS_RECEIVED:
85 91 return merge(state, { flujos: { loading: false, datos: action.flujos } })
86 92
  93 + case FLUJOS_FAILED:
  94 + return merge(state, { flujos: { loading: false, datos: [], error: action.error } })
  95 +
87 96 case SOLICITUD_POST_SUCCESS:
88 97 return merge(state, { post: { loading: false, error: null, result: action.result } })
89 98
... ...
... ... @@ -3,6 +3,7 @@ import { merge } from 'ramda'
3 3 import {
4 4 REQUEST_SOPORTES,
5 5 RECEIVE_SOPORTES,
  6 + ERROR_SOPORTES
6 7 } from '../actions/soportes'
7 8
8 9
... ... @@ -22,6 +23,10 @@ export default (state, action) => {
22 23 return merge(
23 24 state,
24 25 { loading: false, data: action.soportes, pages: action.pages })
  26 + case ERROR_SOPORTES:
  27 + return merge(
  28 + state,
  29 + { loading: false, data: [], pages: null })
25 30 default:
26 31 return state || soportes
27 32 }
... ...
... ... @@ -3,6 +3,7 @@ import { merge } from 'ramda'
3 3 import {
4 4 REQUEST_TIPO_RESPUESTAS,
5 5 RECEIVE_TIPO_RESPUESTAS,
  6 + ERROR_TIPO_RESPUESTAS,
6 7 } from '../actions/tipoRespuestas'
7 8
8 9
... ... @@ -22,6 +23,10 @@ export default (state, action) => {
22 23 return merge(
23 24 state,
24 25 { loading: false, data: action.tipoRespuestas, pages: action.pages })
  26 + case ERROR_TIPO_RESPUESTAS:
  27 + return merge(
  28 + state,
  29 + { loading: false, data: [], pages: null })
25 30 default:
26 31 return state || tipoRespuestas
27 32 }
... ...
  1 +import { merge } from 'ramda'
  2 +
  3 +import {
  4 + VALORACION_REQUESTED,
  5 + VALORACION_RECEIVED,
  6 + VALORACION_FAILED,
  7 + VALORACION_POSTED,
  8 + VALORACION_POST_SUCCESS
  9 +} from '../actions/valoraciones'
  10 +
  11 +
  12 +const valoracion = {
  13 + data: null,
  14 + loading: true,
  15 + error: null
  16 +}
  17 +
  18 +const toggle = (state, val) => {
  19 + if (state.valoracion === val) {
  20 + return null
  21 + }
  22 + return val
  23 +}
  24 +
  25 +export default (state, action) => {
  26 + switch (action.type) {
  27 + case VALORACION_REQUESTED:
  28 + return merge(state, valoracion)
  29 + case VALORACION_POSTED:
  30 + return merge(state, { data: toggle(state, action.valoracion) })
  31 + case VALORACION_POST_SUCCESS:
  32 + return merge(state, { data: action.result.valoracion })
  33 + case VALORACION_RECEIVED:
  34 + return merge(state, { loading: false, error: null, data: action.valoracion })
  35 + case VALORACION_FAILED:
  36 + return merge(state, { loading: false, data: null, error: action.error })
  37 + default:
  38 + return state || valoracion
  39 + }
  40 +}
... ...
1   -import 'moment/locale/es'
2 1 import React, { Component } from 'react'
3   -import { View, Text } from 'react-native'
  2 +import { Button } from 'react-native-elements'
  3 +import { View, Text, Linking } from 'react-native'
4 4 import { connect } from 'react-redux'
  5 +
  6 +import {
  7 + PORTAL_DENUNCIAS_URL,
  8 + TUTORIAL_AIF_URL,
  9 + TUTORIAL_APP
  10 +} from '../../constants'
  11 +
5 12 import css from './style'
6 13 // AIP Movil
7 14
8   -class Estadistica extends Component {
9   - static navigationOptions = () => ({
  15 +class Ayuda extends Component {
  16 + static navigationOptions = ({ navigation }) => ({
10 17 title: 'Ayuda',
11 18 headerStyle: css.headerStyle,
12   - headerTitleStyle: css.headerTitleStyle
  19 + headerTitleStyle: css.headerTitleStyle,
  20 + headerLeft:
  21 + <Button
  22 + icon={{ name: 'menu', size: 27, color: 'white' }}
  23 + buttonStyle={{ backgroundColor: 'transparent' }}
  24 + onPress={() => navigation.toggleDrawer()}
  25 + />
13 26 });
  27 + myHandlePress = (link) => () => {
  28 + Linking.openURL(link)
  29 + }
14 30
15 31
16 32 render() {
17 33 return (
18   - <View style={[css.pane, css.fixStatusBar]}>
19   - <Text> AYUDA </Text>
  34 + <View style={[css.pane, css.distribute]}>
  35 + <View>
  36 + <Text style={css.label}>
  37 + ¿Quieres saber más?
  38 + </Text>
  39 + <Button
  40 + buttonStyle={css.btn}
  41 + onPress={this.myHandlePress(TUTORIAL_AIF_URL)}
  42 + title="Ver Tutorial de Información Pública"
  43 + />
  44 + </View>
  45 + <View>
  46 + <Text style={css.label}>
  47 + Aun no sé como usar la App
  48 + </Text>
  49 + <Button
  50 + buttonStyle={css.btn}
  51 + onPress={this.myHandlePress(TUTORIAL_APP)}
  52 + title="¿Cómo usar la App?"
  53 + />
  54 + </View>
  55 + <View>
  56 + <Text style={css.label}>
  57 + No quiero información... Quiero Denunciar!
  58 + </Text>
  59 + <Button
  60 + buttonStyle={css.btn}
  61 + onPress={this.myHandlePress(PORTAL_DENUNCIAS_URL)}
  62 + title="Ir al Portal de Denuncias"
  63 + />
  64 + </View>
20 65 </View>
21 66 )
22 67 }
... ... @@ -28,4 +73,4 @@ const mapStateToProps = () => ({
28 73 const mapDispatchToProps = () => ({
29 74 })
30 75
31   -export default connect(mapStateToProps, mapDispatchToProps)(Estadistica)
  76 +export default connect(mapStateToProps, mapDispatchToProps)(Ayuda)
... ...
1 1 import { StyleSheet } from 'react-native'
2 2 import { merge } from 'ramda'
3 3
4   -import styles from '../../common/styles'
  4 +import styles, { colors } from '../../common/styles'
5 5
6 6 export default merge(StyleSheet.create({
7   -
  7 + distribute: {
  8 + justifyContent: 'space-evenly',
  9 + },
  10 + btn: {
  11 + backgroundColor: colors.primary,
  12 + margin: 10,
  13 + },
8 14 }), styles)
... ...
... ... @@ -29,8 +29,6 @@ const navegarSiguiente = (self, texto) => {
29 29
30 30 if (validateEmail(texto)) {
31 31 self.props.navigation.dispatch(pushAction)
32   - } else {
33   - console.log('no pasar')
34 32 }
35 33 }
36 34
... ...
... ... @@ -35,8 +35,6 @@ const navegarSiguiente = (self, requeridos) => {
35 35 if (validateNext(requeridos)) {
36 36 self.props.navigation.dispatch(resetAction)
37 37 self.props.guardarUsuarioLocal(self.props.usuario)
38   - } else {
39   - console.log('escribir mensajes de validacion')
40 38 }
41 39 }
42 40
... ...
... ... @@ -7,18 +7,25 @@ import {
7 7 } from 'react-native'
8 8 import { connect } from 'react-redux'
9 9 import { HeaderBackButton } from 'react-navigation'
10   -import { map } from 'ramda'
11   -
  10 +import { map, contains } from 'ramda'
12 11 import { Card, Divider, Text, Button } from 'react-native-elements'
13 12 import HTML from 'react-native-render-html'
14 13
15   -import { INFO_PY_URL } from '../../constants'
  14 +import { INFO_PY_URL, ESTADOS_SOLICITUDES } from '../../constants'
16 15 import { fetchFlujo } from '../../redux/actions/solicitudes'
  16 +import { requestValoracion } from '../../redux/actions/valoraciones'
  17 +import { requestReporte } from '../../redux/actions/reportes'
  18 +
17 19 import css from './style'
18 20 import DefaultIndicator from '../../components/DefaultIndicator'
  21 +import BotonValoracion from '../../components/BotonValoracion'
19 22
20 23 const makeSolicitudURL = (solicitud) => `${INFO_PY_URL}/#!/ciudadano/solicitud/${solicitud.id}`
21 24
  25 +const esFinal = (solicitud) => contains(
  26 + solicitud.estado.nombre,
  27 + ESTADOS_SOLICITUDES.finalizados)
  28 +
22 29 class PaginaDetalle extends Component {
23 30 static navigationOptions = ({ navigation }) => ({
24 31 title: 'Detalles de solicitud',
... ... @@ -37,6 +44,8 @@ class PaginaDetalle extends Component {
37 44
38 45 componentDidMount() {
39 46 this.props.fetchFlujo(this.props.token, this.props.solicitud.id)
  47 + this.props.requestValoracion(this.props.solicitud.id, this.props.miUsuario.mail)
  48 + this.props.requestReporte(this.props.solicitud.id, this.props.miUsuario.mail)
40 49 }
41 50
42 51 render() {
... ... @@ -84,6 +93,11 @@ class PaginaDetalle extends Component {
84 93 </Card>
85 94 {props.flujos.loading ? spinner : flujos(props.flujos.datos)}
86 95 </ScrollView>
  96 + <BotonValoracion
  97 + mostrar={esFinal(props.solicitud)}
  98 + reporte={props.reporte}
  99 + valoracion={props.valoracion}
  100 + />
87 101 </View>
88 102 )
89 103 }
... ... @@ -94,10 +108,15 @@ const mapStateToProps = (state) => ({
94 108 token: state.autenticacion.token,
95 109 flujos: state.solicitudes.flujos,
96 110 usuario: state.solicitudes.current.usuario,
  111 + miUsuario: state.usuario.datos,
  112 + valoracion: state.valoracion,
  113 + repote: state.reporte
97 114 })
98 115
99 116 const mapDispatchToProps = dispatch => ({
100   - fetchFlujo: (token, solicitudId) => dispatch(fetchFlujo(token, solicitudId))
  117 + fetchFlujo: (token, solicitudId) => dispatch(fetchFlujo(token, solicitudId)),
  118 + requestValoracion: (solicitud, usuario) => dispatch(requestValoracion(solicitud, usuario)),
  119 + requestReporte: (solicitud, usuario) => dispatch(requestReporte(solicitud, usuario))
101 120 })
102 121
103 122 export default connect(mapStateToProps, mapDispatchToProps)(PaginaDetalle)
... ...
... ... @@ -22,8 +22,6 @@ const navegarSiguiente = (self, requeridos) => {
22 22
23 23 if (validateNext(requeridos)) {
24 24 self.props.navigation.dispatch(pushAction)
25   - } else {
26   - console.log('no pasar')
27 25 }
28 26 }
29 27
... ...
... ... @@ -33,12 +33,10 @@ const makePicker = (onChange, dato, current) => (
33 33 const validateNext = none(either(isNil, isEmpty))
34 34 const navigateNext = (self, requeridos) => {
35 35 if (validateNext(requeridos)) {
36   - const pushAction = StackActions.push({
37   - routeName: 'ConfirmarSolicitud'
38   - })
39   - self.props.navigation.dispatch(pushAction)
40   - } else {
41   - console.log('no pasar')
  36 + const pushAction = StackActions.push({
  37 + routeName: 'ConfirmarSolicitud'
  38 + })
  39 + self.props.navigation.dispatch(pushAction)
42 40 }
43 41 }
44 42
... ...
... ... @@ -7,9 +7,6 @@ import { connect } from 'react-redux'
7 7 import { NavigationActions, StackActions } from 'react-navigation'
8 8 import { all, not, identity } from 'ramda'
9 9
10   -
11   -import { AUTH_TOKEN, CLIENT_SECRET } from '../../constants'
12   -
13 10 import DefaultIndicator from '../../components/DefaultIndicator'
14 11
15 12 import { inicializarDatos } from '../../redux/actions/inicio'
... ... @@ -34,7 +31,7 @@ const goToUserDetails = StackActions.reset({
34 31 class Splash extends Component {
35 32
36 33 componentWillMount() {
37   - this.props.inicializarDatos(AUTH_TOKEN, CLIENT_SECRET)
  34 + this.props.inicializarDatos()
38 35 }
39 36
40 37
... ... @@ -81,7 +78,7 @@ const mapStateToProps = (state) => ({
81 78 })
82 79
83 80 const mapDispatchToProps = (dispatch) => ({
84   - inicializarDatos: (token, secret) => dispatch(inicializarDatos(token, secret)),
  81 + inicializarDatos: () => dispatch(inicializarDatos()),
85 82 })
86 83
87 84 export default connect(mapStateToProps, mapDispatchToProps)(Splash)
... ...
... ... @@ -1258,12 +1258,14 @@
1258 1258 "balanced-match": {
1259 1259 "version": "1.0.0",
1260 1260 "bundled": true,
1261   - "dev": true
  1261 + "dev": true,
  1262 + "optional": true
1262 1263 },
1263 1264 "brace-expansion": {
1264 1265 "version": "1.1.11",
1265 1266 "bundled": true,
1266 1267 "dev": true,
  1268 + "optional": true,
1267 1269 "requires": {
1268 1270 "balanced-match": "^1.0.0",
1269 1271 "concat-map": "0.0.1"
... ... @@ -1278,17 +1280,20 @@
1278 1280 "code-point-at": {
1279 1281 "version": "1.1.0",
1280 1282 "bundled": true,
1281   - "dev": true
  1283 + "dev": true,
  1284 + "optional": true
1282 1285 },
1283 1286 "concat-map": {
1284 1287 "version": "0.0.1",
1285 1288 "bundled": true,
1286   - "dev": true
  1289 + "dev": true,
  1290 + "optional": true
1287 1291 },
1288 1292 "console-control-strings": {
1289 1293 "version": "1.1.0",
1290 1294 "bundled": true,
1291   - "dev": true
  1295 + "dev": true,
  1296 + "optional": true
1292 1297 },
1293 1298 "core-util-is": {
1294 1299 "version": "1.0.2",
... ... @@ -1405,7 +1410,8 @@
1405 1410 "inherits": {
1406 1411 "version": "2.0.3",
1407 1412 "bundled": true,
1408   - "dev": true
  1413 + "dev": true,
  1414 + "optional": true
1409 1415 },
1410 1416 "ini": {
1411 1417 "version": "1.3.5",
... ... @@ -1417,6 +1423,7 @@
1417 1423 "version": "1.0.0",
1418 1424 "bundled": true,
1419 1425 "dev": true,
  1426 + "optional": true,
1420 1427 "requires": {
1421 1428 "number-is-nan": "^1.0.0"
1422 1429 }
... ... @@ -1431,6 +1438,7 @@
1431 1438 "version": "3.0.4",
1432 1439 "bundled": true,
1433 1440 "dev": true,
  1441 + "optional": true,
1434 1442 "requires": {
1435 1443 "brace-expansion": "^1.1.7"
1436 1444 }
... ... @@ -1438,12 +1446,14 @@
1438 1446 "minimist": {
1439 1447 "version": "0.0.8",
1440 1448 "bundled": true,
1441   - "dev": true
  1449 + "dev": true,
  1450 + "optional": true
1442 1451 },
1443 1452 "minipass": {
1444 1453 "version": "2.2.4",
1445 1454 "bundled": true,
1446 1455 "dev": true,
  1456 + "optional": true,
1447 1457 "requires": {
1448 1458 "safe-buffer": "^5.1.1",
1449 1459 "yallist": "^3.0.0"
... ... @@ -1462,6 +1472,7 @@
1462 1472 "version": "0.5.1",
1463 1473 "bundled": true,
1464 1474 "dev": true,
  1475 + "optional": true,
1465 1476 "requires": {
1466 1477 "minimist": "0.0.8"
1467 1478 }
... ... @@ -1542,7 +1553,8 @@
1542 1553 "number-is-nan": {
1543 1554 "version": "1.0.1",
1544 1555 "bundled": true,
1545   - "dev": true
  1556 + "dev": true,
  1557 + "optional": true
1546 1558 },
1547 1559 "object-assign": {
1548 1560 "version": "4.1.1",
... ... @@ -1554,6 +1566,7 @@
1554 1566 "version": "1.4.0",
1555 1567 "bundled": true,
1556 1568 "dev": true,
  1569 + "optional": true,
1557 1570 "requires": {
1558 1571 "wrappy": "1"
1559 1572 }
... ... @@ -1675,6 +1688,7 @@
1675 1688 "version": "1.0.2",
1676 1689 "bundled": true,
1677 1690 "dev": true,
  1691 + "optional": true,
1678 1692 "requires": {
1679 1693 "code-point-at": "^1.0.0",
1680 1694 "is-fullwidth-code-point": "^1.0.0",
... ...
  1 +<!doctype html>
  2 +<html class="no-js" lang="">
  3 +
  4 +<head>
  5 + <meta charset="utf-8">
  6 + <meta http-equiv="x-ua-compatible" content="ie=edge">
  7 + <title></title>
  8 + <meta name="description" content="">
  9 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  10 +
  11 +</head>
  12 +
  13 +<body>
  14 + <p>Hello world! This is HTML5 Boilerplate.</p>
  15 +</body>
  16 +
  17 +</html>
... ...
1   -body {
2   - padding: 50px;
3   - font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4   -}
5   -
6   -a {
7   - color: #00B7FF;
8   -}
Please register or login to post a comment