Skip to content

Commit cce81a9

Browse files
Andrew OberhardtAndrew Oberhardt
authored andcommitted
Added main workshop dashboard UI (reports will come next)
1 parent c7c5f91 commit cce81a9

28 files changed

+2570
-4
lines changed

code-studio/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
"//": "ejsify is a dependency of apps/src; we declare it here rather than in apps/src/package.json so we can continue to avoid needing to npm install that module.",
4949
"devDependencies": {
5050
"babelify": "^6.3.0",
51+
"bootstrap-sass": "^3.3.6",
5152
"browserify": "^12.0.1",
53+
"browserify-global-shim": "^1.0.3",
5254
"browserify-incremental": "^3.1.1",
5355
"browserify-shim": "^3.8.12",
5456
"chai": "^3.5.0",
@@ -60,18 +62,22 @@
6062
"eslint": "^2.3.0",
6163
"factor-bundle": "^2.5.0",
6264
"gaze": "^1.0.0",
65+
"history": "^2.0.1",
6366
"http-server": "^0.9.0",
6467
"istanbul": "^0.4.2",
6568
"johnny-five": "bcjordan/johnny-five#with-firmata-fix",
6669
"mkdirp": "^0.5.1",
6770
"mocha": "^2.3.4",
6871
"mochify": "^2.15.0",
6972
"mochify-istanbul": "^2.4.1",
73+
"moment": "2.12.0",
7074
"node-sass": "^3.4.2",
7175
"node-watch": "^0.3.5",
7276
"object-assign": "^4.0.1",
7377
"phantomjs-prebuilt": "^2.1.3",
7478
"playground-io": "bcjordan/playground-io#tap-cap-touch",
79+
"react-bootstrap": "^0.28.5",
80+
"react-router": "^2.0.1",
7581
"recursive-readdir-sync": "^1.0.6",
7682
"sass-lint": "^1.4.0",
7783
"semver": "^5.1.0",

code-studio/scripts/build-commands.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ var watchify = require('watchify');
3535
* @param {boolean} [config.forDistribution] - If true, this bundle step will be
3636
* treated as part of a build for distribution, with certain environment
3737
* variables inlined, and dead code and whitespace removed.
38+
* @param {object} [config.browserifyGlobalShim] - If supplied, applied as options
39+
* to a browserifyGlobalShim transform.
3840
* @returns {Promise} that resolves after the build completes or fails - even
3941
* if the build fails, the promise should resolve, but get an Error
4042
* object as its result. If watch is enabled, the promise resolves
@@ -50,6 +52,7 @@ exports.bundle = function (config) {
5052
var shouldFactor = config.shouldFactor;
5153
var shouldWatch = config.shouldWatch;
5254
var forDistribution = config.forDistribution;
55+
var browserifyGlobalShim = config.browserifyGlobalShim;
5356

5457
var outPath = function (inPath) {
5558
return path
@@ -120,6 +123,11 @@ exports.bundle = function (config) {
120123
});
121124
}
122125

126+
if (browserifyGlobalShim) {
127+
var globalShim = require('browserify-global-shim').configure(browserifyGlobalShim);
128+
bundler.transform({global: true}, globalShim);
129+
}
130+
123131
// Optionally enable watch/rebuild loop
124132
if (shouldWatch) {
125133
bundler

code-studio/scripts/build-js.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ Promise.all([
8989
'makerlab/makerlabDependencies.js'
9090
],
9191
commonFile: 'makerlab'
92+
})),
93+
94+
build_commands.bundle(_.extend({}, defaultOptions, {
95+
filenames: [
96+
'pd/workshop_dashboard/workshop_dashboard.jsx'
97+
],
98+
commonFile: 'pd',
99+
browserifyGlobalShim: {
100+
"react": "React",
101+
"react-dom": "ReactDOM"
102+
}
92103
}))
93104
]).then(function (results) {
94105
var allStepsSucceeded = !results.some(function (result) {

code-studio/src/css/pd.scss

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
1-
.form-required-field {
2-
color: red;
1+
@import "color";
2+
@import "bootstrap-sass/assets/stylesheets/bootstrap/mixins/buttons";
3+
4+
@mixin buttons() {
5+
.btn-primary {
6+
// color, background, border
7+
@include button-variant($white, $cyan, $light_gray);
8+
text-shadow: none;
9+
}
10+
}
11+
12+
// Apply the latest bootstrap styles only to the workshop-container div so it
13+
// doesn't conflict with the footer and header that are still using
14+
// Bootstrap 2.2.3
15+
// TODO: Remove this once Dashboard is upgraded to Bootstrap 3
16+
#workshop-container {
17+
@import "bootstrap-sass/assets/stylesheets/bootstrap";
18+
@include buttons();
19+
20+
a {
21+
color: $purple;
22+
font-family: "Gotham 7r";
23+
&:hover {
24+
color: $purple;
25+
}
26+
}
27+
28+
th {
29+
color: $black;
30+
background-color: $lighter_gray;
31+
padding: 2px 5px;
32+
white-space: normal;
33+
font-weight: normal;
34+
}
35+
}
36+
37+
div[role=dialog] {
38+
div.modal {
39+
min-width: 450px;
40+
width: auto;
41+
}
42+
43+
div.modal-content {
44+
margin: 0;
45+
@import "bootstrap-sass/assets/stylesheets/bootstrap";
46+
@include buttons();
47+
48+
div {
49+
height: auto;
50+
}
51+
}
352
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* global React */
2+
3+
var SessionAttendanceRow = require('./session_attendance_row.jsx');
4+
var Table = require('react-bootstrap').Table;
5+
6+
var SessionAttendance = React.createClass({
7+
propTypes: {
8+
sessionId: React.PropTypes.number,
9+
attendance: React.PropTypes.array.isRequired,
10+
adminOverride: React.PropTypes.bool,
11+
onChange: React.PropTypes.func,
12+
isReadOnly: React.PropTypes.bool
13+
},
14+
15+
handleChange: function (i) {
16+
this.props.onChange(i, !this.props.attendance[i].attended);
17+
},
18+
19+
render: function () {
20+
var tableRows = this.props.attendance.map(function (attendanceRow, i) {
21+
return (
22+
<SessionAttendanceRow
23+
key={i}
24+
sessionId={this.props.sessionId}
25+
attendance={attendanceRow}
26+
adminOverride={this.props.adminOverride}
27+
onChange={this.handleChange.bind(null,i)}
28+
isReadOnly={this.props.isReadOnly}
29+
/>
30+
);
31+
}.bind(this));
32+
return (
33+
<div>
34+
<Table striped bordered condensed hover>
35+
<thead>
36+
<tr>
37+
<th>Name</th>
38+
<th>Email</th>
39+
<th>Enrolled</th>
40+
<th>Code Studio Account</th>
41+
<th>Joined Section</th>
42+
<th>Attended</th>
43+
</tr>
44+
</thead>
45+
<tbody>
46+
{tableRows}
47+
</tbody>
48+
</Table>
49+
</div>
50+
);
51+
}
52+
});
53+
module.exports = SessionAttendance;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/* global React */
2+
3+
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
4+
var Tooltip = require('react-bootstrap').Tooltip;
5+
6+
var SessionAttendanceRow = React.createClass({
7+
propTypes: {
8+
sessionId: React.PropTypes.number,
9+
attendance: React.PropTypes.shape({
10+
name: React.PropTypes.string.isRequired,
11+
email: React.PropTypes.string.isRequired,
12+
enrolled: React.PropTypes.bool.isRequired,
13+
user_id: React.PropTypes.number,
14+
in_section: React.PropTypes.bool.isRequired,
15+
attended: React.PropTypes.bool.isRequired
16+
}).isRequired,
17+
adminOverride: React.PropTypes.bool,
18+
onChange: React.PropTypes.func.isRequired,
19+
isReadOnly: React.PropTypes.bool
20+
},
21+
22+
isValid: function () {
23+
// Must have an account and have joined the section before being marked attended,
24+
// unless overridden by an admin.
25+
return (this.props.attendance.user_id && this.props.attendance.in_section) || this.props.adminOverride;
26+
},
27+
28+
handleClickAttended: function () {
29+
if (this.isValid()) {
30+
this.props.onChange();
31+
}
32+
},
33+
34+
renderAttendedCellContents: function () {
35+
var checkBoxClass = this.props.attendance.attended ? "fa fa-check-square-o" : "fa fa-square-o";
36+
if (this.props.isReadOnly) {
37+
return (
38+
<div>
39+
<i className={checkBoxClass}/>
40+
</div>
41+
);
42+
}
43+
44+
var contents = (
45+
<div style={{height: '100%', width: '100%', cursor:'pointer'}} onClick={this.handleClickAttended}>
46+
<i className={checkBoxClass}/>
47+
</div>
48+
);
49+
50+
if (!this.isValid()) {
51+
var tooltip = (
52+
<Tooltip id={0}>
53+
Teachers must have a Code Studio account and join the section before they can be marked attended.
54+
</Tooltip>
55+
);
56+
return (
57+
<OverlayTrigger overlay={tooltip} placement="left" delayShow={500}>
58+
{contents}
59+
</OverlayTrigger>
60+
);
61+
}
62+
63+
return contents;
64+
},
65+
66+
render: function () {
67+
return (
68+
<tr className={this.props.attendance.attended ? 'success' : null}>
69+
<td>
70+
{this.props.attendance.name}
71+
</td>
72+
<td>
73+
{this.props.attendance.email}
74+
</td>
75+
<td>
76+
{this.props.attendance.enrolled ? "Yes" : "No"}
77+
</td>
78+
<td>
79+
{this.props.attendance.user_id ? "Yes" : "No"}
80+
</td>
81+
<td>
82+
{this.props.attendance.in_section ? "Yes" : "No"}
83+
</td>
84+
<td>
85+
{this.renderAttendedCellContents(this.props.attendance.attended)}
86+
</td>
87+
</tr>
88+
);
89+
}
90+
});
91+
module.exports = SessionAttendanceRow;

0 commit comments

Comments
 (0)