HyeonJun Jeon

Merge branch 'mysql' into 'master'

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