Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit b3f30ce

Browse files
committed
Add view and stylesheets #43
1 parent ea7efc7 commit b3f30ce

File tree

12 files changed

+1815
-14
lines changed

12 files changed

+1815
-14
lines changed

package-lock.json

Lines changed: 1499 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"ejs": "~2.6.1",
3939
"express": "~4.16.3",
4040
"express-promise-router": "^3.0.3",
41+
"express-react-views": "^0.10.5",
4142
"immutable": "~3.8.2",
4243
"js-yaml": "~3.11.0",
4344
"jsonwebtoken": "~5.5.0",
@@ -48,6 +49,9 @@
4849
"mongoose": "~5.0.0",
4950
"morgan": "~1.9.0",
5051
"nightmare": "^3.0.1",
52+
"node-sass": "^4.9.4",
53+
"react": "^16.5.2",
54+
"react-dom": "^16.5.2",
5155
"redis": "^2.8.0",
5256
"request": "^2.74.0",
5357
"request-promise-native": "~1.0.5",
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
@import url(//fonts.googleapis.com/earlyaccess/notosanskr.css);
2+
3+
.timetable {
4+
padding: 0px;
5+
border-top-width: 0;
6+
table-layout: fixed;
7+
background-color: #ffffff;
8+
color: rgba(0, 0, 0, 0.4) !important;
9+
font-family: 'Noto Sans KR', sans-serif;
10+
font-size: 14px;
11+
12+
td {
13+
width: 100px;
14+
height:20px;
15+
position: relative;
16+
}
17+
18+
.label-date {
19+
border: none;
20+
text-align: center;
21+
font-weight: normal;
22+
height: 40px;
23+
}
24+
25+
.label-hour {
26+
border: none;
27+
width: 45px;
28+
text-align: right;
29+
padding: 0 7px 0 0;
30+
}
31+
32+
tbody tr:nth-child(odd) .td-body {
33+
border-top: 1px solid #ebeef2;
34+
}
35+
36+
tbody tr:last-child .td-body {
37+
border-bottom: 1px solid #ebeef2;
38+
}
39+
40+
tbody tr:nth-child(even) .td-body {
41+
border-top: 1px solid rgba(235, 238, 242, 0.3); //ebeef2 * 0.3 opacity
42+
}
43+
44+
.td-body {
45+
height: 20px;
46+
border: none;
47+
}
48+
49+
.blank-right {
50+
width: 15px;
51+
border: none !important
52+
}
53+
54+
.dragged {
55+
background-color: red
56+
}
57+
58+
.course-div {
59+
background-color: pink;
60+
height:100%;
61+
position: absolute; top: -1px; left: 0px;
62+
width:100%;
63+
border-radius: 0px;
64+
z-index: 1;
65+
padding: 0 2px;
66+
text-align: center;
67+
.title-box {
68+
position: relative;
69+
transform: translateY(-50%);
70+
top: 50%;
71+
font-size: 0.7em;
72+
line-height: 1em;
73+
}
74+
.title-box p {
75+
margin-bottom: 3px;
76+
}
77+
78+
.tool-box-wrapper {
79+
visibility: hidden;
80+
width: 100%;
81+
height: 100%;
82+
position: absolute;
83+
top: 0px;
84+
right: 0px;
85+
font-size: 20px;
86+
background-color: rgba(0, 0, 0, 0.65);
87+
}
88+
.tool-box {
89+
position: relative;
90+
transform: translateY(-50%);
91+
top: 50%;
92+
span { margin: 0 5px; }
93+
}
94+
&.hovered .tool-box-wrapper {
95+
visibility: visible;
96+
}
97+
}
98+
99+
.preview {
100+
background-color: #D7DEE0 !important;
101+
color: gray !important;
102+
z-index: 2;
103+
opacity: 0.5;
104+
}
105+
106+
.table-info {
107+
position: relative;
108+
font-size: 13px;
109+
}
110+
111+
.add-button {
112+
float: left;
113+
border-radius: 17px;
114+
height: 34px;
115+
opacity: 0.8;
116+
border: 1px solid #b7c3ce;
117+
}
118+
119+
.credit {
120+
float: right;
121+
font-size: 13px;
122+
}
123+
}
124+
#timetable-container {
125+
background-color: #ffffff;
126+
margin-bottom: 40px;
127+
}
128+
.table-info {
129+
130+
padding: 0px 15px 15px 15px;
131+
overflow: hidden;
132+
vertical-align: middle;
133+
134+
.add-button {
135+
margin-left: 30px;
136+
border-radius: 500px;
137+
border: solid 1px #d5dbe0;
138+
padding: 7px 17px;
139+
font-size: 13px;
140+
float: left;
141+
cursor: pointer;
142+
}
143+
144+
.credit {
145+
float: right;
146+
padding: 7px 0px;
147+
font-size: 13px;
148+
}
149+
}
150+
151+
.timetable {
152+
margin-bottom: 15px;
153+
}
154+
155+
.timetable * {
156+
-webkit-touch-callout: none; /* iOS Safari */
157+
-webkit-user-select: none; /* Chrome/Safari/Opera */
158+
-khtml-user-select: none; /* Konqueror */
159+
-moz-user-select: none; /* Firefox */
160+
-ms-user-select: none; /* IE/Edge */
161+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "Timetable.scss"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
3+
import Timetable from './timetable/Timetable.jsx';
4+
5+
const FullscreenTimetable = ({ timetable }) => {
6+
return (
7+
<html>
8+
<head>
9+
<meta charset="UTF-8" />
10+
<meta name="viewport" content="width=device-width, user-scalable=no" />
11+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
12+
crossorigin="anonymous" />
13+
<link rel="stylesheet" href="" />
14+
</head>
15+
16+
<body>
17+
<Timetable
18+
courses={timetable.lecture_list}
19+
/>
20+
</body>
21+
</html>
22+
);
23+
};
24+
25+
export default FullscreenTimetable;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
3+
const DEFAULT_COLOR = { fg: '#1579C2', bg: '#94E6FE' };
4+
5+
const LectureBox = ({ length, course, classroom }) => {
6+
if (!course.color) { course.color = DEFAULT_COLOR; }
7+
const divStyle = {
8+
height: `${length * 20}px`,
9+
color: course.color.fg,
10+
backgroundColor: course.color.bg,
11+
};
12+
return (
13+
<div
14+
className={`course-div`}
15+
style={divStyle}
16+
>
17+
<div className="title-box">
18+
<p>{course.course_title}</p>
19+
<p><strong>{classroom}</strong></p>
20+
</div>
21+
</div>
22+
);
23+
};
24+
25+
export default LectureBox;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
3+
const TableBody = ({ lectureBoxes, hasSunday }) => {
4+
const rows = [];
5+
const numDay = hasSunday ? 7 : 6;
6+
const numRows = lectureBoxes[0].length;
7+
for (let t = 0; t < numRows; t += 1) { // rows
8+
const cols = [];
9+
if (t % 2 === 0) {
10+
const hrIdx = t / 2;
11+
cols.push(<td className="label-hour" rowSpan="2" key={-1}>{hrIdx + 8}</td>);
12+
}
13+
for (let d = 0; d < numDay; d += 1) { // columns
14+
cols.push((
15+
<td className="td-body" key={`${d}{t}`}>
16+
{lectureBoxes[d][t]}
17+
</td>
18+
));
19+
}
20+
rows.push(<tr key={t}>{cols}</tr>);
21+
}
22+
return (<tbody>{rows}</tbody>);
23+
};
24+
25+
export default TableBody;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
3+
const TableHeader = ({ hasSunday }) => {
4+
const days = ['월', '화', '수', '목', '금', '토'];
5+
if (hasSunday) days.push('일');
6+
return (
7+
<thead>
8+
<tr>
9+
<th className="label-hour" />
10+
{days.map(v => (<th className="label-date" key={v}>{v}</th>))}
11+
<th className="label-date blank-right" />
12+
</tr>
13+
</thead>
14+
);
15+
};
16+
17+
export default TableHeader;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
3+
import LectureBox from './LectureBox.jsx';
4+
import TableHeader from './TableHeader.jsx';
5+
import TableBody from './TableBody.jsx';
6+
7+
const NUM_SLOTS = 32;
8+
const NUM_DAY = 7;
9+
10+
const Timetable = ({ courses }) => {
11+
const lectureBoxes = new Array(NUM_DAY).fill(0).map(() => new Array(NUM_SLOTS));
12+
for (const course of courses) {
13+
for (const lecture of course.class_time_json) {
14+
const day = lecture.day;
15+
lectureBoxes[day][lecture.start * 2] = (
16+
<LectureBox
17+
course={course}
18+
length={lecture.len * 2}
19+
classroom={lecture.place}
20+
/>
21+
);
22+
}
23+
}
24+
25+
let hasSunday = lectureBoxes[6].filter(v => Boolean(v)).length > 0;
26+
27+
return (
28+
<div id="container-fluid">
29+
<table className="table timetable">
30+
<TableHeader hasSunday={hasSunday} />
31+
<TableBody
32+
hasSunday={hasSunday}
33+
lectureBoxes={lectureBoxes}
34+
/>
35+
</table>
36+
</div>
37+
);
38+
};
39+
40+
export default Timetable;

src/api/config/express.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ var app = express();
1616

1717
// view engine setup
1818
app.set('views', path.join(__dirname, '..', 'views'));
19-
//app.set('view engine', 'jade');
19+
20+
app.set('view engine', 'jsx')
21+
app.engine('.jsx', require('express-react-views').createEngine());
2022
app.engine('.html', require('ejs').renderFile);
2123

2224
// X-Forwarded-For 헤더를 신뢰할 주소. 앞단에 nginx 프록시를 둘 경우 필요함. localhost만을 활성화한다

src/api/routes/TimetableRouter.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ restGet(router, '/:id')(async function(context, req) {
5252
return result;
5353
});
5454

55+
router.get('/:id/html', async function(req, res, next) {
56+
let context: RequestContext = req['context'];
57+
let user:User = context.user;
58+
59+
let table = await TimetableService.getByMongooseId(user._id, req.params.id);
60+
if (!table) {
61+
throw new ApiError(404, ErrorCode.TIMETABLE_NOT_FOUND, "timetable not found");
62+
}
63+
64+
res.render('FullscreenTimetable.jsx', { timetable: table });
65+
});
66+
5567
router.get('/:id/image', async function(req, res, next) {
5668
let context: RequestContext = req['context'];
5769
let user:User = context.user;

src/core/nightmare/NightmareService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ let nm = Nightmare({
1010
let nmLock = new AsyncLock({maxPending : 100, timeout: 5000});
1111
const NM_LOCK_KEY = "NightmareServiceLock";
1212

13+
/**
14+
* 하나의 Nightmare 객체를 공유하므로 lock을 사용해야 한다.
15+
*/
1316
function execute<T>(f: (Nightmare) => Promise<T>): Promise<T> {
1417
return nmLock.acquire(NM_LOCK_KEY, function() {
1518
return f(nm);

0 commit comments

Comments
 (0)