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