HyeonJun Jeon

[Add] Schedule add popup

...@@ -21,8 +21,7 @@ async function route() { ...@@ -21,8 +21,7 @@ async function route() {
21 DELETE FROM \`${req.body.table}\` sc 21 DELETE FROM \`${req.body.table}\` sc
22 WHERE sc.userID = ${req.body.userID} 22 WHERE sc.userID = ${req.body.userID}
23 AND sc.uid = ${req.body.uid}`; 23 AND sc.uid = ${req.body.uid}`;
24 - const [result] = await connection.query(queryString); 24 + await connection.query(queryString);
25 - console.log(req.body);
26 res.end(); 25 res.end();
27 } catch (e) { 26 } catch (e) {
28 console.log(e); 27 console.log(e);
......
...@@ -30,6 +30,41 @@ async function route() { ...@@ -30,6 +30,41 @@ async function route() {
30 res.end(); 30 res.end();
31 } 31 }
32 }); 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 + });
33 } 68 }
34 route(); 69 route();
35 70
......
...@@ -38,12 +38,29 @@ async function route() { ...@@ -38,12 +38,29 @@ async function route() {
38 try { 38 try {
39 const queryString = ` 39 const queryString = `
40 INSERT INTO schedules_repeat (userID, label, subjectID, type, status, day, startTime, endTime) 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}, 41 + VALUE (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}",
42 - ${req.body.status}, ${req.body.day}, ${req.body.startTime}, ${req.body.endTime}); 42 + 1, ${req.body.day}, "${req.body.startTime}", "${req.body.endTime}");`;
43 +
44 + queryString = `
43 UPDATE schedules_repeat 45 UPDATE schedules_repeat
44 - SET uid = -id;`; 46 + SET uid = -ID
47 + WHERE ID = LAST_INSERT_ID()`;
45 await connection.query(queryString); 48 await connection.query(queryString);
46 - res.end(); 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]);
47 } catch (e) { 64 } catch (e) {
48 console.log(e); 65 console.log(e);
49 res.end(); 66 res.end();
......
...@@ -31,6 +31,44 @@ async function route() { ...@@ -31,6 +31,44 @@ async function route() {
31 res.end(); 31 res.end();
32 } 32 }
33 }); 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 + });
34 } 72 }
35 route(); 73 route();
36 74
......
1 -import { useContext, useEffect, useState } from "react"; 1 +import { useContext, useEffect, useRef, useState } from "react";
2 import localforage from "localforage"; 2 import localforage from "localforage";
3 3
4 import { toYMD, toYMDStr } from "../utils/Dates"; 4 import { toYMD, toYMDStr } from "../utils/Dates";
...@@ -8,10 +8,19 @@ import ScheduleItem from "./ScheduleItem"; ...@@ -8,10 +8,19 @@ import ScheduleItem from "./ScheduleItem";
8 import axios from "axios"; 8 import axios from "axios";
9 9
10 const GridItem = ({ targetDate }) => { 10 const GridItem = ({ targetDate }) => {
11 - const { state } = useContext(CalendarStateContext); 11 + const { state: calState, subsObj } = useContext(CalendarStateContext);
12 - const { month: calMonth } = toYMD(state.date); 12 + const { month: calMonth } = toYMD(calState.date);
13 const { month, date } = toYMD(targetDate); 13 const { month, date } = toYMD(targetDate);
14 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 + });
15 24
16 useEffect(() => { 25 useEffect(() => {
17 async function loadScheduleItems() { 26 async function loadScheduleItems() {
...@@ -32,11 +41,20 @@ const GridItem = ({ targetDate }) => { ...@@ -32,11 +41,20 @@ const GridItem = ({ targetDate }) => {
32 "http://localhost:3001/db/schedules_repeat", 41 "http://localhost:3001/db/schedules_repeat",
33 { params } 42 { params }
34 ); 43 );
44 + const subs = await localforage.getItem("subjects");
45 + setState({ ...state, subjectID: subs[0].subjectID });
35 setSchedules(scrpeat.concat(scdate, sctime)); 46 setSchedules(scrpeat.concat(scdate, sctime));
36 } 47 }
37 loadScheduleItems(); 48 loadScheduleItems();
38 }, [targetDate]); 49 }, [targetDate]);
39 50
51 + const handleChangeState = (e) => {
52 + setState({
53 + ...state,
54 + [e.target.name]: e.target.value,
55 + });
56 + };
57 +
40 const finishSchedule = async (table, uid) => { 58 const finishSchedule = async (table, uid) => {
41 for (const i in schedules) 59 for (const i in schedules)
42 if (schedules[i].uid === uid) { 60 if (schedules[i].uid === uid) {
...@@ -52,15 +70,96 @@ const GridItem = ({ targetDate }) => { ...@@ -52,15 +70,96 @@ const GridItem = ({ targetDate }) => {
52 } 70 }
53 }; 71 };
54 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 +
55 return ( 96 return (
56 - <div className="GridItem" relative={month - calMonth || null}> 97 + <div
57 - <span className="date"> 98 + className="GridItem gi"
99 + relative={month - calMonth || null}
100 + onClick={click}
101 + >
102 + <span className="date gi">
58 {calMonth !== month ? month + "/" + date : date} 103 {calMonth !== month ? month + "/" + date : date}
59 </span> 104 </span>
60 {schedules && 105 {schedules &&
61 schedules.map((sche, index) => ( 106 schedules.map((sche, index) => (
62 <ScheduleItem key={index} schedule={sche} finish={finishSchedule} /> 107 <ScheduleItem key={index} schedule={sche} finish={finishSchedule} />
63 ))} 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>
64 </div> 163 </div>
65 ); 164 );
66 }; 165 };
......
...@@ -62,7 +62,7 @@ const ScheduleItem = ({ schedule, finish }) => { ...@@ -62,7 +62,7 @@ const ScheduleItem = ({ schedule, finish }) => {
62 {endTime && <span className="s_end ss">{eTime}</span>} 62 {endTime && <span className="s_end ss">{eTime}</span>}
63 <span className="s_slabel ss">{label}</span> 63 <span className="s_slabel ss">{label}</span>
64 64
65 - <div className="s_popup" ref={popupRef}> 65 + <div className="s_popup" popup="true" ref={popupRef}>
66 <div className="spl"> 66 <div className="spl">
67 <span>{subjectName}</span> 67 <span>{subjectName}</span>
68 {url ? ( 68 {url ? (
......
...@@ -65,7 +65,7 @@ const SideSubject = ({ subject, dispatch }) => { ...@@ -65,7 +65,7 @@ const SideSubject = ({ subject, dispatch }) => {
65 <span className="ssl" onClick={labelClick}> 65 <span className="ssl" onClick={labelClick}>
66 {subject.nickname || subject.name} 66 {subject.nickname || subject.name}
67 </span> 67 </span>
68 - <div className="ss_popup" ref={popupRef}> 68 + <div className="ss_popup" popup="true" ref={popupRef}>
69 <div className="sspd"> 69 <div className="sspd">
70 <div className="sspd_1"> 70 <div className="sspd_1">
71 <span>이름</span> 71 <span>이름</span>
......
...@@ -59,6 +59,7 @@ const Calendar = () => { ...@@ -59,6 +59,7 @@ const Calendar = () => {
59 "http://localhost:3001/db/user-subject", 59 "http://localhost:3001/db/user-subject",
60 { params: { userID } } 60 { params: { userID } }
61 ); 61 );
62 + await localforage.setItem("subjects", subjects);
62 let tsubsObj = {}; 63 let tsubsObj = {};
63 for (const sub of subjects) { 64 for (const sub of subjects) {
64 tsubsObj[sub.subjectID] = sub; 65 tsubsObj[sub.subjectID] = sub;
......
...@@ -47,6 +47,54 @@ button:disabled { ...@@ -47,6 +47,54 @@ 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 display: flex; 99 display: flex;
52 margin: 1px 0 1px 0; 100 margin: 1px 0 1px 0;
...@@ -80,18 +128,21 @@ button:disabled { ...@@ -80,18 +128,21 @@ button:disabled {
80 padding: 0; 128 padding: 0;
81 } 129 }
82 130
83 -.s_popup { 131 +[popup] {
84 position: absolute; 132 position: absolute;
85 z-index: 1000; 133 z-index: 1000;
86 - top: calc(100% + 5px);
87 - left: 20px;
88 background: rgb(250, 250, 250); 134 background: rgb(250, 250, 250);
89 box-shadow: 0px 0px 5px gray; 135 box-shadow: 0px 0px 5px gray;
90 border: solid thin grey; 136 border: solid thin grey;
91 border-radius: 5px; 137 border-radius: 5px;
92 display: none; 138 display: none;
93 - padding: 10px;
94 cursor: auto; 139 cursor: auto;
140 +}
141 +
142 +.s_popup {
143 + top: calc(100% + 5px);
144 + left: 20px;
145 + padding: 10px;
95 width: 300px; 146 width: 300px;
96 } 147 }
97 148
......
...@@ -28,18 +28,11 @@ aside { ...@@ -28,18 +28,11 @@ aside {
28 .ss_popup { 28 .ss_popup {
29 flex-direction: column; 29 flex-direction: column;
30 align-items: flex-end; 30 align-items: flex-end;
31 - position: absolute;
32 - z-index: 1000;
33 top: 30px; 31 top: 30px;
34 left: 30px; 32 left: 30px;
35 /* height: 150px; 33 /* height: 150px;
36 width: 250px; */ 34 width: 250px; */
37 /* transform: translate(-50%, -50%); */ 35 /* transform: translate(-50%, -50%); */
38 - background: rgb(250, 250, 250);
39 - box-shadow: 0px 0px 5px gray;
40 - border: solid thin grey;
41 - border-radius: 5px;
42 - display: none;
43 padding: 5px; 36 padding: 5px;
44 } 37 }
45 38
......