Showing
10 changed files
with
257 additions
and
24 deletions
... | @@ -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 | ... | ... |
-
Please register or login to post a comment