HyeonJun Jeon

Merge branch 'mysql' into 'master'

This diff is collapsed. Click to expand it.
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
9 "axios": "^0.27.2", 9 "axios": "^0.27.2",
10 "body-parser": "^1.20.0", 10 "body-parser": "^1.20.0",
11 "cors": "^2.8.5", 11 "cors": "^2.8.5",
12 + "crypto-js": "^4.1.1",
12 "express": "^4.18.1", 13 "express": "^4.18.1",
13 "fs": "^0.0.1-security", 14 "fs": "^0.0.1-security",
14 "localforage": "^1.10.0", 15 "localforage": "^1.10.0",
15 "mysql": "^2.18.1", 16 "mysql": "^2.18.1",
17 + "mysql2": "^2.3.3",
16 "puppeteer": "^14.1.1", 18 "puppeteer": "^14.1.1",
17 "react": "^18.1.0", 19 "react": "^18.1.0",
18 "react-dom": "^18.1.0", 20 "react-dom": "^18.1.0",
......
...@@ -55,6 +55,7 @@ function process(sche, k, v) { ...@@ -55,6 +55,7 @@ function process(sche, k, v) {
55 case "URL": 55 case "URL":
56 k = "url"; 56 k = "url";
57 sche.subjectID = v.substring(v.indexOf("se_") + 3, v.indexOf("&m")); 57 sche.subjectID = v.substring(v.indexOf("se_") + 3, v.indexOf("&m"));
58 + v = `https://khcanvas.khu.ac.kr/courses/${sche.subjectID}/assignments/${sche.uid}`;
58 break; 59 break;
59 default: 60 default:
60 errArr.push(k); 61 errArr.push(k);
......
1 const mysql = require("mysql"); 1 const mysql = require("mysql");
2 +const mysql2 = require("mysql2/promise");
2 const fs = require("fs"); 3 const fs = require("fs");
3 const parseICal = require("./ICal"); 4 const parseICal = require("./ICal");
4 5
...@@ -61,8 +62,19 @@ function jcalToSQL(jcal, userID) { ...@@ -61,8 +62,19 @@ function jcalToSQL(jcal, userID) {
61 62
62 // iCal의 소유주를 DB에 등록 후 userID get 63 // iCal의 소유주를 DB에 등록 후 userID get
63 // subjectID가 subjects, userID가 users에 있어야함 64 // subjectID가 subjects, userID가 users에 있어야함
64 -const fdata = fs.readFileSync("C:/Users/teddy/Downloads/data.ics", "utf8");
65 -const jcal = parseICal(fdata);
66 -jcalToSQL(jcal, 1);
67 65
68 -module.exports = jcalToSQL; 66 +// const fdata = fs.readFileSync("C:/Users/teddy/Downloads/data.ics", "utf8");
67 +// const jcal = parseICal(fdata);
68 +// jcalToSQL(jcal, 1);
69 +
70 +const [id, pw] = fs
71 + .readFileSync("server/libs/sql.pvdata", "utf8")
72 + .split("\r\n");
73 +const connectOption = {
74 + host: "localhost",
75 + user: id,
76 + password: pw,
77 + database: "db",
78 +};
79 +
80 +module.exports = { jcalToSQL, connectOption };
......
1 const express = require("express"); 1 const express = require("express");
2 -const mysql = require("mysql"); 2 +const mysql2 = require("mysql2/promise");
3 -const fs = require("fs"); 3 +const { connectOption } = require("../libs/MySQL");
4 -const router = express.Router(); 4 +const dbRouter = express.Router();
5 5
6 -const [id, pw] = fs 6 +const schedules_dateRouter = require("./schedules_date");
7 - .readFileSync("server/libs/sql.pvdata", "utf8") 7 +const schedules_repeatRouter = require("./Schedules_repeat");
8 - .split("\r\n"); 8 +const schedules_timeRouter = require("./Schedules_time");
9 +const subjectsRouter = require("./Subjects");
10 +const userSubjectRouter = require("./user-subject");
11 +const usersRouter = require("./Users");
9 12
10 -const connection = mysql.createConnection({ 13 +async function route() {
11 - host: "localhost", 14 + const connection = await mysql2.createConnection(connectOption);
12 - user: id,
13 - password: pw,
14 - database: "mydb",
15 -});
16 15
17 -router.get("/", (req, res) => { 16 + // (userID, uid)
18 - res.send("DB Root"); 17 + dbRouter.delete("/schedule", async (req, res) => {
19 -}); 18 + console.log("delete /db/schedule");
20 - 19 + try {
21 -router.get("/mytable", (req, res) => { 20 + const queryString = `
22 - connection.query("SELECT * from mytable", (error, rows) => { 21 + DELETE FROM \`${req.body.table}\` sc
23 - if (error) throw error; 22 + WHERE sc.userID = ${req.body.userID}
24 - console.log(rows); 23 + AND sc.uid = ${req.body.uid}`;
25 - res.send(rows); 24 + await connection.query(queryString);
25 + res.end();
26 + } catch (e) {
27 + console.log(e);
28 + res.end();
29 + }
26 }); 30 });
27 -});
28 31
29 -module.exports = router; 32 + dbRouter.use("/user-subject", userSubjectRouter);
33 + dbRouter.use("/users", usersRouter);
34 + dbRouter.use("/subjects", subjectsRouter);
35 + dbRouter.use("/schedules_date", schedules_dateRouter);
36 + dbRouter.use("/schedules_time", schedules_timeRouter);
37 + dbRouter.use("/schedules_repeat", schedules_repeatRouter);
38 +}
39 +
40 +route();
41 +
42 +module.exports = dbRouter;
......
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const schedules_dateRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID, date) => schedules
10 + schedules_dateRouter.get("/", async (req, res) => {
11 + // console.log("/db/schedules_date");
12 + try {
13 + const queryString = `
14 + SELECT sc.label, sc.type, sc.description, sc.url, sc.detail,
15 + sbj.name, us.nickname, us.color, sc.uid, "schedules_date" \`table\`
16 + FROM schedules_date sc
17 + INNER JOIN \`user-subject\` us
18 + ON sc.userID = us.userID
19 + AND sc.subjectID = us.subjectID
20 + AND us.status = 1
21 + INNER JOIN subjects sbj
22 + ON sc.subjectID = sbj.ID
23 + WHERE sc.date = "${req.query.date}"
24 + AND sc.userID = ${req.query.userID}
25 + AND sc.status = 1`;
26 + const [results] = await connection.query(queryString);
27 + res.send(results);
28 + } catch (e) {
29 + console.log(e);
30 + res.end();
31 + }
32 + });
33 +
34 + // (userID, label, subjectID, type, description, url, date)
35 + schedules_dateRouter.post("/", async (req, res) => {
36 + console.log("post /db/schedules_date");
37 + try {
38 + let queryString = `
39 + INSERT INTO \`schedules_date\`
40 + (userID, label, subjectID, type, description, url, date, status)
41 + VALUES (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}",
42 + "${req.body.description}", "${req.body.url}", "${req.body.date}", 1);`;
43 + await connection.query(queryString);
44 +
45 + queryString = `UPDATE \`schedules_date\`
46 + SET uid = -ID
47 + WHERE ID = LAST_INSERT_ID();`;
48 + await connection.query(queryString);
49 +
50 + queryString = `
51 + SELECT sc.label, sc.type, sc.description, sc.url, sc.detail,
52 + sbj.name, us.nickname, us.color, sc.uid, "schedules_date" \`table\`
53 + FROM schedules_date sc
54 + INNER JOIN \`user-subject\` us
55 + ON sc.userID = us.userID
56 + AND sc.subjectID = us.subjectID
57 + AND us.status = 1
58 + INNER JOIN subjects sbj
59 + ON sc.subjectID = sbj.ID
60 + WHERE sc.ID = LAST_INSERT_ID()`;
61 + [results] = await connection.query(queryString);
62 + res.send(results[0]);
63 + } catch (e) {
64 + console.log(e);
65 + res.end();
66 + }
67 + });
68 +}
69 +route();
70 +
71 +module.exports = schedules_dateRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const schedules_repeatRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID, day) => schedules
10 + schedules_repeatRouter.get("/", async (req, res) => {
11 + // console.log("/db/schedules_repeat");
12 + try {
13 + const queryString = `
14 + SELECT sc.label, sc.type, sc.description, sc.uid, sc.url, sc.detail, sbj.name,
15 + us.nickname, us.color, sc.startTime, sc.endTime, "schedules_repeat" \`table\`
16 + FROM schedules_repeat sc
17 + INNER JOIN \`user-subject\` us
18 + ON sc.userID = us.userID
19 + AND sc.subjectID = us.subjectID
20 + AND us.status = 1
21 + INNER JOIN subjects sbj
22 + ON sc.subjectID = sbj.ID
23 + WHERE sc.day = "${req.query.day}"
24 + AND sc.userID = ${req.query.userID}
25 + AND sc.status = 1
26 + ORDER BY sc.startTime`;
27 + const [results] = await connection.query(queryString);
28 + res.send(results);
29 + } catch (e) {
30 + console.log(e);
31 + res.end();
32 + }
33 + });
34 +
35 + // (userID, label, subjectID, type, desciption, url, status, day, startTime, endTime)
36 + schedules_repeatRouter.post("/", async (req, res) => {
37 + console.log("/db/schedules_repeat");
38 + try {
39 + const queryString = `
40 + INSERT INTO schedules_repeat (userID, label, subjectID, type, status, day, startTime, endTime)
41 + VALUE (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}",
42 + 1, ${req.body.day}, "${req.body.startTime}", "${req.body.endTime}");`;
43 +
44 + queryString = `
45 + UPDATE schedules_repeat
46 + SET uid = -ID
47 + WHERE ID = LAST_INSERT_ID()`;
48 + await connection.query(queryString);
49 +
50 + queryString = `
51 + SELECT sc.label, sc.type, sc.description, sc.uid, sc.url, sc.detail, sbj.name,
52 + us.nickname, us.color, sc.startTime, sc.endTime, "schedules_repeat" \`table\`
53 + FROM schedules_repeat sc
54 + INNER JOIN \`user-subject\` us
55 + ON sc.userID = us.userID
56 + AND sc.subjectID = us.subjectID
57 + AND us.status = 1
58 + INNER JOIN subjects sbj
59 + ON sc.subjectID = sbj.ID
60 + WHERE sc.ID = LAST_INSERT_ID()
61 + ORDER BY sc.startTime`;
62 + [results] = await connection.query(queryString);
63 + res.send(results[0]);
64 + } catch (e) {
65 + console.log(e);
66 + res.end();
67 + }
68 + });
69 +}
70 +route();
71 +
72 +module.exports = schedules_repeatRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const schedules_timeRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID, date) => schedules
10 + schedules_timeRouter.get("/", async (req, res) => {
11 + // console.log("/db/schedules_time");
12 + try {
13 + const queryString = `
14 + SELECT sc.label, sc.type, sc.description, sc.url, sc.detail, sbj.name, us.nickname,
15 + us.color, sc.uid, sc.startTime, sc.endTime, "schedules_time" \`table\`
16 + FROM schedules_time sc
17 + INNER JOIN \`user-subject\` us
18 + ON sc.userID = us.userID
19 + AND sc.subjectID = us.subjectID
20 + AND us.status = 1
21 + INNER JOIN subjects sbj
22 + ON sc.subjectID = sbj.ID
23 + WHERE sc.date = "${req.query.date}"
24 + AND sc.userID = ${req.query.userID}
25 + AND sc.status = 1
26 + ORDER BY sc.endTime`;
27 + const [results] = await connection.query(queryString);
28 + res.send(results);
29 + } catch (e) {
30 + console.log(e);
31 + res.end();
32 + }
33 + });
34 +
35 + // (userID, label, subjectID, type, description, url, date, startTime, endTime)
36 + schedules_timeRouter.post("/", async (req, res) => {
37 + console.log("post /db/schedules_time");
38 + try {
39 + const startTime = req.body.startTime ? `"${req.body.startTime}"` : "null";
40 + const endTime = req.body.endTime ? `"${req.body.endTime}"` : "null";
41 + let queryString = `
42 + INSERT INTO \`schedules_time\`
43 + (userID, label, subjectID, type, description, url, date, status, startTime, endTime)
44 + VALUES (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}", "${req.body.description}",
45 + "${req.body.url}", "${req.body.date}", 1, ${startTime}, ${endTime});`;
46 + await connection.query(queryString);
47 +
48 + queryString = `
49 + UPDATE schedules_time
50 + SET uid = -ID
51 + WHERE ID = LAST_INSERT_ID()`;
52 + await connection.query(queryString);
53 +
54 + queryString = `SELECT sc.label, sc.type, sc.description, sc.url, sc.detail, sbj.name, us.nickname,
55 + us.color, sc.uid, sc.startTime, sc.endTime, "schedules_time" \`table\`
56 + FROM schedules_time sc
57 + INNER JOIN \`user-subject\` us
58 + ON sc.userID = us.userID
59 + AND sc.subjectID = us.subjectID
60 + AND us.status = 1
61 + INNER JOIN subjects sbj
62 + ON sc.subjectID = sbj.ID
63 + WHERE sc.ID = LAST_INSERT_ID()
64 + ORDER BY sc.endTime`;
65 + [results] = await connection.query(queryString);
66 + res.send(results[0]);
67 + } catch (e) {
68 + console.log(e);
69 + res.end();
70 + }
71 + });
72 +}
73 +route();
74 +
75 +module.exports = schedules_timeRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const subjectsRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (ID) => null | name //unused
10 + subjectsRouter.get("/", async (req, res) => {
11 + console.log("/db/subjects");
12 + try {
13 + const queryString = `
14 + SELECT name FROM subjects sbj
15 + WHERE sbj.ID = ${req.query.ID}`;
16 + const [results] = await connection.query(queryString);
17 + res.send(results.length && results[0].name);
18 + } catch (e) {
19 + console.log(e);
20 + res.end();
21 + }
22 + });
23 +}
24 +route();
25 +
26 +module.exports = subjectsRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const userSubjectRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID) => [{userID, subjectID, nickname, status, color, name}, ...]
10 + userSubjectRouter.get("/", async (req, res) => {
11 + console.log("/db/user-subject");
12 + try {
13 + const queryString = `
14 + SELECT us.userID, us.subjectID, us.nickname, us.status, us.color, sub.name
15 + FROM \`user-subject\` us
16 + INNER JOIN subjects sub
17 + ON us.subjectID = sub.ID
18 + WHERE us.userID = ${req.query.userID}`;
19 + const [results] = await connection.query(queryString);
20 + res.send(results);
21 + } catch (e) {
22 + console.log(e);
23 + res.end();
24 + }
25 + });
26 +
27 + // (userID, subjectID, status)
28 + userSubjectRouter.put("/check", async (req, res) => {
29 + console.log("/db/user-subject/check");
30 + try {
31 + const queryString = `
32 + UPDATE \`user-subject\`
33 + SET status = ${req.body.status}
34 + WHERE userID = ${req.body.userID}
35 + AND subjectID = ${req.body.subjectID}`;
36 + await connection.query(queryString);
37 + res.end();
38 + } catch (e) {
39 + console.log(e);
40 + res.end();
41 + }
42 + });
43 +
44 + // (userID, subjectID, nickname, color)
45 + userSubjectRouter.put("/modify", async (req, res) => {
46 + console.log("/db/user-subject/modify");
47 + try {
48 + const queryString = `
49 + UPDATE \`user-subject\`
50 + SET color = '${req.body.color}',
51 + nickname = '${req.body.nickname}'
52 + WHERE userID = ${req.body.userID}
53 + AND subjectID = ${req.body.subjectID}`;
54 + await connection.query(queryString);
55 + res.end();
56 + } catch (e) {
57 + console.log(e);
58 + res.end();
59 + }
60 + });
61 +}
62 +route();
63 +
64 +module.exports = userSubjectRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const usersRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (loginID) => null | ID(str)
10 + usersRouter.get("/", async (req, res) => {
11 + console.log("/db/users");
12 + try {
13 + const queryString = `
14 + SELECT ID FROM users us
15 + WHERE us.loginID = '${req.query.loginID}'`;
16 + const [results] = await connection.query(queryString);
17 + res.send(results.length ? results[0].ID.toString() : null);
18 + } catch (e) {
19 + console.log(e);
20 + res.end();
21 + }
22 + });
23 +
24 + // (loginID, loginPW) => null | "correct"
25 + usersRouter.get("/check", async (req, res) => {
26 + console.log("/db/users/check");
27 + try {
28 + const queryString = `
29 + SELECT loginPW FROM users us
30 + WHERE us.loginID = '${req.query.loginID}'`;
31 + const [results] = await connection.query(queryString);
32 + res.send(results[0].loginPW === req.query.loginPW ? "correct" : null);
33 + } catch (e) {
34 + console.log(e);
35 + res.end();
36 + }
37 + });
38 +}
39 +route();
40 +
41 +module.exports = usersRouter;
1 const express = require("express"); 1 const express = require("express");
2 -const bodyParser = require("body-parser");
3 const fs = require("fs"); 2 const fs = require("fs");
4 const cors = require("cors"); 3 const cors = require("cors");
5 -const { getPeed, getSubjects, login } = require("./libs/E_Campus.js");
6 4
7 const loginRouter = require("./routers/Login"); 5 const loginRouter = require("./routers/Login");
8 const dbRouter = require("./routers/DB"); 6 const dbRouter = require("./routers/DB");
...@@ -15,12 +13,13 @@ app.use( ...@@ -15,12 +13,13 @@ app.use(
15 credentials: true, 13 credentials: true,
16 }) 14 })
17 ); 15 );
18 -app.use(bodyParser.urlencoded({ extended: false })); 16 +// app.use(bodyParser.urlencoded({ extended: false }));
19 -app.use(bodyParser.json()); 17 +// app.use(bodyParser.json());
18 +app.use(express.json());
19 +app.use(express.urlencoded({ extended: true }));
20 20
21 app.post("/", (req, res) => { 21 app.post("/", (req, res) => {
22 - res.send({ body: req.body }); 22 + console.log("/");
23 - console.log("listened /");
24 }); 23 });
25 24
26 app.use("/login", loginRouter); 25 app.use("/login", loginRouter);
......
1 -import { useContext, useEffect, useState } from "react"; 1 +import { useContext, useEffect, useRef, useState } from "react";
2 +import localforage from "localforage";
2 3
3 import { toYMD, toYMDStr } from "../utils/Dates"; 4 import { toYMD, toYMDStr } from "../utils/Dates";
4 -import { scheForage } from "../utils/LocalForage";
5 5
6 import { CalendarStateContext } from "../pages/Calendar"; 6 import { CalendarStateContext } from "../pages/Calendar";
7 import ScheduleItem from "./ScheduleItem"; 7 import ScheduleItem from "./ScheduleItem";
8 +import axios from "axios";
8 9
9 const GridItem = ({ targetDate }) => { 10 const GridItem = ({ targetDate }) => {
10 - const { state } = useContext(CalendarStateContext); 11 + const { state: calState, subsObj } = useContext(CalendarStateContext);
11 - const { month: calMonth } = toYMD(state.date); 12 + const { month: calMonth } = toYMD(calState.date);
12 const { month, date } = toYMD(targetDate); 13 const { month, date } = toYMD(targetDate);
13 const [schedules, setSchedules] = useState(); 14 const [schedules, setSchedules] = useState();
15 + const [state, setState] = useState({
16 + label: "",
17 + startTime: "",
18 + endTime: "",
19 + description: "",
20 + url: "",
21 + type: "assignment",
22 + subjectID: 0,
23 + });
14 24
15 useEffect(() => { 25 useEffect(() => {
16 async function loadScheduleItems() { 26 async function loadScheduleItems() {
17 - setSchedules(await scheForage.getItem(toYMDStr(targetDate))); 27 + const params = {
28 + userID: await localforage.getItem("userID"),
29 + date: toYMDStr(targetDate, "-"),
30 + day: targetDate.getDay(),
31 + };
32 + const { data: scdate } = await axios.get(
33 + "http://localhost:3001/db/schedules_date",
34 + { params }
35 + );
36 + const { data: sctime } = await axios.get(
37 + "http://localhost:3001/db/schedules_time",
38 + { params }
39 + );
40 + const { data: scrpeat } = await axios.get(
41 + "http://localhost:3001/db/schedules_repeat",
42 + { params }
43 + );
44 + const subs = await localforage.getItem("subjects");
45 + setState({ ...state, subjectID: subs[0].subjectID });
46 + setSchedules(scrpeat.concat(scdate, sctime));
18 } 47 }
19 loadScheduleItems(); 48 loadScheduleItems();
20 }, [targetDate]); 49 }, [targetDate]);
21 50
51 + const handleChangeState = (e) => {
52 + setState({
53 + ...state,
54 + [e.target.name]: e.target.value,
55 + });
56 + };
57 +
58 + const finishSchedule = async (table, uid) => {
59 + for (const i in schedules)
60 + if (schedules[i].uid === uid) {
61 + await axios.delete("http://localhost:3001/db/schedule", {
62 + data: {
63 + table,
64 + uid,
65 + userID: await localforage.getItem("userID"),
66 + },
67 + });
68 + schedules.splice(i, 1);
69 + setSchedules(schedules);
70 + }
71 + };
72 +
73 + const popupRef = useRef();
74 + const click = async (e) => {
75 + if (e.target.classList.contains("gi")) {
76 + popupRef.current.style.display = "flex";
77 + } else if (e.target.className === "gipc") {
78 + popupRef.current.style.display = "none";
79 + } else if (e.target.className === "gipa") {
80 + // (userID, label, subjectID, type, description, url, date)
81 + const table =
82 + state.startTime || state.endTime ? "schedules_time" : "schedules_date";
83 + const { data: sche } = await axios.post(
84 + "http://localhost:3001/db/" + table,
85 + {
86 + userID: await localforage.getItem("userID"),
87 + ...state,
88 + date: toYMDStr(targetDate, "-"),
89 + }
90 + );
91 + setSchedules(schedules.concat(sche));
92 + popupRef.current.style.display = "none";
93 + }
94 + };
95 +
22 return ( 96 return (
23 - <div className="GridItem" relative={month - calMonth || null}> 97 + <div
24 - <span className="date"> 98 + className="GridItem gi"
99 + relative={month - calMonth || null}
100 + onClick={click}
101 + >
102 + <span className="date gi">
25 {calMonth !== month ? month + "/" + date : date} 103 {calMonth !== month ? month + "/" + date : date}
26 </span> 104 </span>
27 {schedules && 105 {schedules &&
28 schedules.map((sche, index) => ( 106 schedules.map((sche, index) => (
29 - <ScheduleItem key={index} schedule={sche} /> 107 + <ScheduleItem key={index} schedule={sche} finish={finishSchedule} />
30 ))} 108 ))}
109 + <div className="gi_popup" popup="true" ref={popupRef}>
110 + <span>일정 추가</span>
111 + <div className="gipd">
112 + <input
113 + name="label"
114 + placeholder="이름"
115 + value={state.label}
116 + onChange={handleChangeState}
117 + />
118 + <select
119 + name="subjectID"
120 + value={state.subjectID}
121 + onChange={handleChangeState}
122 + >
123 + {Object.values(subsObj).map((sub, index) => (
124 + <option key={index} value={sub.subjectID}>
125 + {sub.name}
126 + </option>
127 + ))}
128 + </select>
129 + <select name="type" value={state.type} onChange={handleChangeState}>
130 + <option value={"assignment"}>E-Campus</option>
131 + <option value={"zoom"}>Zoom</option>
132 + </select>
133 + <input
134 + name="startTime"
135 + placeholder="시작 (HH:MM)"
136 + value={state.startTime}
137 + onChange={handleChangeState}
138 + />
139 + <input
140 + name="endTime"
141 + placeholder="종료 (HH:MM)"
142 + value={state.endTime}
143 + onChange={handleChangeState}
144 + />
145 + <textarea
146 + name="description"
147 + placeholder="설명"
148 + value={state.description}
149 + onChange={handleChangeState}
150 + />
151 + <textarea
152 + name="url"
153 + placeholder="링크"
154 + value={state.url}
155 + onChange={handleChangeState}
156 + />
157 + </div>
158 + <div className="gip_btn">
159 + <button className="gipc">취소</button>
160 + <button className="gipa">추가</button>
161 + </div>
162 + </div>
31 </div> 163 </div>
32 ); 164 );
33 }; 165 };
......
1 +import localforage from "localforage";
1 import { useContext } from "react"; 2 import { useContext } from "react";
2 import { useNavigate } from "react-router-dom"; 3 import { useNavigate } from "react-router-dom";
3 4
4 import { CalendarStateContext } from "../pages/Calendar"; 5 import { CalendarStateContext } from "../pages/Calendar";
5 import "../styles/Header.css"; 6 import "../styles/Header.css";
6 import { moveDate, toYMD } from "../utils/Dates"; 7 import { moveDate, toYMD } from "../utils/Dates";
7 -import { dataForage } from "../utils/LocalForage";
8 8
9 const Header = () => { 9 const Header = () => {
10 const { state, setState } = useContext(CalendarStateContext); 10 const { state, setState } = useContext(CalendarStateContext);
...@@ -93,7 +93,7 @@ const Header = () => { ...@@ -93,7 +93,7 @@ const Header = () => {
93 <button 93 <button
94 className="hrb_r" 94 className="hrb_r"
95 onClick={async () => { 95 onClick={async () => {
96 - await dataForage.setItem("session", ""); 96 + await localforage.setItem("session", null);
97 navigate("/"); 97 navigate("/");
98 }} 98 }}
99 > 99 >
......
1 -import { useContext } from "react"; 1 +import { useRef } from "react";
2 -
3 -import { CalendarStateContext } from "../pages/Calendar";
4 import zoomSymbol from "../assets/zoom.png"; 2 import zoomSymbol from "../assets/zoom.png";
5 import ecampusSymbol from "../assets/e-Campus.png"; 3 import ecampusSymbol from "../assets/e-Campus.png";
6 4
7 -const ScheduleItem = ({ schedule }) => { 5 +const ScheduleItem = ({ schedule, finish }) => {
8 - const { subCode, type, category, label, start, end } = schedule; 6 + const {
9 - const { subsObj } = useContext(CalendarStateContext); 7 + name: subjectName,
10 - const subject = subsObj[subCode]; 8 + nickname: subjectNickname,
11 - if (!subject) { 9 + uid: scheUID,
12 - console.log("can't find " + subCode); 10 + color: subjectColor,
13 - return; 11 + url,
14 - } 12 + type,
15 - if (!subject.selected) return; 13 + label,
14 + description = null,
15 + detail = null,
16 + startTime = null, //HHMMSS문자열
17 + endTime = null,
18 + table,
19 + } = schedule;
20 + let sTime = startTime ? startTime.substring(0, 5) : ""; //HHMM
21 + let eTime = endTime ? endTime.substring(0, 5) : "";
16 22
17 const selectSymbol = () => { 23 const selectSymbol = () => {
18 let symbol; 24 let symbol;
...@@ -20,7 +26,7 @@ const ScheduleItem = ({ schedule }) => { ...@@ -20,7 +26,7 @@ const ScheduleItem = ({ schedule }) => {
20 case "zoom": 26 case "zoom":
21 symbol = zoomSymbol; 27 symbol = zoomSymbol;
22 break; 28 break;
23 - case "ecampus": 29 + case "assignment":
24 symbol = ecampusSymbol; 30 symbol = ecampusSymbol;
25 break; 31 break;
26 default: 32 default:
...@@ -28,13 +34,53 @@ const ScheduleItem = ({ schedule }) => { ...@@ -28,13 +34,53 @@ const ScheduleItem = ({ schedule }) => {
28 return symbol; 34 return symbol;
29 }; 35 };
30 36
37 + const popupRef = useRef();
38 + const thisRef = useRef();
39 +
40 + const click = (e) => {
41 + //Item
42 + if (e.target.classList.contains("ss"))
43 + popupRef.current.style.display = "grid";
44 + else {
45 + if (e.target.className === "spc") popupRef.current.style.display = "none"; //popup close
46 + if (e.target.className === "spd") {
47 + finish(table, scheUID);
48 + thisRef.current.style.display = "none";
49 + }
50 + }
51 + };
52 +
31 return ( 53 return (
32 - <div className="ScheduleItem" style={{ borderColor: subject.color }}> 54 + <div
33 - <img className="s_symbol" src={selectSymbol()} alt="404" /> 55 + className="ScheduleItem ss"
34 - {start && <span className="s_start">{start[0] + ":" + start[1]}</span>} 56 + style={{ borderColor: "#" + subjectColor }}
35 - {end && <span className="s_end">{end[0] + ":" + end[1]}</span>} 57 + onClick={click}
36 - <span className="s_category">{category}</span> 58 + ref={thisRef}
37 - <span className="s_slabel">{label}</span> 59 + >
60 + <img className="s_symbol ss" src={selectSymbol()} alt="404" />
61 + {startTime && <span className="s_start ss">{sTime}</span>}
62 + {endTime && <span className="s_end ss">{eTime}</span>}
63 + <span className="s_slabel ss">{label}</span>
64 +
65 + <div className="s_popup" popup="true" ref={popupRef}>
66 + <div className="spl">
67 + <span>{subjectName}</span>
68 + {url ? (
69 + <a href={url} target="_blank">
70 + {label}
71 + </a>
72 + ) : (
73 + <span>{label}</span>
74 + )}
75 + {(startTime || endTime) && <span>{sTime + " ~ " + eTime}</span>}
76 + {description && <span>{description}</span>}
77 + {detail && <div dangerouslySetInnerHTML={{ __html: detail }}></div>}
78 + </div>
79 + <div className="sp_btn">
80 + <button className="spd">완료</button>
81 + <button className="spc">닫기</button>
82 + </div>
83 + </div>
38 </div> 84 </div>
39 ); 85 );
40 }; 86 };
......
...@@ -11,12 +11,7 @@ const Side = () => { ...@@ -11,12 +11,7 @@ const Side = () => {
11 11
12 for (const code in subsObj) { 12 for (const code in subsObj) {
13 sideSubjects.push( 13 sideSubjects.push(
14 - <SideSubject 14 + <SideSubject key={code} subject={subsObj[code]} dispatch={dispatch} />
15 - key={code}
16 - code={code}
17 - subject={subsObj[code]}
18 - dispatch={dispatch}
19 - />
20 ); 15 );
21 } 16 }
22 17
......
1 -const SideSubject = ({ code, subject, dispatch }) => { 1 +import { useRef, useState } from "react";
2 +
3 +const SideSubject = ({ subject, dispatch }) => {
2 const defaultColor = "#EFEFEF"; 4 const defaultColor = "#EFEFEF";
5 + const [state, setState] = useState({
6 + nickname: subject.nickname || subject.name,
7 + color: subject.color,
8 + });
9 +
10 + const handleChangeState = (e) => {
11 + setState({
12 + ...state,
13 + [e.target.name]: e.target.value,
14 + });
15 + };
3 16
4 const check = (e) => { 17 const check = (e) => {
5 - dispatch({ type: "CHECKED", code }); 18 + dispatch({ type: "CHECKED", subjectID: subject.subjectID });
6 - if (subject.selected) e.target.style["background-color"] = defaultColor; 19 + if (subject.status) e.target.style["background-color"] = defaultColor;
7 else e.target.style["background-color"] = subject.color; 20 else e.target.style["background-color"] = subject.color;
8 }; 21 };
9 22
23 + const popupRef = useRef();
24 +
25 + const labelClick = (e) => {
26 + popupRef.current.style.display = "flex";
27 + };
28 +
29 + const popupClose = (e) => {
30 + setState({
31 + nickname: subject.nickname || subject.name,
32 + color: subject.color,
33 + });
34 + popupRef.current.style.display = "none";
35 + };
36 +
37 + const popupApply = (e) => {
38 + if (state.nickname.length < 1) alert("이름을 입력해 주세요");
39 + else if (state.color.length !== 6) alert("색상은 6자리 16진수 값 입니다");
40 + else {
41 + let check = true;
42 + for (const c of state.color) if (isNaN(parseInt(c, 16))) check = false;
43 + if (!check) alert("색상은 16진수 값 입니다");
44 + else {
45 + dispatch({
46 + type: "MODIFY",
47 + subjectID: subject.subjectID,
48 + nickname: state.nickname,
49 + color: state.color,
50 + });
51 + e.target.offsetParent.style.display = "none";
52 + }
53 + }
54 + };
55 +
10 return ( 56 return (
11 <div className="SideSubject"> 57 <div className="SideSubject">
12 <div 58 <div
13 className="ssc" 59 className="ssc"
14 onClick={check} 60 onClick={check}
15 style={{ 61 style={{
16 - backgroundColor: subject.selected ? subject.color : defaultColor, 62 + backgroundColor: subject.status ? "#" + subject.color : defaultColor,
17 }} 63 }}
18 ></div> 64 ></div>
19 - 65 + <span className="ssl" onClick={labelClick}>
20 - <span>{subject.name}</span> 66 + {subject.nickname || subject.name}
67 + </span>
68 + <div className="ss_popup" popup="true" ref={popupRef}>
69 + <div className="sspd">
70 + <div className="sspd_1">
71 + <span>이름</span>
72 + <input
73 + name="nickname"
74 + value={state.nickname}
75 + onChange={handleChangeState}
76 + />
77 + </div>
78 + <div className="sspd_2">
79 + <span>색상</span>
80 + <input
81 + name="color"
82 + value={state.color}
83 + onChange={handleChangeState}
84 + />
85 + </div>
86 + </div>
87 + <div className="ssp_btns">
88 + <button onClick={popupApply}>적용 </button>
89 + <button onClick={popupClose}>취소</button>
90 + </div>
91 + </div>
21 </div> 92 </div>
22 ); 93 );
23 }; 94 };
......
1 +import React, { useContext } from "react";
2 +import GridItem from "./GridItem.js";
3 +
4 +import { CalendarStateContext } from "../pages/Calendar";
5 +import { moveDate, toSunday } from "../utils/Dates";
6 +import "../styles/Week.css";
7 +
8 +const GridHead = () => {
9 + const days = ["일", "월", "화", "수", "목", "금", "토"];
10 + const renderItems = () => {
11 + const items = [];
12 + for (let i = 0; i < 7; i++) {
13 + items.push(
14 + <div className="GridHeadItem" key={i}>
15 + {days[i]}
16 + </div>
17 + );
18 + }
19 + return items;
20 + };
21 +
22 + return <div className="GridHead">{renderItems()}</div>;
23 +};
24 +
25 +const Row = () => {
26 + const { state } = useContext(CalendarStateContext);
27 +
28 + const renderItems = () => {
29 + const items = [];
30 + const ndate = new Date(state.date);
31 + toSunday(ndate);
32 +
33 + for (let i = 0; i < 7; i++) {
34 + items.push(<GridItem key={i} targetDate={new Date(ndate)} />);
35 + moveDate(ndate, "day", 1);
36 + }
37 +
38 + return items;
39 + };
40 +
41 + return <div className="Row">{renderItems()}</div>;
42 +};
43 +
44 +const Week = () => {
45 + return (
46 + <div className="Week">
47 + <GridHead />
48 + <Row />
49 + </div>
50 + );
51 +};
52 +
53 +export default Week;
1 import React, { useEffect, useReducer, useState } from "react"; 1 import React, { useEffect, useReducer, useState } from "react";
2 import { useNavigate, Route, Routes } from "react-router-dom"; 2 import { useNavigate, Route, Routes } from "react-router-dom";
3 3
4 -import { initTempSubjects } from "../utils/Test";
5 -import { dataForage, subForage } from "../utils/LocalForage";
6 -
7 import Month from "../components/Month"; 4 import Month from "../components/Month";
8 import Header from "../components/Header"; 5 import Header from "../components/Header";
9 import Side from "../components/Side"; 6 import Side from "../components/Side";
7 +import localforage from "localforage";
8 +import axios from "axios";
9 +import Week from "../components/Week";
10 10
11 export const CalendarStateContext = React.createContext(); 11 export const CalendarStateContext = React.createContext();
12 12
13 const render = (subsObj, args) => { 13 const render = (subsObj, args) => {
14 + let sub;
14 switch (args.type) { 15 switch (args.type) {
15 case "CHECKED": 16 case "CHECKED":
16 - const sub = subsObj[args.code]; 17 + sub = subsObj[args.subjectID];
17 - sub.selected = !sub.selected; 18 + sub.status = !sub.status;
18 - subForage.setItem(args.code, sub); 19 + axios.put("http://localhost:3001/db/user-subject/check", {
19 - return { ...subsObj, [args.code]: sub }; 20 + userID: sub.userID,
21 + subjectID: args.subjectID,
22 + status: +sub.status,
23 + });
24 + return { ...subsObj, [args.subjectID]: sub };
25 + case "MODIFY":
26 + sub = subsObj[args.subjectID];
27 + sub.nickname = args.nickname;
28 + sub.color = args.color;
29 + axios.put("http://localhost:3001/db/user-subject/modify", {
30 + userID: sub.userID,
31 + subjectID: args.subjectID,
32 + nickname: sub.nickname,
33 + color: sub.color,
34 + });
35 + return { ...subsObj, [args.subjectID]: sub };
20 case "INIT": 36 case "INIT":
21 return args.subsObj; 37 return args.subsObj;
22 default: 38 default:
...@@ -36,12 +52,17 @@ const Calendar = () => { ...@@ -36,12 +52,17 @@ const Calendar = () => {
36 const navigate = useNavigate(); 52 const navigate = useNavigate();
37 useEffect(() => { 53 useEffect(() => {
38 async function onMount() { 54 async function onMount() {
39 - if (!(await dataForage.getItem("session"))) return navigate("/login"); 55 + if (!(await localforage.getItem("session"))) return navigate("/login");
40 - 56 + // get user's subjects
41 - if (!(await dataForage.getItem("Subjects"))) await initTempSubjects(); 57 + const userID = await localforage.getItem("userID");
58 + const { data: subjects } = await axios.get(
59 + "http://localhost:3001/db/user-subject",
60 + { params: { userID } }
61 + );
62 + await localforage.setItem("subjects", subjects);
42 let tsubsObj = {}; 63 let tsubsObj = {};
43 - for (const code of await dataForage.getItem("Subjects")) { 64 + for (const sub of subjects) {
44 - tsubsObj[code] = await subForage.getItem(code); 65 + tsubsObj[sub.subjectID] = sub;
45 } 66 }
46 dispatch({ type: "INIT", subsObj: tsubsObj }); 67 dispatch({ type: "INIT", subsObj: tsubsObj });
47 } 68 }
...@@ -58,7 +79,7 @@ const Calendar = () => { ...@@ -58,7 +79,7 @@ const Calendar = () => {
58 <Side /> 79 <Side />
59 <Routes> 80 <Routes>
60 <Route path="/month/*" element={<Month />} /> 81 <Route path="/month/*" element={<Month />} />
61 - <Route path="/week/*" element={<></>} /> 82 + <Route path="/week/*" element={<Week />} />
62 <Route path="/day/*" element={<></>} /> 83 <Route path="/day/*" element={<></>} />
63 </Routes> 84 </Routes>
64 </div> 85 </div>
......
...@@ -4,7 +4,6 @@ import { useNavigate } from "react-router-dom"; ...@@ -4,7 +4,6 @@ import { useNavigate } from "react-router-dom";
4 //import { login } from "../libs/E_Campus"; 4 //import { login } from "../libs/E_Campus";
5 5
6 import "../styles/Debug.css"; 6 import "../styles/Debug.css";
7 -import { initTempSubjects } from "../utils/Test";
8 7
9 const Debug = () => { 8 const Debug = () => {
10 console.log("visit Debug"); 9 console.log("visit Debug");
...@@ -26,10 +25,6 @@ const Debug = () => { ...@@ -26,10 +25,6 @@ const Debug = () => {
26 const handleSubmit = async (e) => { 25 const handleSubmit = async (e) => {
27 let result; 26 let result;
28 switch (state.type) { 27 switch (state.type) {
29 - case "initSubjects":
30 - await initTempSubjects();
31 - result = "inited";
32 - break;
33 case "login": 28 case "login":
34 //result = login(state.input1, state.input2); 29 //result = login(state.input1, state.input2);
35 break; 30 break;
...@@ -66,7 +61,6 @@ const Debug = () => { ...@@ -66,7 +61,6 @@ const Debug = () => {
66 <div> 61 <div>
67 <span>type : </span> 62 <span>type : </span>
68 <select name="type" value={state.type} onChange={handleChangeState}> 63 <select name="type" value={state.type} onChange={handleChangeState}>
69 - <option value={"initSubjects"}>initSubjects</option>
70 <option value={"login"}>login</option> 64 <option value={"login"}>login</option>
71 <option value={"server"}>server</option> 65 <option value={"server"}>server</option>
72 </select> 66 </select>
......
1 +import localforage from "localforage";
1 import { useEffect } from "react"; 2 import { useEffect } from "react";
2 import { useNavigate } from "react-router-dom"; 3 import { useNavigate } from "react-router-dom";
3 4
4 -import { dataForage } from "../utils/LocalForage";
5 -
6 const Home = () => { 5 const Home = () => {
7 console.log("visit Home"); 6 console.log("visit Home");
8 7
...@@ -10,7 +9,7 @@ const Home = () => { ...@@ -10,7 +9,7 @@ const Home = () => {
10 useEffect(() => { 9 useEffect(() => {
11 async function where() { 10 async function where() {
12 let destination; 11 let destination;
13 - if (await dataForage.getItem("session")) { 12 + if (await localforage.getItem("session")) {
14 destination = "/calendar/month"; 13 destination = "/calendar/month";
15 } else { 14 } else {
16 destination = "/login"; 15 destination = "/login";
......
1 import { useEffect, useState } from "react"; 1 import { useEffect, useState } from "react";
2 import { useNavigate } from "react-router-dom"; 2 import { useNavigate } from "react-router-dom";
3 +import localforage from "localforage";
3 4
4 -import { dataForage } from "../utils/LocalForage";
5 import "../styles/Login.css"; 5 import "../styles/Login.css";
6 import axios from "axios"; 6 import axios from "axios";
7 +import cryptoJs from "crypto-js";
7 8
8 const Login = () => { 9 const Login = () => {
9 console.log("visit Login"); 10 console.log("visit Login");
10 const [state, setState] = useState({ 11 const [state, setState] = useState({
11 id: "", 12 id: "",
12 pw: "", 13 pw: "",
14 + btn: "Login",
13 }); 15 });
14 16
15 const handleChangeState = (e) => { 17 const handleChangeState = (e) => {
...@@ -21,21 +23,57 @@ const Login = () => { ...@@ -21,21 +23,57 @@ const Login = () => {
21 23
22 const navigate = useNavigate(); 24 const navigate = useNavigate();
23 const login = async () => { 25 const login = async () => {
24 - const res = await axios.post("http://localhost:3001/login/", { 26 + setState({ ...state, btn: "Login..." });
25 - id: state.id, 27 + const { data: userDBID } = await axios.get(
26 - pw: state.pw, 28 + "http://localhost:3001/db/users",
27 - }); 29 + {
30 + params: {
31 + loginID: state.id,
32 + },
33 + }
34 + );
28 35
29 - if (res.data === "login failed") alert("ID/PW를 확인해주세요"); 36 + if (userDBID) {
30 - else { 37 + //pass crawling
31 - await dataForage.setItem("session", true); 38 + const hashpw = cryptoJs.SHA256(state.pw).toString();
32 - navigate("/"); 39 + const { data: isCorrectPW } = await axios.get(
40 + "http://localhost:3001/db/users/check",
41 + {
42 + params: { loginID: state.id, loginPW: hashpw },
43 + }
44 + );
45 + if (isCorrectPW) await localforage.setItem("userID", Number(userDBID));
46 + else {
47 + setState({ ...state, btn: "Login" });
48 + alert("ID/PW를 확인해주세요");
49 + return;
50 + }
51 + } else {
52 + //crawling
53 + const { data: loginResult } = await axios.post(
54 + "http://localhost:3001/login/",
55 + {
56 + id: state.id,
57 + pw: state.pw,
58 + }
59 + );
60 + if (loginResult === "login failed") {
61 + setState({ ...state, btn: "Login" });
62 + alert("ID/PW를 확인해주세요");
63 + return;
64 + }
65 + // + else (성공시) localforage에 userID추가
33 } 66 }
67 + // + localforage에 id pw 추가
68 + await localforage.setItem("id", state.id);
69 + await localforage.setItem("pw", state.pw);
70 + await localforage.setItem("session", true);
71 + navigate("/");
34 }; 72 };
35 73
36 useEffect(() => { 74 useEffect(() => {
37 async function ifAlreadyLogined() { 75 async function ifAlreadyLogined() {
38 - if (await dataForage.getItem("session")) navigate("/"); 76 + if (await localforage.getItem("session")) navigate("/");
39 } 77 }
40 ifAlreadyLogined(); 78 ifAlreadyLogined();
41 }, [navigate]); 79 }, [navigate]);
...@@ -61,7 +99,7 @@ const Login = () => { ...@@ -61,7 +99,7 @@ const Login = () => {
61 type="password" 99 type="password"
62 /> 100 />
63 </div> 101 </div>
64 - <button onClick={login}>Login</button> 102 + <button onClick={login}>{state.btn}</button>
65 </div> 103 </div>
66 ); 104 );
67 }; 105 };
......
1 +import localforage from "localforage";
1 import { Navigate, useNavigate } from "react-router-dom"; 2 import { Navigate, useNavigate } from "react-router-dom";
2 3
3 import "../styles/Settings.css"; 4 import "../styles/Settings.css";
4 -import { dataForage } from "../utils/LocalForage";
5 5
6 const Settings = () => { 6 const Settings = () => {
7 console.log("visit Settings"); 7 console.log("visit Settings");
8 8
9 - const session = dataForage.getItem("session"); 9 + const session = localforage.getItem("session");
10 10
11 const navigate = useNavigate(); 11 const navigate = useNavigate();
12 12
......
...@@ -47,12 +47,61 @@ button:disabled { ...@@ -47,12 +47,61 @@ button:disabled {
47 display: flex; 47 display: flex;
48 } 48 }
49 49
50 +.GridItem {
51 + position: relative;
52 +}
53 +
54 +.gi_popup {
55 + flex-direction: column;
56 + padding: 5px;
57 + top: 30px;
58 +}
59 +
60 +.gi_popup > span {
61 + font-size: large;
62 +}
63 +
64 +.gip_btn {
65 + display: flex;
66 + flex-direction: row-reverse;
67 +}
68 +
69 +.gip_btn > button {
70 + padding: 5px 9px 5px 9px;
71 + margin-left: 2px;
72 +}
73 +
74 +.gipd {
75 + margin: 5px 0 5px 0;
76 + display: grid;
77 + grid-template-columns: repeat(2, minmax(100px, auto));
78 + grid-template-rows: repeat(6, minmax(30px, auto));
79 + row-gap: 5px;
80 + column-gap: 5px;
81 +}
82 +
83 +.gipd > * {
84 + grid-column: 1 / span 2;
85 +}
86 +.gipd > :nth-child(4) {
87 + grid-column: 1;
88 +}
89 +.gipd > :nth-child(5) {
90 + grid-column: 2;
91 +}
92 +.gipd > :nth-child(6),
93 +.gipd > :nth-child(7) {
94 + resize: none;
95 + height: 60px;
96 +}
97 +
50 .ScheduleItem { 98 .ScheduleItem {
51 - height: 23px;
52 display: flex; 99 display: flex;
53 margin: 1px 0 1px 0; 100 margin: 1px 0 1px 0;
54 border: solid 3px bisque; 101 border: solid 3px bisque;
55 border-radius: 3px; 102 border-radius: 3px;
103 + cursor: pointer;
104 + position: relative;
56 } 105 }
57 106
58 .ScheduleItem > span { 107 .ScheduleItem > span {
...@@ -63,7 +112,7 @@ button:disabled { ...@@ -63,7 +112,7 @@ button:disabled {
63 } 112 }
64 113
65 .s_start { 114 .s_start {
66 - color: deepskyblue; 115 + color: blue;
67 /* text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white; */ 116 /* text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white; */
68 } 117 }
69 118
...@@ -78,3 +127,61 @@ button:disabled { ...@@ -78,3 +127,61 @@ button:disabled {
78 border-radius: 3px; 127 border-radius: 3px;
79 padding: 0; 128 padding: 0;
80 } 129 }
130 +
131 +[popup] {
132 + position: absolute;
133 + z-index: 1000;
134 + background: rgb(250, 250, 250);
135 + box-shadow: 0px 0px 5px gray;
136 + border: solid thin grey;
137 + border-radius: 5px;
138 + display: none;
139 + cursor: auto;
140 +}
141 +
142 +.s_popup {
143 + top: calc(100% + 5px);
144 + left: 20px;
145 + padding: 10px;
146 + width: 300px;
147 +}
148 +
149 +.spl {
150 + display: flex;
151 + flex-direction: column;
152 + align-items: flex-start;
153 +}
154 +
155 +.spl > * {
156 + margin-bottom: 3px;
157 +}
158 +
159 +.spl > :nth-child(1) {
160 + margin-bottom: 5px;
161 + font-size: large;
162 +}
163 +
164 +.spl > :nth-child(2) {
165 + font-size: medium;
166 +}
167 +
168 +.spl > div {
169 + margin-top: 5px;
170 + font-size: small;
171 + background-color: white;
172 + border: solid 1px rgb(235, 235, 235);
173 + width: calc(100% - 2px);
174 +}
175 +
176 +.sp_btn {
177 + display: flex;
178 + margin-left: calc(100% - 100px);
179 +}
180 +
181 +.sp_btn > button {
182 + margin-left: 2px;
183 + margin-top: 2px;
184 + width: 50px;
185 + height: 35px;
186 + cursor: pointer;
187 +}
......
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
12 12
13 .GridItem { 13 .GridItem {
14 border: solid thin lightgray; 14 border: solid thin lightgray;
15 - height: 150px; 15 + display: flex;
16 + flex-direction: column;
17 + /* height: 150px; */
16 padding: 5px; 18 padding: 5px;
17 } 19 }
18 20
...@@ -34,7 +36,7 @@ ...@@ -34,7 +36,7 @@
34 .Grid { 36 .Grid {
35 display: grid; 37 display: grid;
36 grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; 38 grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
37 - overflow-y: auto; 39 + overflow-y: scroll;
38 height: calc(100vh - 115px); 40 height: calc(100vh - 115px);
39 border: solid thin gray; 41 border: solid thin gray;
40 } 42 }
......
1 aside { 1 aside {
2 width: 220px; 2 width: 220px;
3 + flex-shrink: 0;
3 margin-right: 8px; 4 margin-right: 8px;
4 padding-top: 50px; 5 padding-top: 50px;
5 } 6 }
6 7
7 .SideSubject { 8 .SideSubject {
8 - height: 25px;
9 padding: 5px 0px 5px 0px; 9 padding: 5px 0px 5px 0px;
10 display: flex; 10 display: flex;
11 + position: relative;
11 } 12 }
12 13
13 .ssc { 14 .ssc {
...@@ -20,5 +21,48 @@ aside { ...@@ -20,5 +21,48 @@ aside {
20 } 21 }
21 22
22 .SideSubject > span { 23 .SideSubject > span {
24 + cursor: pointer;
23 line-height: 25px; 25 line-height: 25px;
24 } 26 }
27 +
28 +.ss_popup {
29 + flex-direction: column;
30 + align-items: flex-end;
31 + top: 30px;
32 + left: 30px;
33 + /* height: 150px;
34 + width: 250px; */
35 + /* transform: translate(-50%, -50%); */
36 + padding: 5px;
37 +}
38 +
39 +.sspd {
40 + display: flex;
41 + flex-direction: column;
42 +}
43 +
44 +.sspd > div {
45 + display: flex;
46 + margin-bottom: 3px;
47 +}
48 +
49 +.sspd > div > span {
50 + text-align: center;
51 + line-height: 30px;
52 + margin-right: 5px;
53 + width: 35px;
54 +}
55 +
56 +.sspd > div > input {
57 + flex-grow: 1;
58 + height: 24px;
59 +}
60 +
61 +.ssp_btns {
62 + margin-top: 2px;
63 +}
64 +
65 +.ssp_btns > button {
66 + padding: 5px 9px 5px 9px;
67 + margin-left: 2px;
68 +}
......
1 +.Week {
2 + display: flex;
3 + flex-direction: column;
4 + flex-grow: 1;
5 +}
6 +
7 +.GridItem,
8 +.GridHeadItem {
9 + flex-basis: 100px;
10 + flex-grow: 1;
11 +}
12 +
13 +.GridItem {
14 + border: solid thin lightgray;
15 + display: flex;
16 + flex-direction: column;
17 + /* height: 150px; */
18 + padding: 5px;
19 +}
20 +
21 +.GridHeadItem {
22 + height: 20px;
23 + text-align: center;
24 + line-height: 20px;
25 + padding: 10px 5px 10px 5px;
26 +}
27 +
28 +.GridHead {
29 + display: flex;
30 +}
31 +
32 +.GridHead {
33 + margin-right: 16.8px;
34 +}
35 +
36 +.Row {
37 + display: flex;
38 + overflow-y: scroll;
39 + height: calc(100vh - 115px);
40 + border: solid thin gray;
41 +}
42 +
43 +.GridItem[relative] > span {
44 + color: gray;
45 +}
...@@ -6,12 +6,12 @@ function toYMD(dateObj) { ...@@ -6,12 +6,12 @@ function toYMD(dateObj) {
6 return { year, month, date }; 6 return { year, month, date };
7 } 7 }
8 8
9 -function toYMDStr(dateObj) { 9 +function toYMDStr(dateObj, joint) {
10 return [ 10 return [
11 dateObj.getFullYear(), 11 dateObj.getFullYear(),
12 dateObj.getMonth() + 1, 12 dateObj.getMonth() + 1,
13 dateObj.getDate(), 13 dateObj.getDate(),
14 - ].join("/"); 14 + ].join(joint);
15 } 15 }
16 16
17 function toSunday(dateObj) { 17 function toSunday(dateObj) {
......
1 -import { subForage, scheForage, dataForage } from "./LocalForage";
2 -import { toYMDStr } from "./Dates";
3 -
4 -async function initTempSubjects() {
5 - const tempsch = [
6 - {
7 - subCode: "1",
8 - type: "zoom",
9 - category: "과목A",
10 - label: "",
11 - start: [9, 30],
12 - },
13 - {
14 - subCode: "2",
15 - type: "ecampus",
16 - category: "과목B",
17 - label: "과제",
18 - end: [23, 59],
19 - },
20 - ];
21 -
22 - await scheForage.setItem(toYMDStr(new Date("2022-5-20")), tempsch);
23 - await scheForage.setItem(toYMDStr(new Date("2022-5-27")), tempsch);
24 - await scheForage.setItem(toYMDStr(new Date("2022-6-3")), tempsch);
25 -
26 - let tcolors = [
27 - "red",
28 - "green",
29 - "blue",
30 - "orange",
31 - "gold",
32 - "aqua",
33 - "chartreuse",
34 - ];
35 - const subCodeLst = ["1", "2"];
36 - const subObj = {
37 - 1: {
38 - name: "과목A",
39 - color: tcolors[0],
40 - selected: true,
41 - },
42 - 2: {
43 - name: "과목B",
44 - color: tcolors[1],
45 - selected: true,
46 - },
47 - };
48 -
49 - await subForage.setItem("1", subObj["1"]);
50 - await subForage.setItem("2", subObj["2"]);
51 -
52 - for (let i = 2; i < 7; i++) {
53 - let code = (i + 1).toString();
54 - let tsub = {
55 - name: "과목" + code,
56 - color: tcolors[i],
57 - selected: true,
58 - };
59 - subCodeLst.push(code);
60 - await subForage.setItem(code, tsub);
61 - }
62 - dataForage.setItem("Subjects", subCodeLst);
63 -}
64 -
65 -export { initTempSubjects };