kopia lustrzana https://github.com/OpenDroneMap/WebODM
React setup, ajax retrieval example, live reload, auto SCSS compile
rodzic
03cc307e96
commit
d688c60b49
|
@ -32,8 +32,3 @@ RUN npm install
|
|||
WORKDIR /webodm
|
||||
RUN npm install -g webpack
|
||||
RUN npm install
|
||||
|
||||
RUN webpack
|
||||
|
||||
# Make sure all scripts are executable
|
||||
RUN chmod +x *.sh
|
||||
|
|
|
@ -64,7 +64,10 @@ Then:
|
|||
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
pip install --upgrade git+git://github.com/Yelp/swagger_spec_validator git+https://github.com/pierotofy/django-knockout
|
||||
pip install --upgrade git+git://github.com/Yelp/swagger_spec_validator
|
||||
npm install -g webpack
|
||||
npm install
|
||||
webpack
|
||||
chmod +x start.sh && ./start.sh
|
||||
```
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ from app import models, permissions
|
|||
from guardian.shortcuts import get_objects_for_user
|
||||
|
||||
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
|
||||
id = serializers.ReadOnlyField()
|
||||
owner = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -1 +1 @@
|
|||
*.js
|
||||
*
|
|
@ -1,55 +0,0 @@
|
|||
#navbar-top {
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
background-color: #18bc9c; }
|
||||
#navbar-top .navbar-brand {
|
||||
height: auto;
|
||||
padding: 8px; }
|
||||
#navbar-top .navbar-text {
|
||||
margin-top: 14px;
|
||||
margin-left: 4px; }
|
||||
#navbar-top .navbar-link:hover {
|
||||
color: white;
|
||||
text-decoration: none; }
|
||||
#navbar-top .navbar-top-links a.dropdown-toggle {
|
||||
color: white; }
|
||||
#navbar-top .navbar-top-links a:hover, #navbar-top .navbar-top-links a:focus, #navbar-top .navbar-top-links .open > a {
|
||||
background-color: #2c3e50; }
|
||||
#navbar-top ul#side-menu a:hover, #navbar-top ul#side-menu a:focus {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
background-color: #2c3e50; }
|
||||
#navbar-top ul#side-menu li {
|
||||
word-break: break-word; }
|
||||
#navbar-top .navbar-top-links li a.dropdown-toggle {
|
||||
height: 50px; }
|
||||
#navbar-top .user-profile {
|
||||
padding: 0 8px; }
|
||||
#navbar-top .user-profile .email {
|
||||
font-size: 90%;
|
||||
color: gray; }
|
||||
|
||||
ul#side-menu.nav a {
|
||||
color: #111; }
|
||||
|
||||
.content {
|
||||
clear: both; }
|
||||
.content h1, .content h2, .content h3, .content h4, .content h5 {
|
||||
padding-top: 4px; }
|
||||
|
||||
.top-buffer {
|
||||
margin-top: 15px; }
|
||||
|
||||
.navbar-default {
|
||||
background-color: white; }
|
||||
|
||||
.alert {
|
||||
margin-bottom: 10px; }
|
||||
|
||||
table.table-first-col-bold td:first-child {
|
||||
font-weight: bold; }
|
||||
|
||||
button span.glyphicon {
|
||||
margin-right: 4px; }
|
||||
|
||||
/*# sourceMappingURL=main.css.map */
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA,WAAW;EACP,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,IAAI;EAChB,gBAAgB,EAAE,OAAO;EAEzB,yBAAa;IACT,MAAM,EAAE,IAAI;IACZ,OAAO,EAAE,GAAG;EAEhB,wBAAY;IACR,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,GAAG;EAGpB,8BAAkB;IACd,KAAK,EAAE,KAAK;IACZ,eAAe,EAAE,IAAI;EAIrB,+CAAiB;IACb,KAAK,EAAE,KAAK;EAGhB,qHAAyB;IACrB,gBAAgB,EAAE,OAAO;EAM7B,kEAAgB;IACZ,KAAK,EAAE,KAAK;IACZ,eAAe,EAAE,IAAI;IACrB,gBAAgB,EAAE,OAAO;EAG7B,2BAAE;IACE,UAAU,EAAE,UAAU;EAI9B,kDAAsC;IAClC,MAAM,EAAE,IAAI;EAGhB,yBAAa;IACT,OAAO,EAAE,KAAK;IAEd,gCAAM;MACF,SAAS,EAAE,GAAG;MACd,KAAK,EAAE,IAAI;;AAMnB,kBAAC;EACG,KAAK,EAAE,IAAI;;AAInB,QAAQ;EACJ,KAAK,EAAE,IAAI;EACX,+DAAc;IACV,WAAW,EAAE,GAAG;;AAIxB,WAAY;EACR,UAAU,EAAE,IAAI;;AAGpB,eAAe;EACX,gBAAgB,EAAE,KAAK;;AAG3B,MAAM;EACF,aAAa,EAAE,IAAI;;AAInB,yCAAc;EACV,WAAW,EAAE,IAAI;;AAIzB,qBAAqB;EACjB,YAAY,EAAE,GAAG",
|
||||
"sources": ["main.scss"],
|
||||
"names": [],
|
||||
"file": "main.css"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import './css/Dashboard.scss';
|
||||
import ProjectList from './components/ProjectList';
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ProjectList source="/api/projects/"/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Dashboard;
|
|
@ -1,11 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>Hello World!</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,62 @@
|
|||
import React from 'react';
|
||||
import $ from 'jquery';
|
||||
|
||||
import ProjectListItem from './ProjectListItem';
|
||||
|
||||
class ProjectList extends React.Component {
|
||||
constructor(){
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
loading: true,
|
||||
error: "",
|
||||
projects: null
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
// Load projects from API
|
||||
this.serverRequest = $.getJSON(this.props.source, json => {
|
||||
if (json.results){
|
||||
this.setState({
|
||||
projects: json.results,
|
||||
loading: false
|
||||
});
|
||||
}else{
|
||||
this.setState({
|
||||
error: `Invalid JSON response: ${JSON.stringify(json)}`,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
})
|
||||
.fail((jqXHR, textStatus, errorThrown) => {
|
||||
this.setState({
|
||||
error: `Could not load projects list: ${textStatus}`,
|
||||
loading: false
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
this.serverRequest.abort();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.loading){
|
||||
return (<div>Loading projects...</div>);
|
||||
}
|
||||
else if (this.state.projects){
|
||||
return (<div>
|
||||
{this.state.projects.map(p => (
|
||||
<ProjectListItem key={p.id} data={p} />
|
||||
))}
|
||||
</div>);
|
||||
}else if (this.state.error){
|
||||
return (<div>An error occurred: {this.state.error}</div>);
|
||||
}else{
|
||||
return (<div></div>); // should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ProjectList;
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import ProjectListItemPanel from './ProjectListItemPanel';
|
||||
|
||||
class ProjectListItem extends React.Component {
|
||||
constructor(){
|
||||
super();
|
||||
this.state = {
|
||||
collapsed: true
|
||||
};
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick(){
|
||||
this.state.collapsed = !this.state.collapsed;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div onClick={this.handleClick}>
|
||||
{this.props.data.name} {this.props.data.created_at}
|
||||
|
||||
{this.props.collapsed ? "" : <ProjectListItemPanel />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ProjectListItem;
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
|
||||
class ProjectListItemPanel extends React.Component {
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
Sup!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ProjectListItemPanel;
|
|
@ -0,0 +1,3 @@
|
|||
#dashboard-app{
|
||||
background-color: yellow;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './app';
|
||||
|
||||
ReactDOM.render(<App/>, document.getElementById('react-app'))
|
|
@ -1,38 +0,0 @@
|
|||
$(function(){
|
||||
console.log("Hello!");
|
||||
$('#side-menu').metisMenu();
|
||||
|
||||
$(window).bind("load resize", function() {
|
||||
var topOffset = 50;
|
||||
var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
|
||||
if (width < 768) {
|
||||
$('div.navbar-collapse').addClass('collapse');
|
||||
topOffset = 100; // 2-row-menu
|
||||
} else {
|
||||
$('div.navbar-collapse').removeClass('collapse');
|
||||
}
|
||||
|
||||
var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
|
||||
height = height - topOffset;
|
||||
if (height < 1) height = 1;
|
||||
if (height > topOffset) {
|
||||
$("#page-wrapper").css("min-height", (height) + "px");
|
||||
}
|
||||
});
|
||||
|
||||
var url = window.location;
|
||||
// var element = $('ul.nav a').filter(function() {
|
||||
// return this.href == url;
|
||||
// }).addClass('active').parent().parent().addClass('in').parent();
|
||||
var element = $('ul.nav a').filter(function() {
|
||||
return this.href == url;
|
||||
}).addClass('active').parent();
|
||||
|
||||
while(true){
|
||||
if (element.is('li')){
|
||||
element = element.parent().addClass('in').parent();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import '../css/main.scss';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Dashboard from './Dashboard';
|
||||
|
||||
ReactDOM.render(<Dashboard/>, document.getElementById('dashboard-app'));
|
|
@ -22,9 +22,8 @@
|
|||
<link href="{% static 'app/css/metisMenu.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'app/css/sb-admin-2.css' %}" rel="stylesheet">
|
||||
{% block extra-headers %}{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'app/css/main.css' %}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'app/bundles/css/main.css' %}" />
|
||||
<script src="{% static 'app/js/vendor/modernizr-2.8.3.min.js' %}"></script>
|
||||
<script src="{% static 'app/js/vendor/knockout-3.4.0.js' %}"></script>
|
||||
<script src="{% static 'app/js/vendor/jquery-1.11.2.min.js' %}"></script>
|
||||
<title>{{title|default:"Login"}} - WebODM</title>
|
||||
</head>
|
||||
|
@ -71,5 +70,44 @@
|
|||
</body>
|
||||
<script src="{% static 'app/js/vendor/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'app/js/vendor/metisMenu.min.js' %}"></script>
|
||||
<script src="{% static 'app/js/main.js' %}"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#side-menu').metisMenu();
|
||||
|
||||
$(window).bind("load resize", function() {
|
||||
let topOffset = 50;
|
||||
let width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
|
||||
if (width < 768) {
|
||||
$('div.navbar-collapse').addClass('collapse');
|
||||
topOffset = 100; // 2-row-menu
|
||||
} else {
|
||||
$('div.navbar-collapse').removeClass('collapse');
|
||||
}
|
||||
|
||||
let height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
|
||||
height = height - topOffset;
|
||||
if (height < 1) height = 1;
|
||||
if (height > topOffset) {
|
||||
$("#page-wrapper").css("min-height", (height) + "px");
|
||||
}
|
||||
});
|
||||
|
||||
let url = window.location;
|
||||
// let element = $('ul.nav a').filter(function() {
|
||||
// return this.href == url;
|
||||
// }).addClass('active').parent().parent().addClass('in').parent();
|
||||
let element = $('ul.nav a').filter(function() {
|
||||
return this.href == url;
|
||||
}).addClass('active').parent();
|
||||
|
||||
while(true){
|
||||
if (element.is('li')){
|
||||
element = element.parent().addClass('in').parent();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</html>
|
|
@ -37,6 +37,6 @@
|
|||
{% endif %}
|
||||
<!-- <h4>Projects</h4> -->
|
||||
|
||||
<div id="react-app"></div>
|
||||
<div id="dashboard-app"></div>
|
||||
{% render_bundle 'main' %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -12,6 +12,7 @@ services:
|
|||
volumes:
|
||||
- .:/webodm
|
||||
- /webodm/nodeodm/external/node-OpenDroneMap/node_modules
|
||||
- /webodm/node_modules
|
||||
ports:
|
||||
- "8000:8000"
|
||||
depends_on:
|
||||
|
|
11
package.json
11
package.json
|
@ -24,9 +24,18 @@
|
|||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-es2015": "^6.16.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"css-loader": "^0.25.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.9.0",
|
||||
"node-sass": "^3.10.1",
|
||||
"react": "^15.3.2",
|
||||
"react-dom": "^15.3.2",
|
||||
"react-hot-loader": "^3.0.0-beta.5",
|
||||
"sass-loader": "^4.0.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-bundle-tracker": "0.0.93"
|
||||
"webpack-bundle-tracker": "0.0.93",
|
||||
"webpack-dev-server": "^1.16.2",
|
||||
"webpack-livereload-plugin": "^0.9.0"
|
||||
}
|
||||
}
|
||||
|
|
3
start.sh
3
start.sh
|
@ -1,4 +1,7 @@
|
|||
#!/bin/bash
|
||||
echo Building asssets...
|
||||
webpack
|
||||
|
||||
echo Running migrations
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
let webpack = require('webpack');
|
||||
let WebpackDevServer = require('webpack-dev-server');
|
||||
let config = require('./webpack.config');
|
||||
|
||||
new WebpackDevServer(webpack(config), {
|
||||
publicPath: config.output.publicPath,
|
||||
hot: true,
|
||||
inline: true
|
||||
}).listen(3000, '0.0.0.0', (err, result) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
console.log('Listening at 0.0.0.0:3000');
|
||||
})
|
|
@ -1,19 +1,32 @@
|
|||
var path = require("path")
|
||||
var webpack = require('webpack')
|
||||
var BundleTracker = require('webpack-bundle-tracker')
|
||||
let path = require("path");
|
||||
let webpack = require('webpack');
|
||||
let BundleTracker = require('webpack-bundle-tracker');
|
||||
let ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
let LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
|
||||
entry: './app/static/app/js/index.jsx', // entry point of our app.
|
||||
entry: [
|
||||
// 'webpack-dev-server/client?http://localhost:3000',
|
||||
// 'webpack/hot/only-dev-server',
|
||||
'./app/static/app/js/main.jsx',
|
||||
],
|
||||
|
||||
output: {
|
||||
path: path.resolve('./app/static/app/bundles/'),
|
||||
filename: "[name]-[hash].js",
|
||||
path: path.join(__dirname, './app/static/app/bundles/'),
|
||||
filename: "[name]-[hash].js"
|
||||
// publicPath: 'http://localhost:3000/app/static/app/bundles/', // Tell django to use this URL to load packages and not use STATIC_URL + bundle_name
|
||||
},
|
||||
|
||||
plugins: [
|
||||
// new webpack.HotModuleReplacementPlugin(),
|
||||
// new webpack.NoErrorsPlugin(), // don't reload if there is an error
|
||||
new LiveReloadPlugin(),
|
||||
new BundleTracker({filename: './webpack-stats.json'}),
|
||||
new ExtractTextPlugin('css/main.css', {
|
||||
allChunks: true
|
||||
})
|
||||
],
|
||||
|
||||
module: {
|
||||
|
@ -23,8 +36,13 @@ module.exports = {
|
|||
exclude: /(node_modules|bower_components)/,
|
||||
loader: 'babel-loader',
|
||||
query: {
|
||||
// plugins: ['react-hot-loader/babel'],
|
||||
presets: ['es2015', 'react']
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loader: ExtractTextPlugin.extract('css!sass')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -32,5 +50,11 @@ module.exports = {
|
|||
resolve: {
|
||||
modulesDirectories: ['node_modules', 'bower_components'],
|
||||
extensions: ['', '.js', '.jsx']
|
||||
},
|
||||
|
||||
externals: {
|
||||
// require("jquery") is external and available
|
||||
// on the global let jQuery
|
||||
"jquery": "jQuery"
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue