diff --git a/main.go b/main.go index e69bb90..1b1b6a1 100644 --- a/main.go +++ b/main.go @@ -761,7 +761,9 @@ func scheduleList(w http.ResponseWriter, r *http.Request, sess *session.T) { // w.Header().Add("Access-Control-Allow-Origin", "*") request := struct { - NodeID int + NodeID int + StartDate time.Time + EndDate time.Time }{} body, _ := io.ReadAll(r.Body) if len(body) > 0 { @@ -773,14 +775,14 @@ func scheduleList(w http.ResponseWriter, r *http.Request, sess *session.T) { // } var schedules []Schedule - schedules, err = FutureSchedules(sess.UserID, request.NodeID) + schedules, err = FutureSchedules(sess.UserID, request.NodeID, request.StartDate, request.EndDate) if err != nil { responseError(w, err) return } responseData(w, map[string]interface{}{ - "OK": true, + "OK": true, "ScheduleEvents": schedules, }) } // }}} diff --git a/schedule.go b/schedule.go index ba64e15..fe4b7b5 100644 --- a/schedule.go +++ b/schedule.go @@ -215,9 +215,17 @@ func ExpiredSchedules() (schedules []Schedule) { // {{{ } return } // }}} -func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) {// {{{ +func FutureSchedules(userID int, nodeID int, start time.Time, end time.Time) (schedules []Schedule, err error) { // {{{ schedules = []Schedule{} + var foo string + row := service.Db.Conn.QueryRow(`SELECT TO_CHAR($1::date AT TIME ZONE 'UTC', 'yyyy-mm-dd HH24:MI')`, start) + err = row.Scan(&foo) + if err != nil { + return + } + logger.Info("FOO", "date", foo) + res := service.Db.Conn.QueryRow(` WITH schedule_events AS ( SELECT @@ -243,6 +251,14 @@ func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) { WHEN $2 > 0 THEN n.id = $2 ELSE true END + ) AND ( + CASE WHEN TO_CHAR($3::date, 'yyyy-mm-dd HH24:MI') = '0001-01-01 00:00' THEN TRUE + ELSE (s.time AT TIME ZONE u.timezone) >= $3 + END + ) AND ( + CASE WHEN TO_CHAR($4::date, 'yyyy-mm-dd HH24:MI') = '0001-01-01 00:00' THEN TRUE + ELSE (s.time AT TIME ZONE u.timezone) <= $4 + END ) AND time >= NOW() AND NOT acknowledged @@ -251,8 +267,10 @@ func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) { COALESCE(jsonb_agg(s.*), '[]'::jsonb) FROM schedule_events s `, - userID, - nodeID, + userID, + nodeID, + start, + end, ) var j []byte err = res.Scan(&j) @@ -268,4 +286,4 @@ func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) { } return -}// }}} +} // }}} diff --git a/static/css/main.css b/static/css/main.css index bf68f18..2617798 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -802,6 +802,38 @@ header .menu { grid-gap: 8px; margin-top: 8px; } +#fullcalendar { + margin: 32px; + color: #444; +} +.folder .tabs { + border-left: 1px solid #888; + display: flex; +} +.folder .tabs .tab { + padding: 16px 32px; + border-top: 1px solid #888; + border-bottom: 1px solid #888; + border-right: 1px solid #888; + color: #444; + background: #eee; + cursor: pointer; +} +.folder .tabs .tab.selected { + border-bottom: none; + background: #fff; +} +.folder .tabs .hack { + border-bottom: 1px solid #888; + width: 100%; +} +.folder .content { + padding-top: 1px; + border-left: 1px solid #888; + border-right: 1px solid #888; + border-bottom: 1px solid #888; + padding-bottom: 1px; +} @media only screen and (max-width: 932px) { #app.node { grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "schedule" "files" "blank"; diff --git a/static/index.html b/static/index.html index ac87bce..5ed40a5 100644 --- a/static/index.html +++ b/static/index.html @@ -27,6 +27,7 @@ +
diff --git a/static/js/lib/fullcalendar.min.js b/static/js/lib/fullcalendar.min.js new file mode 100644 index 0000000..e96f044 --- /dev/null +++ b/static/js/lib/fullcalendar.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar Standard Bundle v6.1.11 +Docs & License: https://fullcalendar.io/docs/initialize-globals +(c) 2023 Adam Shaw +*/ +var FullCalendar=function(e){"use strict";var t,n,r,i,s,o,a,l,c,d={},u=[],h=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function f(e,t){for(var n in t)e[n]=t[n];return e}function g(e){var t=e.parentNode;t&&t.removeChild(e)}function p(e,n,r){var i,s,o,a={};for(o in n)"key"==o?i=n[o]:"ref"==o?s=n[o]:a[o]=n[o];if(arguments.length>2&&(a.children=arguments.length>3?t.call(arguments,2):r),"function"==typeof e&&null!=e.defaultProps)for(o in e.defaultProps)void 0===a[o]&&(a[o]=e.defaultProps[o]);return m(e,a,i,s,null)}function m(e,t,i,s,o){var a={type:e,props:t,key:i,ref:s,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++r:o};return null==o&&null!=n.vnode&&n.vnode(a),a}function v(){return{current:null}}function y(e){return e.children}function b(e,t,n){"-"===t[0]?e.setProperty(t,null==n?"":n):e[t]=null==n?"":"number"!=typeof n||h.test(t)?n:n+"px"}function E(e,t,n,r,i){var s;e:if("style"===t)if("string"==typeof n)e.style.cssText=n;else{if("string"==typeof r&&(e.style.cssText=r=""),r)for(t in r)n&&t in n||b(e.style,t,"");if(n)for(t in n)r&&n[t]===r[t]||b(e.style,t,n[t])}else if("o"===t[0]&&"n"===t[1])s=t!==(t=t.replace(/Capture$/,"")),t=t.toLowerCase()in e?t.toLowerCase().slice(2):t.slice(2),e.l||(e.l={}),e.l[t+s]=n,n?r||e.addEventListener(t,s?A:S,s):e.removeEventListener(t,s?A:S,s);else if("dangerouslySetInnerHTML"!==t){if(i)t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!==t&&"height"!==t&&"href"!==t&&"list"!==t&&"form"!==t&&"tabIndex"!==t&&"download"!==t&&t in e)try{e[t]=null==n?"":n;break e}catch(e){}"function"==typeof n||(null==n||!1===n&&-1==t.indexOf("-")?e.removeAttribute(t):e.setAttribute(t,n))}}function S(e){s=!0;try{return this.l[e.type+!1](n.event?n.event(e):e)}finally{s=!1}}function A(e){s=!0;try{return this.l[e.type+!0](n.event?n.event(e):e)}finally{s=!1}}function D(e,t){this.props=e,this.context=t}function w(e,t){if(null==t)return e.__?w(e.__,e.__.__k.indexOf(e)+1):null;for(var n;t