github.com/aitjcize/Overlord@v0.0.0-20240314041920-104a804cf5e8/overlord/app/fixture/js/view.jsx (about)

     1  // Copyright 2015 The Chromium OS Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  //
     5  // Requires:
     6  //   NavBar.jsx :: NavBar
     7  //   UploadProgressWidget.jsx :: UploadProgressWidget
     8  //   FixtureWidget.jsx :: FixtureWidget
     9  //   TerminalWindow.jsx :: TerminalWindow
    10  //
    11  // View for Fixture Dashboard App
    12  // - App
    13  //  - NavBar
    14  //  - UploadProgressWidget
    15  //  - Paginator
    16  //   - [FixtureWidget ...]
    17  
    18  // Identifier for selecting all clients
    19  ALL = "All";
    20  
    21  var App = React.createClass({
    22    mixins: [BaseApp],
    23    onNewClient: function (client) {
    24      var prop = client.properties;
    25  
    26      if (typeof(prop) == "undefined" ||
    27          typeof(prop.context) == "undefined" ||
    28          prop.context.indexOf("ui") === -1) {
    29        return false;
    30      }
    31  
    32      if (typeof(prop.ui) != "undefined" &&
    33          typeof(prop.ui.group) != "undefined") {
    34        if (this.state.groups.indexOf(prop.ui.group) == -1) {
    35          this.setState(function (state, props) {
    36            state.groups.push(prop.ui.group);
    37          });
    38        }
    39      }
    40  
    41      return true;
    42    },
    43    addTerminal: function (id, term) {
    44      this.setState(function (state, props) {
    45        state.terminals[id] = term;
    46      });
    47    },
    48    removeTerminal: function (id) {
    49      this.setState(function (state, props) {
    50        if (typeof(state.terminals[id]) != "undefined") {
    51          delete state.terminals[id];
    52        }
    53      });
    54    },
    55    getInitialState: function () {
    56      return {terminals: {}, groups: [ALL]};
    57    },
    58    componentWillMount: function () {
    59      this.addOnNewClientHandler(this.onNewClient);
    60    },
    61    componentDidMount: function () {
    62      var socket = io(window.location.protocol + "//" + window.location.host,
    63                      {path: "/api/socket.io/"});
    64      socket.on("agent joined", function (msg) {
    65        var obj = JSON.parse(msg);
    66        this.addClient(obj);
    67      }.bind(this));
    68  
    69      socket.on("agent left", function (msg) {
    70        var obj = JSON.parse(msg);
    71        this.removeClient(obj);
    72      }.bind(this));
    73  
    74      // Initiate a file download
    75      socket.on("file download", function (sid) {
    76        var url = window.location.protocol + "//" + window.location.host +
    77                  "/api/file/download/" + sid;
    78        $("<iframe id='" + sid + "' src='" + url + "' style='display:none'>" +
    79          "</iframe>").appendTo('body');
    80      });
    81      this.socket = socket;
    82    },
    83    computePageSize: function () {
    84      // compute how many clients we can put in the screen
    85      var screen = {
    86          width: window.innerWidth,
    87      };
    88  
    89      var nFixturePerRow = Math.floor(
    90         screen.width / (FIXTURE_WINDOW_WIDTH + FIXTURE_WINDOW_MARGIN * 2));
    91      nFixturePerRow = Math.max(1, nFixturePerRow);
    92      var nTotalFixture = Math.min(2 * nFixturePerRow, 8);
    93      return nTotalFixture;
    94    },
    95    render: function () {
    96      var onControl = function (control) {
    97        if (control.type == "sid") {
    98          this.terminal_sid = control.data;
    99          this.props.app.socket.emit("subscribe", control.data);
   100        }
   101      };
   102      var onCloseClicked = function (event) {
   103        this.props.app.removeTerminal(this.props.id);
   104        this.props.app.socket.emit("unsubscribe", this.terminal_sid);
   105      };
   106      return (
   107        <div id="main">
   108          <NavBar name="Fixture Dashboard" url="/api/apps/list" />
   109          <div className="terminals">
   110            {
   111              Object.keys(this.state.terminals).map(function (id) {
   112                var term = this.state.terminals[id];
   113                var extra = "";
   114                if (typeof(term.path) != "undefined") {
   115                  extra = "?tty_device=" + term.path;
   116                }
   117                return (
   118                  <TerminalWindow key={id} mid={term.mid} id={id} title={id}
   119                   path={"/api/agent/tty/" + term.mid + extra}
   120                   uploadRequestPath={"/api/agent/upload/" + term.mid}
   121                   enableMaximize={true}
   122                   app={this} progressBars={this.refs.uploadProgress}
   123                   onControl={onControl} onCloseClicked={onCloseClicked} />
   124                );
   125              }.bind(this))
   126            }
   127          </div>
   128          <div className="upload-progress">
   129            <UploadProgressWidget ref="uploadProgress"/>
   130          </div>
   131          <Paginator header="Clients" app={this}
   132              pageSize={this.computePageSize()}>
   133            {
   134              this.getFilteredClientList().map(function (data) {
   135                return (
   136                  <FixtureWidget key={data.mid} client={data} app={this}
   137                   progressBars={this.refs.uploadProgress}/>
   138                );
   139              }.bind(this))
   140            }
   141          </Paginator>
   142        </div>
   143      );
   144    }
   145  });
   146  
   147  Paginator = React.createClass({
   148    onKeyUp: function (event) {
   149      this.props.app.setMidFilterPattern(this.refs.filter.value);
   150    },
   151    changePage: function (i) {
   152      this.setState(function (state, props) {
   153        state.pageNumber = i;
   154      });
   155    },
   156    getInitialState: function () {
   157      return {pageNumber: 0, selectedGroup: ALL};
   158    },
   159    filterBySelectedGroup: function (client) {
   160      var prop = client.properties;
   161      if (this.state.selectedGroup == ALL)
   162        return true;
   163  
   164      if (typeof(prop.ui) == "undefined")
   165        return false;
   166      return prop.ui.group == this.state.selectedGroup;
   167    },
   168    componentWillMount: function () {
   169      this.props.app.addClientFilter(this.filterBySelectedGroup);
   170    },
   171    onGroupSelected: function (e) {
   172      this.setState(function (state, props) {
   173        state.selectedGroup = e.target.hash.substring(1);
   174        this.props.app.forceUpdate();
   175      });
   176    },
   177    render: function () {
   178      var nPage = Math.ceil(this.props.children.length / this.props.pageSize);
   179      var pageNumber = Math.max(Math.min(this.state.pageNumber, nPage - 1), 0);
   180      var start = pageNumber * this.props.pageSize;
   181      var end = start + this.props.pageSize;
   182      var children = this.props.children.slice(start, end);
   183      var pages = Array.apply(null, {length: nPage}).map(Number.call, Number);
   184      return (
   185        <div className="app-box panel panel-info">
   186          <div className="panel-heading">
   187            <div className="container-fluid panel-container">
   188              <div className="col-xs-3 text-left">
   189                <h3 className="panel-title">{this.props.header}</h3>
   190                <div className="dropdown group-dropdown">
   191                  <button className="btn btn-default dropdown-toggle"
   192                  type="button" data-toggle="dropdown"
   193                  aria-haspopup="true" aria-expanded="true">
   194                    {this.state.selectedGroup}
   195                    <span className="caret"></span>
   196                  </button>
   197                  <ul className="dropdown-menu">
   198                    {
   199                      this.props.app.state.groups.map(function (i) {
   200                        return <li><a href={"#" + i}
   201                                    onClick={this.onGroupSelected}>{i}</a></li>
   202                      }.bind(this))
   203                    }
   204                  </ul>
   205                </div>
   206              </div>
   207              <div className="col-xs-6 text-center">
   208                <ul className="pagination panel-pagination">
   209                  <li>
   210                    <a href="#" aria-label="Previous"
   211                        onClick={this.changePage.bind(this, pageNumber - 1)}>
   212                      <span aria-hidden="true">&laquo;</span>
   213                    </a>
   214                  </li>
   215                  {
   216                    pages.map(function (i) {
   217                      var extra = {};
   218                      if (i == pageNumber) {
   219                          extra.className = "active";
   220                      }
   221                      return (
   222                        <li key={i} {...extra}>
   223                          <a onClick={this.changePage.bind(this, i)} href="#">
   224                            {i + 1}
   225                          </a>
   226                        </li>
   227                      )
   228                    }.bind(this))
   229                  }
   230                  <li>
   231                    <a href="#" aria-label="Next"
   232                        onClick={this.changePage.bind(this, pageNumber + 1)}>
   233                      <span aria-hidden="true">&raquo;</span>
   234                    </a>
   235                  </li>
   236                </ul>
   237              </div>
   238              <div className="col-xs-3">
   239                <div className="col-xs-6 pull-right">
   240                <input type="text" ref="filter" placeholder="keyword"
   241                    className="filter-input form-control"
   242                    onKeyUp={this.onKeyUp} />
   243                </div>
   244              </div>
   245            </div>
   246          </div>
   247          <div className="panel-body">
   248          {
   249            children.map(function (child) {
   250              return child;
   251            }.bind(this))
   252          }
   253          </div>
   254        </div>
   255      );
   256    }
   257  });
   258  
   259  ReactDOM.render(
   260    <App url="/api/agents/list"/>,
   261    document.getElementById("body")
   262  );