Skip to content

Commit dfcf2a5

Browse files
committed
Merge pull request code-dot-org#8474 from code-dot-org/pd-workshop-ui
Added main workshop dashboard UI
2 parents c7c5f91 + 4b55571 commit dfcf2a5

28 files changed

+2675
-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
@@ -11,6 +11,7 @@ var fs = require('fs');
1111
var mkdirp = require('mkdirp');
1212
var path = require('path');
1313
var watchify = require('watchify');
14+
var globalShim = require('browserify-global-shim');
1415

1516
/**
1617
* Generate command to:
@@ -35,6 +36,8 @@ var watchify = require('watchify');
3536
* @param {boolean} [config.forDistribution] - If true, this bundle step will be
3637
* treated as part of a build for distribution, with certain environment
3738
* variables inlined, and dead code and whitespace removed.
39+
* @param {object} [config.browserifyGlobalShim] - If supplied, applied as options
40+
* to a browserifyGlobalShim transform.
3841
* @returns {Promise} that resolves after the build completes or fails - even
3942
* if the build fails, the promise should resolve, but get an Error
4043
* object as its result. If watch is enabled, the promise resolves
@@ -50,6 +53,7 @@ exports.bundle = function (config) {
5053
var shouldFactor = config.shouldFactor;
5154
var shouldWatch = config.shouldWatch;
5255
var forDistribution = config.forDistribution;
56+
var browserifyGlobalShim = config.browserifyGlobalShim;
5357

5458
var outPath = function (inPath) {
5559
return path
@@ -120,6 +124,10 @@ exports.bundle = function (config) {
120124
});
121125
}
122126

127+
if (browserifyGlobalShim) {
128+
bundler.transform({global: true}, globalShim.configure(browserifyGlobalShim));
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: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* global React */
2+
3+
/*
4+
Display and edit attendance for a workshop session, for display in a WorkshopAttendance tab.
5+
*/
6+
7+
var SessionAttendanceRow = require('./session_attendance_row');
8+
var Table = require('react-bootstrap').Table;
9+
10+
var SessionAttendance = React.createClass({
11+
propTypes: {
12+
sessionId: React.PropTypes.number,
13+
attendance: React.PropTypes.array.isRequired,
14+
adminOverride: React.PropTypes.bool,
15+
onChange: React.PropTypes.func,
16+
isReadOnly: React.PropTypes.bool
17+
},
18+
19+
handleChange: function (i) {
20+
this.props.onChange(i, !this.props.attendance[i].attended);
21+
},
22+
23+
render: function () {
24+
var tableRows = this.props.attendance.map(function (attendanceRow, i) {
25+
return (
26+
<SessionAttendanceRow
27+
key={i}
28+
sessionId={this.props.sessionId}
29+
attendance={attendanceRow}
30+
adminOverride={this.props.adminOverride}
31+
onChange={this.handleChange.bind(null,i)}
32+
isReadOnly={this.props.isReadOnly}
33+
/>
34+
);
35+
}.bind(this));
36+
return (
37+
<div>
38+
<Table striped bordered condensed hover>
39+
<thead>
40+
<tr>
41+
<th>Name</th>
42+
<th>Email</th>
43+
<th>Enrolled</th>
44+
<th>Code Studio Account</th>
45+
<th>Joined Section</th>
46+
<th>Attended</th>
47+
</tr>
48+
</thead>
49+
<tbody>
50+
{tableRows}
51+
</tbody>
52+
</Table>
53+
</div>
54+
);
55+
}
56+
});
57+
module.exports = SessionAttendance;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* global React */
2+
3+
/*
4+
Display and edit attendance for a single teacher in a session,
5+
for use in SessionAttendance.
6+
*/
7+
8+
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
9+
var Tooltip = require('react-bootstrap').Tooltip;
10+
11+
var styles = {
12+
contents: {
13+
height: '100%',
14+
width: '100%',
15+
cursor:'pointer'
16+
}
17+
};
18+
19+
var SessionAttendanceRow = React.createClass({
20+
propTypes: {
21+
sessionId: React.PropTypes.number,
22+
attendance: React.PropTypes.shape({
23+
name: React.PropTypes.string.isRequired,
24+
email: React.PropTypes.string.isRequired,
25+
enrolled: React.PropTypes.bool.isRequired,
26+
user_id: React.PropTypes.number,
27+
in_section: React.PropTypes.bool.isRequired,
28+
attended: React.PropTypes.bool.isRequired
29+
}).isRequired,
30+
adminOverride: React.PropTypes.bool,
31+
onChange: React.PropTypes.func.isRequired,
32+
isReadOnly: React.PropTypes.bool
33+
},
34+
35+
isValid: function () {
36+
// Must have an account and have joined the section before being marked attended,
37+
// unless overridden by an admin.
38+
return (this.props.attendance.user_id && this.props.attendance.in_section) || this.props.adminOverride;
39+
},
40+
41+
handleClickAttended: function () {
42+
if (this.isValid()) {
43+
this.props.onChange();
44+
}
45+
},
46+
47+
renderAttendedCellContents: function () {
48+
var checkBoxClass = this.props.attendance.attended ? "fa fa-check-square-o" : "fa fa-square-o";
49+
if (this.props.isReadOnly) {
50+
return (
51+
<div>
52+
<i className={checkBoxClass}/>
53+
</div>
54+
);
55+
}
56+
57+
var contents = (
58+
<div style={styles.contents} onClick={this.handleClickAttended}>
59+
<i className={checkBoxClass}/>
60+
</div>
61+
);
62+
63+
if (!this.isValid()) {
64+
var tooltip = (
65+
<Tooltip id={0}>
66+
Teachers must have a Code Studio account and join the section before they can be marked attended.
67+
</Tooltip>
68+
);
69+
return (
70+
<OverlayTrigger overlay={tooltip} placement="left" delayShow={500}>
71+
{contents}
72+
</OverlayTrigger>
73+
);
74+
}
75+
76+
return contents;
77+
},
78+
79+
render: function () {
80+
return (
81+
<tr className={this.props.attendance.attended ? 'success' : null}>
82+
<td>
83+
{this.props.attendance.name}
84+
</td>
85+
<td>
86+
{this.props.attendance.email}
87+
</td>
88+
<td>
89+
{this.props.attendance.enrolled ? "Yes" : "No"}
90+
</td>
91+
<td>
92+
{this.props.attendance.user_id ? "Yes" : "No"}
93+
</td>
94+
<td>
95+
{this.props.attendance.in_section ? "Yes" : "No"}
96+
</td>
97+
<td>
98+
{this.renderAttendedCellContents()}
99+
</td>
100+
</tr>
101+
);
102+
}
103+
});
104+
module.exports = SessionAttendanceRow;

0 commit comments

Comments
 (0)