View | Details | Raw Unified | Return to bug 30160
Collapse All | Expand All

(-)a/gulpfile.js (-11 / +34 lines)
Lines 11-16 const util = require('util'); Link Here
11
const tap = require( "gulp-tap" );
11
const tap = require( "gulp-tap" );
12
const browserify = require( "browserify" );
12
const browserify = require( "browserify" );
13
const babelify = require( "babelify" );
13
const babelify = require( "babelify" );
14
const watchify = require('watchify');
15
const source = require('vinyl-source-stream');
14
16
15
const sass = require("gulp-sass");
17
const sass = require("gulp-sass");
16
const cssnano = require("gulp-cssnano");
18
const cssnano = require("gulp-cssnano");
Lines 86-103 function build() { Link Here
86
    }
88
    }
87
}
89
}
88
90
89
function build_js(){
90
91
    let bundler = browserify().transform(babelify, {presets: ["@babel/preset-env", "@babel/preset-react"]});
92
93
    return src( js_base + '/src/index.js' )
94
        .pipe( tap( file => {
95
            let base_start = file.path.indexOf( js_base );
96
            bundler.require( [ "react", "react-dom" ] );
97
            file.contents = bundler.add( file.path ).bundle();
98
        } ) )
99
        .pipe( dest( js_base+ "/built" ) );
100
91
92
function build_js(){
93
  var bundler = browserify({
94
    entries: [js_base + '/src/index.js'],
95
    debug: false,
96
    cache: {},
97
    packageCache: {},
98
    extensions: ['.js', '.json', '.jsx'],
99
    paths: [js_base + 'src'],
100
    fullPaths: false
101
  });
102
103
  bundler = bundler.transform(babelify, {presets: ["@babel/preset-env", "@babel/preset-react"]});
104
105
  function bundle(b) {
106
    var startMs = Date.now();
107
    var db = b.bundle()
108
        .on('error', function(err) {
109
          console.log(err.message);
110
          this.emit('end');
111
        })
112
        .pipe(source('../built/index.js'))
113
    db.pipe(dest(js_base + '/src'));
114
    console.log('Updated bundle file in', (Date.now() - startMs) + 'ms');
115
    return db;
116
  }
117
118
  bundler = watchify(bundler)
119
      .on('update', function() {
120
        bundle(bundler);
121
      });
122
123
  return bundle(bundler);
101
}
124
}
102
125
103
const poTasks = {
126
const poTasks = {
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/Cities.js (-161 lines)
Lines 1-161 Link Here
1
import React, { Component } from 'react'
2
import { Container, Button, Alert } from 'react-bootstrap'
3
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5
import CityList from './cities/CityList'
6
import AddCity from './cities/AddCity'
7
import DeleteCity from './cities/DeleteCity'
8
9
class App extends Component {
10
    constructor(props) {
11
        super(props)
12
        this.state = {
13
            op: 'list',
14
            error: null,
15
            message: null,
16
            city: {},
17
            cities: [],
18
        }
19
    }
20
21
    onCreate() {
22
        this.setState({ op: 'add', city: {}, error: null, message: null })
23
    }
24
25
    addCity = city => {
26
        let apiUrl
27
28
        apiUrl = '/api/v1/cities'
29
30
        const myHeaders = new Headers()
31
        myHeaders.append('Content-Type', 'application/json')
32
33
        let method = 'POST'
34
        if ( city.city_id ) {
35
            method = 'PUT'
36
            apiUrl += '/' + city.city_id
37
        }
38
        delete city.city_id
39
40
        const options = {
41
            method: method,
42
            body: JSON.stringify(city),
43
            myHeaders
44
        }
45
46
        fetch(apiUrl, options)
47
          .then(response => {
48
            if ( response.status == 200 ) {
49
                this.setState({
50
                    message: 'City updated',
51
                    error: null,
52
                    op: 'list',
53
                })
54
            } else if ( response.status == 201 ) {
55
                this.setState({
56
                    message: 'City created',
57
                    error: null,
58
                    op: 'list',
59
                })
60
            } else {
61
                this.setState({ error: response.message })
62
            }
63
          }, (error) => {
64
            this.setState({ error })
65
          })
66
    }
67
68
    editCity = city_id => {
69
70
        const apiUrl = '/api/v1/cities/' + city_id
71
72
        const options = {
73
          method: 'GET',
74
        }
75
76
        fetch(apiUrl, options)
77
          .then(res => res.json())
78
          .then(
79
            (result) => {
80
              this.setState({
81
                city: result,
82
                op: 'add',
83
                message: null,
84
                error: null,
85
              })
86
            }, (error) => {
87
              this.setState({ error })
88
            }
89
          )
90
    }
91
92
    confirmDeleteCity = city_id => {
93
        const apiUrl = '/api/v1/cities/' + city_id
94
95
        const options = {
96
          method: 'GET',
97
        }
98
99
        fetch(apiUrl, options)
100
          .then(res => res.json())
101
          .then(
102
            (result) => {
103
              this.setState({
104
                city: result,
105
                op: 'delete',
106
                message: null,
107
                error: null,
108
              })
109
            }, (error) => {
110
              this.setState({ error })
111
            }
112
          )
113
    }
114
115
    deleteCity = city_id => {
116
117
        const { cities } = this.state
118
        const apiUrl = '/api/v1/cities/' + city_id
119
120
        const options = {
121
          method: 'DELETE',
122
        }
123
124
        fetch(apiUrl, options)
125
          .then(response => {
126
            if ( response.status == 204 ) {
127
                this.setState({
128
                    message: 'City deleted',
129
                    error: null,
130
                    op: 'list',
131
                })
132
            } else {
133
                this.setState({ error: response.message })
134
            }
135
          }, (error) => {
136
              this.setState({ error })
137
          }
138
         )
139
    }
140
141
    cancel = event => {
142
        event.preventDefault()
143
        this.setState({op: 'list'})
144
    }
145
146
    render() {
147
148
        return (
149
          <div className="App">
150
              {this.state.op == 'list' && <div id="toolbar" className="btn-toolbar"><Button id="newcity" variant="default" onClick={() => this.onCreate()}><FontAwesomeIcon icon={faPlus} /> New city</Button></div>}
151
              {this.state.message && <div><Alert key="message_info" variant="info" transition={false}>{this.state.message}</Alert></div>}
152
              {this.state.error && <div><Alert key="message_error" variant="warning" transition={false}>Error: {this.state.error.message}</Alert></div>}
153
              {this.state.op == 'list' && <CityList editCity={this.editCity} confirmDeleteCity={this.confirmDeleteCity} cities={this.state.cities} />}
154
              {this.state.op == 'add' && <AddCity onFormSubmit={this.addCity} onFormCancel={this.cancel} city={this.state.city} />}
155
              {this.state.op == 'delete' && <DeleteCity onFormSubmit={this.deleteCity} onFormCancel={this.cancel} city={this.state.city} />}
156
          </div>
157
        )
158
    }
159
}
160
161
export default App
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/cities/AddCity.js (-121 lines)
Lines 1-121 Link Here
1
import React from 'react';
2
import { Row, Form, Col, Button } from 'react-bootstrap';
3
4
class AddCity extends React.Component {
5
    constructor(props) {
6
        super(props);
7
        this.initialState = {
8
            city_id: '',
9
            name: '',
10
            state: '',
11
            postal_code: '',
12
            country: ''
13
        }
14
15
        if(props.city.city_id){
16
          this.state = props.city
17
        } else {
18
          this.state = this.initialState;
19
        }
20
21
        this.handleChange = this.handleChange.bind(this);
22
        this.handleSubmit = this.handleSubmit.bind(this);
23
        this.handleCancel = this.handleCancel.bind(this);
24
    }
25
26
    handleChange(event) {
27
        const name = event.target.name;
28
        const value = event.target.value;
29
30
        this.setState({
31
            [name]: value
32
        })
33
    }
34
35
    handleSubmit(event) {
36
        event.preventDefault();
37
        this.props.onFormSubmit(this.state);
38
    }
39
40
    handleCancel(event) {
41
        event.preventDefault();
42
        this.props.onFormCancel(event);
43
    }
44
45
    render() {
46
47
        let pageTitle;
48
        if(this.state.city_id) {
49
            pageTitle = <h2>Edit city</h2>
50
        } else {
51
            pageTitle = <h2>New city</h2>
52
        }
53
54
        return(
55
          <div>
56
            {pageTitle}
57
            <Form onSubmit={this.handleSubmit} className="validated">
58
            <fieldset className="rows">
59
              <Form.Group controlId="name" as={Row}>
60
                <Form.Label column sm={4} className="required">City:</Form.Label>
61
                <Col sm={8}>
62
                <Form.Control
63
                  type="text"
64
                  name="name"
65
                  value={this.state.name}
66
                  onChange={this.handleChange}
67
                  placeholder="City name"
68
                  required="required"/>
69
                  <span className="required">Required</span>
70
                  </Col>
71
              </Form.Group>
72
              <Form.Group controlId="state" as={Row}>
73
                <Form.Label column sm={4}>State:</Form.Label>
74
                <Col sm={8}>
75
                <Form.Control
76
                  type="text"
77
                  name="state"
78
                  value={this.state.state}
79
                  onChange={this.handleChange}
80
                  placeholder="City State"/>
81
                  </Col>
82
              </Form.Group>
83
              <Form.Group controlId="postal_code" as={Row}>
84
                <Form.Label column sm={4} className="required">ZIP/Postal code:</Form.Label>
85
                <Col sm={8}>
86
                <Form.Control
87
                  type="text"
88
                  name="postal_code"
89
                  value={this.state.postal_code}
90
                  onChange={this.handleChange}
91
                  placeholder="City postal code"
92
                  required="required"/>
93
                  <span className="required">Required</span>
94
                  </Col>
95
              </Form.Group>
96
              <Form.Group controlId="country" as={Row}>
97
                <Form.Label column sm={4}>Country:</Form.Label>
98
                <Col sm={8}>
99
                <Form.Control
100
                  type="text"
101
                  name="country"
102
                  value={this.state.country}
103
                  onChange={this.handleChange}
104
                  placeholder="City country"/>
105
                  </Col>
106
              </Form.Group>
107
              </fieldset>
108
              <fieldset className="action">
109
              <Form.Group>
110
                <Form.Control type="hidden" name="id" value={this.state.city_id} />
111
                <Button variant="default" type="submit">Submit</Button>
112
                <a href="#" onClick={(e) => this.handleCancel(e)}>Cancel</a>
113
              </Form.Group>
114
              </fieldset>
115
            </Form>
116
           </div>
117
        )
118
    }
119
}
120
121
export default AddCity;
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/cities/DeleteCity.js (-67 lines)
Lines 1-67 Link Here
1
import React from 'react';
2
import { Row, Form, Col, Button } from 'react-bootstrap';
3
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
import { faCheck, faRemove } from '@fortawesome/free-solid-svg-icons'
5
6
class DeleteCity extends React.Component {
7
    constructor(props) {
8
        super(props);
9
        this.state = props.city;
10
11
        this.handleSubmit = this.handleSubmit.bind(this);
12
        this.handleCancel = this.handleCancel.bind(this);
13
    }
14
15
    handleSubmit(event) {
16
        event.preventDefault();
17
        this.props.onFormSubmit(this.state.city_id);
18
    }
19
    handleCancel(event) {
20
        event.preventDefault();
21
        this.props.onFormCancel(event);
22
    }
23
24
    render() {
25
26
        let pageTitle = <h2>Delete city</h2>
27
28
        return(
29
          <div>
30
           <Row>
31
            {pageTitle}
32
            <Form onSubmit={this.handleSubmit}>
33
              <fieldset className="rows">
34
                <h3>Delete city "{this.state.name}"?</h3>
35
                <Form.Group controlId="city_id" as={Row}>
36
                  <Form.Label column sm={4}>City id:</Form.Label>
37
                  <Col sm={8}>{this.state.city_id}</Col>
38
                </Form.Group>
39
                <Form.Group controlId="name" as={Row}>
40
                  <Form.Label column sm={4}>City:</Form.Label>
41
                  <Col sm={8}>{this.state.name}</Col>
42
                </Form.Group>
43
                <Form.Group controlId="state" as={Row}>
44
                  <Form.Label column sm={4}>State:</Form.Label>
45
                  <Col sm={8}>{this.state.state}</Col>
46
                </Form.Group>
47
                <Form.Group controlId="postal_code" as={Row}>
48
                  <Form.Label column sm={4}>ZIP/Postal code:</Form.Label>
49
                  <Col sm={8}>{this.state.postal_code}</Col>
50
                </Form.Group>
51
                <Form.Group controlId="country" as={Row}>
52
                  <Form.Label column sm={4}>Country:</Form.Label>
53
                  <Col sm={8}>{this.state.country}</Col>
54
                </Form.Group>
55
                </fieldset>
56
                <fieldset className="action">
57
                <Button variant="default" className="approve" type="submit"><FontAwesomeIcon icon={faCheck} /> Yes, Delete</Button>
58
                <Button variant="default" className="deny" onClick={(e) => this.handleCancel(e)}><FontAwesomeIcon icon={faRemove} /> No, do not remove</Button>
59
              </fieldset>
60
            </Form>
61
            </Row>
62
          </div>
63
        )
64
    }
65
}
66
67
export default DeleteCity;
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/components/Cities.js (+144 lines)
Line 0 Link Here
1
import React, { useState } from 'react';
2
import { Container, Button, Alert } from 'react-bootstrap'
3
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5
import ListCity from './cities/ListCity'
6
import AddCity from './cities/AddCity'
7
import DeleteCity from './cities/DeleteCity'
8
9
const Cities = () => {
10
    const [op, setOp] = useState('list')
11
    const [error, setError] = useState()
12
    const [message, setMessage] = useState()
13
    const [city, setCity] = useState({})
14
15
    const onCreate = () => {
16
        setOp('add');
17
        setCity({});
18
        setError(null);
19
        setMessage(null);
20
    }
21
22
    const addCity = city => {
23
        let apiUrl
24
25
        apiUrl = '/api/v1/cities'
26
27
        const myHeaders = new Headers()
28
        myHeaders.append('Content-Type', 'application/json')
29
30
        let method = 'POST'
31
        if ( city.city_id ) {
32
            method = 'PUT'
33
            apiUrl += '/' + city.city_id
34
        }
35
        delete city.city_id
36
37
        const options = {
38
            method: method,
39
            body: JSON.stringify(city),
40
            myHeaders
41
        }
42
43
        fetch(apiUrl, options)
44
          .then(response => {
45
            if ( response.status == 200 ) {
46
                setMessage('City updated')
47
                setError(null)
48
                setOp('list')
49
            } else if ( response.status == 201 ) {
50
                setMessage('City created')
51
                setError(null)
52
                setOp('list')
53
            } else {
54
                setError(response.message);
55
            }
56
          }, (e) => {
57
              setError(e);
58
          })
59
    }
60
61
    const editCity = city_id => {
62
63
        const apiUrl = '/api/v1/cities/' + city_id
64
65
        const options = {
66
          method: 'GET',
67
        }
68
69
        fetch(apiUrl, options)
70
          .then(res => res.json())
71
          .then(
72
            (result) => {
73
                setCity(result)
74
                setOp('add')
75
                setMessage(null)
76
                setError(null)
77
            }, (e) => {
78
                setError(e)
79
            }
80
          )
81
    }
82
83
    const confirmDeleteCity = city_id => {
84
        const apiUrl = '/api/v1/cities/' + city_id
85
86
        const options = {
87
          method: 'GET',
88
        }
89
90
        fetch(apiUrl, options)
91
          .then(res => res.json())
92
          .then(
93
            (result) => {
94
                setCity(result)
95
                setOp('delete')
96
                setMessage(null)
97
                setError(null)
98
            }, (e) => {
99
                setError(e);
100
            }
101
          )
102
    }
103
104
    const deleteCity = city_id => {
105
106
        const apiUrl = '/api/v1/cities/' + city_id
107
108
        const options = {
109
          method: 'DELETE',
110
        }
111
112
        fetch(apiUrl, options)
113
          .then(response => {
114
            if ( response.status == 204 ) {
115
                setMessage('City deleted')
116
                setError(null)
117
                setOp('list')
118
            } else {
119
                setError(response.message)
120
            }
121
          }, (e) => {
122
              setError(e)
123
          }
124
         )
125
    }
126
127
    const cancel = event => {
128
        event.preventDefault()
129
        setOp('list')
130
    }
131
132
    return (
133
      <div className="App">
134
          {op == 'list' && <div id="toolbar" className="btn-toolbar"><Button id="newcity" variant="default" onClick={() => onCreate()}><FontAwesomeIcon icon={faPlus} /> New city</Button></div>}
135
          {message && <div><Alert key="message_info" variant="info" transition={false}>{message}</Alert></div>}
136
          {error && <div><Alert key="message_error" variant="warning" transition={false}>Error: {error.message}</Alert></div>}
137
          {op == 'list' && <ListCity editCity={editCity} confirmDeleteCity={confirmDeleteCity} />}
138
          {op == 'add' && <AddCity onFormSubmit={addCity} onFormCancel={cancel} city={city} />}
139
          {op == 'delete' && <DeleteCity onFormSubmit={deleteCity} onFormCancel={cancel} city={city} />}
140
      </div>
141
    )
142
}
143
144
export default Cities
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/components/cities/AddCity.js (+104 lines)
Line 0 Link Here
1
import React, { useState } from 'react';
2
import { Row, Form, Col, Button } from 'react-bootstrap';
3
4
const AddCity = ({ onFormSubmit, onFormCancel, city }) => {
5
6
    const initialState = {
7
        city_id: '',
8
        name: '',
9
        state: '',
10
        postal_code: '',
11
        country: ''
12
    }
13
    const [myCity, setCity] = useState(city ? city : initialState)
14
15
    const handleChange = event => {
16
        const name = event.target.name;
17
        const value = event.target.value;
18
        setCity({...myCity, [name]: value})
19
    }
20
21
    const handleSubmit = event => {
22
        event.preventDefault();
23
        onFormSubmit(myCity);
24
    }
25
26
    const handleCancel = event => {
27
        event.preventDefault();
28
        onFormCancel(event);
29
    }
30
31
    let pageTitle;
32
    if(city.city_id) {
33
        pageTitle = <h2>Edit city</h2>
34
    } else {
35
        pageTitle = <h2>New city</h2>
36
    }
37
38
    return(
39
      <div>
40
        {pageTitle}
41
        <Form onSubmit={handleSubmit} className="validated">
42
        <fieldset className="rows">
43
          <Form.Group controlId="name" as={Row}>
44
            <Form.Label column sm={4} className="required">City:</Form.Label>
45
            <Col sm={8}>
46
            <Form.Control
47
              type="text"
48
              name="name"
49
              value={myCity.name}
50
              onChange={handleChange}
51
              placeholder="City name"
52
              required="required"/>
53
              <span className="required">Required</span>
54
              </Col>
55
          </Form.Group>
56
          <Form.Group controlId="state" as={Row}>
57
            <Form.Label column sm={4}>State:</Form.Label>
58
            <Col sm={8}>
59
            <Form.Control
60
              type="text"
61
              name="state"
62
              value={myCity.state}
63
              onChange={handleChange}
64
              placeholder="City State"/>
65
              </Col>
66
          </Form.Group>
67
          <Form.Group controlId="postal_code" as={Row}>
68
            <Form.Label column sm={4} className="required">ZIP/Postal code:</Form.Label>
69
            <Col sm={8}>
70
            <Form.Control
71
              type="text"
72
              name="postal_code"
73
              value={myCity.postal_code}
74
              onChange={handleChange}
75
              placeholder="City postal code"
76
              required="required"/>
77
              <span className="required">Required</span>
78
              </Col>
79
          </Form.Group>
80
          <Form.Group controlId="country" as={Row}>
81
            <Form.Label column sm={4}>Country:</Form.Label>
82
            <Col sm={8}>
83
            <Form.Control
84
              type="text"
85
              name="country"
86
              value={myCity.country}
87
              onChange={handleChange}
88
              placeholder="City country"/>
89
              </Col>
90
          </Form.Group>
91
          </fieldset>
92
          <fieldset className="action">
93
          <Form.Group>
94
            <Form.Control type="hidden" name="id" value={myCity.city_id} />
95
            <Button variant="default" type="submit">Submit</Button>
96
            <a href="#" onClick={(e) => handleCancel(e)}>Cancel</a>
97
          </Form.Group>
98
          </fieldset>
99
        </Form>
100
       </div>
101
    )
102
}
103
104
export default AddCity;
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/components/cities/DeleteCity.js (+57 lines)
Line 0 Link Here
1
import React from 'react';
2
import { Row, Form, Col, Button } from 'react-bootstrap';
3
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
import { faCheck, faRemove } from '@fortawesome/free-solid-svg-icons'
5
6
const DeleteCity = ({ onFormSubmit, onFormCancel, city }) => {
7
8
    const handleSubmit = event => {
9
        event.preventDefault();
10
        onFormSubmit(city.city_id);
11
    }
12
    const handleCancel = event => {
13
        event.preventDefault();
14
        onFormCancel(event);
15
    }
16
17
    let pageTitle = <h2>Delete city</h2>
18
19
    return(
20
      <div>
21
       <Row>
22
        {pageTitle}
23
        <Form onSubmit={handleSubmit}>
24
          <fieldset className="rows">
25
            <h3>Delete city "{city.name}"?</h3>
26
            <Form.Group controlId="city_id" as={Row}>
27
              <Form.Label column sm={4}>City id:</Form.Label>
28
              <Col sm={8}>{city.city_id}</Col>
29
            </Form.Group>
30
            <Form.Group controlId="name" as={Row}>
31
              <Form.Label column sm={4}>City:</Form.Label>
32
              <Col sm={8}>{city.name}</Col>
33
            </Form.Group>
34
            <Form.Group controlId="state" as={Row}>
35
              <Form.Label column sm={4}>State:</Form.Label>
36
              <Col sm={8}>{city.state}</Col>
37
            </Form.Group>
38
            <Form.Group controlId="postal_code" as={Row}>
39
              <Form.Label column sm={4}>ZIP/Postal code:</Form.Label>
40
              <Col sm={8}>{city.postal_code}</Col>
41
            </Form.Group>
42
            <Form.Group controlId="country" as={Row}>
43
              <Form.Label column sm={4}>Country:</Form.Label>
44
              <Col sm={8}>{city.country}</Col>
45
            </Form.Group>
46
            </fieldset>
47
            <fieldset className="action">
48
            <Button variant="default" className="approve" type="submit"><FontAwesomeIcon icon={faCheck} /> Yes, Delete</Button>
49
            <Button variant="default" className="deny" onClick={(e) => handleCancel(e)}><FontAwesomeIcon icon={faRemove} /> No, do not remove</Button>
50
          </fieldset>
51
        </Form>
52
        </Row>
53
      </div>
54
    )
55
}
56
57
export default DeleteCity;
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/cities/CityList.js (-88 / +46 lines)
Lines 1-51 Link Here
1
import React, { Component } from 'react';
1
import React, { useState, useEffect, useRef } from 'react';
2
import ReactDOM from 'react-dom';
2
import ReactDOM from 'react-dom';
3
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
import { faPencil, faTrash } from '@fortawesome/free-solid-svg-icons'
4
import { faPencil, faTrash } from '@fortawesome/free-solid-svg-icons'
5
import { Button, Alert } from 'react-bootstrap';
5
import { Button, Alert } from 'react-bootstrap';
6
6
7
class EditButton extends Component{
7
const EditButton = ({city_id, handleClick}) => {
8
    constructor(props){
8
    return (
9
        super(props);
9
        <Button variant="default" size="sm" onClick={() => handleClick(city_id)}><FontAwesomeIcon icon={faPencil} size="xs" /> Edit</Button>
10
        this.handleClick = this.handleClick.bind(this);
10
    )
11
    }
12
    handleClick(){
13
        this.props.handleClick(this.props.city_id);
14
    }
15
16
    render(){
17
        return (
18
            <Button variant="default" size="sm" onClick={this.handleClick}><FontAwesomeIcon icon={faPencil} size="xs" /> Edit</Button>
19
        )
20
    }
21
}
11
}
22
12
23
class DeleteButton extends Component{
13
const DeleteButton = ({city_id, handleClick}) => {
24
    constructor(props){
14
    return (
25
        super(props);
15
        <Button variant="default" size="sm" onClick={() => handleClick(city_id)}><FontAwesomeIcon icon={faTrash} size="xs" /> Delete</Button>
26
        this.handleClick = this.handleClick.bind(this);
16
    )
27
    }
28
    handleClick(){
29
        this.props.handleClick(this.props.city_id);
30
    }
31
32
    render(){
33
        return (
34
            <Button variant="default" size="sm" onClick={this.handleClick}><FontAwesomeIcon icon={faTrash} size="xs" /> Delete</Button>
35
        )
36
    }
37
}
17
}
38
18
39
class Table extends Component {
19
const Table = ({editCity, confirmDeleteCity}) => {
40
    constructor(props){
20
    const table_ref = React.createRef()
41
        super(props);
42
    }
43
21
44
    componentDidMount() {
22
    useEffect(() => {
45
23
46
        const editCity = this.props.editCity;
24
        $(table_ref.current).kohaTable({
47
        const confirmDeleteCity = this.props.confirmDeleteCity;
48
        $(this.refs.main).kohaTable({
49
                "ajax": {
25
                "ajax": {
50
                    "url": cities_table_url,
26
                    "url": cities_table_url,
51
                },
27
                },
Lines 106-176 class Table extends Component { Link Here
106
                }
82
                }
107
83
108
            }, columns_settings, 1);
84
            }, columns_settings, 1);
109
    }
110
85
111
    componentWillUnmount(){
86
            return function cleanup(){
112
       $('.data-table-wrapper')
87
               $('.data-table-wrapper')
113
       .find('table')
88
               .find('table')
114
       .DataTable()
89
               .DataTable()
115
       .destroy(true);
90
               .destroy(true);
116
    }
91
            }
117
92
    }, [])
118
    shouldComponentUpdate() {
93
119
        return true;
94
    return (
120
    }
95
        <div>
121
96
            <table ref={table_ref} />
122
    render() {
97
        </div>)
123
        return (
124
            <div>
125
                <table ref="main" />
126
            </div>);
127
    }
128
}
98
}
129
99
130
class CityList extends React.Component {
100
const ListCity = ({ editCity, confirmDeleteCity }) => {
131
    constructor(props) {
101
    const [cities, setCities] = useState()
132
        super(props);
133
        this.state = {
134
            error: null,
135
            cities: [],
136
        }
137
138
        if ( props.cities ){
139
            this.state.cities = props.cities
140
        }
141
    }
142
102
143
    componentDidMount() {
103
    useEffect(() => {
144
        const apiUrl = '/api/v1/cities';
104
        const apiUrl = '/api/v1/cities';
145
105
106
        let r = []
146
        fetch(apiUrl)
107
        fetch(apiUrl)
147
            .then(res => res.json())
108
            .then(res => res.json())
148
            .then(
109
            .then(
149
                (result) => {
110
                (result) => {
150
                    this.setState({
111
                    setCities(result)
151
                        cities: result
152
                    });
153
                },
112
                },
154
                (error) => {
113
                (error) => {
155
                    this.setState({ error });
114
                    console.log(error)
156
                }
115
                }
157
            )
116
            )
158
    }
117
    }, [])
159
118
160
    render() {
119
    if(cities == null ) {
161
        const { error, cities } = this.state;
120
        return (<div>
162
121
                <h1>Cities</h1>
163
        if(error) {
122
                <div>Loading...</div>
164
            return ("Error: {error.message}")
123
            </div>)
165
        } else if(cities.length) {
124
    } else if(cities.length) {
166
            return(<div>
125
        return(<div>
167
                    <h1>Cities</h1>
126
                <h1>Cities</h1>
168
                    <Table editCity={this.props.editCity} confirmDeleteCity={this.props.confirmDeleteCity} />
127
                <Table editCity={editCity} confirmDeleteCity={confirmDeleteCity} />
169
                </div>)
128
            </div>)
170
        } else {
129
    } else {
171
            return(<div><h1>Cities</h1><div className="dialog message">There are no cities defined.</div></div>)
130
        return(<div><h1>Cities</h1><div className="dialog message">There are no cities defined.</div></div>)
172
        }
173
    }
131
    }
174
}
132
}
175
133
176
export default CityList;
134
export default ListCity;
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/index.js (-2 / +2 lines)
Lines 1-5 Link Here
1
import React from 'react';
1
import React from 'react';
2
import ReactDOM from 'react-dom';
2
import ReactDOM from 'react-dom';
3
import App from './Cities';
3
import Cities from './components/Cities.js';
4
4
5
ReactDOM.render(<App />, document.getElementById('cities_container'));
5
ReactDOM.render(<Cities />, document.getElementById('cities_container'));
(-)a/package.json (-2 / +3 lines)
Lines 57-62 Link Here
57
  "author": "",
57
  "author": "",
58
  "license": "GPL-3.0",
58
  "license": "GPL-3.0",
59
  "devDependencies": {
59
  "devDependencies": {
60
    "@types/react": "^17.0.39"
60
    "@types/react": "^17.0.39",
61
    "vinyl-source-stream": "^2.0.0",
62
    "watchify": "^4.0.0"
61
  }
63
  }
62
}
64
}
63
- 

Return to bug 30160