full calendar instead of bootsfaces calendar

This commit is contained in:
Jörg Henke
2021-03-08 23:34:34 +01:00
parent ed81dc40ad
commit 0629b6171d
13 changed files with 463 additions and 30 deletions

View File

@ -25,7 +25,7 @@ apply plugin: 'war'
apply plugin: 'eclipse'
group = 'jottyfan'
version = '1.1.5'
version = '1.1.6'
description = """timetrack"""

View File

@ -122,15 +122,6 @@ public class DoneBean implements Bean, Serializable, Comparable<DoneBean> {
return ldt;
}
public String getProjectNameWithBilling() {
StringBuilder buf = new StringBuilder();
buf.append(project == null ? "" : project.getName());
if (billing != null) {
buf.append(" (").append(billing.getShortcut()).append(")");
}
return buf.toString();
}
public String getProjectName() {
return project == null ? "" : project.getName();
}
@ -146,6 +137,14 @@ public class DoneBean implements Bean, Serializable, Comparable<DoneBean> {
public String getBillingName() {
return billing == null ? "" : billing.getName();
}
public String getBillingShortcut() {
return billing == null ? "" : billing.getShortcut();
}
public String getBillingCsskey() {
return billing == null ? "" : billing.getCsskey();
}
public String getTimeFromString() {
return timeFrom == null ? "" : timeFrom.format(hhmm);

View File

@ -33,7 +33,6 @@ import org.jooq.Record;
import org.jooq.Record3;
import org.jooq.Record4;
import org.jooq.Record5;
import org.jooq.Record6;
import org.jooq.Record7;
import org.jooq.SelectConditionStep;
import org.jooq.SelectJoinStep;
@ -48,8 +47,8 @@ import de.jottyfan.timetrack.db.done.tables.records.TJobRecord;
import de.jottyfan.timetrack.db.done.tables.records.TModuleRecord;
import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord;
import de.jottyfan.timetrack.modules.JooqGateway;
import net.bootsfaces.component.fullCalendar.FullCalendarEventBean;
import net.bootsfaces.component.fullCalendar.FullCalendarEventList;
import de.jottyfan.timetrack.modules.done.fullcalendar.ColorfulFCEventBean;
import de.jottyfan.timetrack.modules.done.fullcalendar.FCEventList;
/**
*
@ -386,14 +385,15 @@ public class DoneGateway extends JooqGateway {
*/
public String getAllCalendarEvents() throws DataAccessException, ClassNotFoundException, SQLException {
try (CloseableDSLContext jooq = getJooq()) {
SelectConditionStep<Record6<LocalDateTime, LocalDateTime, String, String, String, String>> sql = jooq
SelectConditionStep<Record7<LocalDateTime, LocalDateTime, String, String, String, String, String>> sql = jooq
// @formatter:off
.select(T_DONE.TIME_FROM,
T_DONE.TIME_UNTIL,
T_PROJECT.NAME,
T_MODULE.NAME,
T_JOB.NAME,
T_BILLING.CSSKEY)
T_BILLING.CSSKEY,
T_BILLING.SHORTCUT)
.from(T_DONE)
.leftJoin(T_PROJECT).on(T_PROJECT.PK.eq(T_DONE.FK_PROJECT))
.leftJoin(T_MODULE).on(T_MODULE.PK.eq(T_DONE.FK_MODULE))
@ -402,7 +402,7 @@ public class DoneGateway extends JooqGateway {
.where(T_DONE.FK_LOGIN.eq(getFkLogin()));
// @formatter:on
LOGGER.debug(sql.toString());
FullCalendarEventList list = new FullCalendarEventList();
FCEventList list = new FCEventList();
for (Record r : sql.fetch()) {
String projectName = r.get(T_PROJECT.NAME);
String moduleName = r.get(T_MODULE.NAME);
@ -410,6 +410,7 @@ public class DoneGateway extends JooqGateway {
LocalDateTime timeFrom = r.get(T_DONE.TIME_FROM);
LocalDateTime timeUntil = r.get(T_DONE.TIME_UNTIL);
String cssKey = r.get(T_BILLING.CSSKEY);
String shortcut = r.get(T_BILLING.SHORTCUT);
StringBuilder buf = new StringBuilder();
buf.append(projectName);
@ -418,14 +419,14 @@ public class DoneGateway extends JooqGateway {
buf.append(": ");
buf.append(jobName);
FullCalendarEventBean bean = new FullCalendarEventBean(buf.toString(),
java.util.Date.from(timeFrom.atZone(ZoneId.systemDefault()).toInstant())) {
private static final long serialVersionUID = 1L;
@Override
public void addExtendedFields(StringBuilder buf) {
}
};
Date theDate = Date.from(timeFrom.atZone(ZoneId.systemDefault()).toInstant());
ColorfulFCEventBean bean = new ColorfulFCEventBean(buf.toString(),
theDate, new RgbColor().determineTextcolorFromCssKey(cssKey));
bean.setProject(projectName);
bean.setModule(moduleName);
bean.setJob(jobName);
bean.setBilling(shortcut);
Date endDate = timeUntil == null ? null : Date.from(timeUntil.atZone(ZoneId.systemDefault()).toInstant());
bean.setEnd(endDate);
if (cssKey != null && !cssKey.isBlank()) {

View File

@ -58,4 +58,14 @@ public class RgbColor {
map.put("WP5", "rgb(255, 0, 0)");
return map.get(cssKey);
}
public String determineTextcolorFromCssKey(String cssKey) {
Map<String, String> map = new HashMap<>();
map.put("TA3", "rgb(0, 0, 0)");
map.put("WP2", "rgb(0, 0, 0)");
map.put("WP4", "rgb(0, 0, 0)");
map.put("WP5", "rgb(0, 0, 0)");
String value = map.get(cssKey);
return value == null ? "rgb(255, 255, 255)" : value;
}
}

View File

@ -0,0 +1,89 @@
package de.jottyfan.timetrack.modules.done.fullcalendar;
import java.util.Date;
/**
*
* @author henkej
*
*/
public class ColorfulFCEventBean extends FCEventBean {
private static final long serialVersionUID = 1L;
private final String textColor;
private String billing;
private String project;
private String module;
private String job;
public ColorfulFCEventBean(String title, Date start, String textColor) {
super(title, start);
this.textColor = textColor;
}
@Override
public String getExtendedFields() {
StringBuilder buf = new StringBuilder();
addParam(buf, "textColor", textColor);
addParam(buf, "billing", billing);
addParam(buf, "project", project);
addParam(buf, "module", module);
addParam(buf, "job", job);
return buf.toString();
}
/**
* @return the billing
*/
public String getBilling() {
return billing;
}
/**
* @param billing the billing to set
*/
public void setBilling(String billing) {
this.billing = billing;
}
/**
* @return the project
*/
public String getProject() {
return project;
}
/**
* @param project the project to set
*/
public void setProject(String project) {
this.project = project;
}
/**
* @return the module
*/
public String getModule() {
return module;
}
/**
* @param module the module to set
*/
public void setModule(String module) {
this.module = module;
}
/**
* @return the job
*/
public String getJob() {
return job;
}
/**
* @param job the job to set
*/
public void setJob(String job) {
this.job = job;
}
}

View File

@ -0,0 +1,198 @@
package de.jottyfan.timetrack.modules.done.fullcalendar;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
*
* @author henkej
*
*/
public abstract class FCEventBean implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
private Boolean allDay;
private Date start;
private Date end;
private Boolean editable;
private Boolean startEditable;
private Boolean durationEditable;
private String color;
private String url;
public FCEventBean(String title, Date start) {
super();
this.title = title;
this.start = start;
}
protected String toJavascriptDate(Date date){
if (date == null) {
return null;
}
StringBuilder buf = new StringBuilder("new Date(");
Calendar cal = new GregorianCalendar();
cal.setTime(date);
buf.append(cal.get(Calendar.YEAR)).append(",");
buf.append(cal.get(Calendar.MONTH)).append(",");
buf.append(cal.get(Calendar.DAY_OF_MONTH)).append(",");
buf.append(cal.get(Calendar.HOUR_OF_DAY)).append(",");
buf.append(cal.get(Calendar.MINUTE)).append(",");
buf.append(cal.get(Calendar.SECOND));
buf.append(")");
return buf.toString();
}
/**
* get extended fields to json string
*/
public abstract String getExtendedFields();
/**
* adds a param to buf if value is not null
*
* @param buf the buffer
* @param name the name
* @param value the value
* @param encapsulate if true, encapsulate value by '
* @param appendComma if true, append a comma
*/
public void addParam(StringBuilder buf, String name, String value, boolean encapsulate, boolean appendComma) {
if (value != null) {
buf.append(name).append(":");
buf.append(encapsulate ? "'" : "").append(value).append(encapsulate ? "'" : "");
buf.append(appendComma ? "," : "");
}
}
/**
* adds a param to buf if value is not null and append a comma
*
* @param buf the buffer
* @param name the name
* @param value the value
* @param encapsulate if true, encapsulate value by '
*/
public void addParam(StringBuilder buf, String name, String value, boolean encapsulate) {
addParam(buf, name, value, encapsulate, true);
}
/**
* adds a param to buf if value is not null and encapsulate it with ' and add a comma
*
* @param buf the buffer
* @param name the name
* @param value the value
*/
public void addParam(StringBuilder buf, String name, String value) {
addParam(buf, name, value, true, true);
}
/**
* adds a param to buf if value is not null and do not encapsulate it with ' but add a comma
*
* @param buf the buffer
* @param name the name
* @param value the value
*/
public void addParam(StringBuilder buf, String name, Boolean value) {
addParam(buf, name, value == null ? null : value.toString(), false, true);
}
public String toJson() {
StringBuilder buf = new StringBuilder("{");
addParam(buf, "title", title);
addParam(buf, "allDay", allDay);
addParam(buf, "editable", editable);
addParam(buf, "startEditable", startEditable);
addParam(buf, "durationEditable", durationEditable);
addParam(buf, "color", color);
addParam(buf, "end", toJavascriptDate(end), false);
addParam(buf, "url", url);
buf.append(getExtendedFields());
// this is the last element, we need no trailing ,
addParam(buf, "start", toJavascriptDate(start), false, false);
buf.append("}");
return buf.toString();
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Boolean getAllDay() {
return allDay;
}
public void setAllDay(Boolean allDay) {
this.allDay = allDay;
}
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
public Date getEnd() {
return end;
}
public void setEnd(Date end) {
this.end = end;
}
public Boolean getEditable() {
return editable;
}
public void setEditable(Boolean editable) {
this.editable = editable;
}
public Boolean getDurationEditable() {
return durationEditable;
}
public void setDurationEditable(Boolean durationEditable) {
this.durationEditable = durationEditable;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
public Boolean getStartEditable() {
return startEditable;
}
public void setStartEditable(Boolean startEditable) {
this.startEditable = startEditable;
}
}

View File

@ -0,0 +1,40 @@
package de.jottyfan.timetrack.modules.done.fullcalendar;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* @author henkej
*
*/
public class FCEventList implements Serializable {
private static final long serialVersionUID = 1L;
private List<FCEventBean> list;
public FCEventList() {
super();
this.list = new ArrayList<FCEventBean>();
}
public String toJson() {
StringBuilder buf = new StringBuilder("[");
Iterator<FCEventBean> iterator = list.iterator();
while (iterator.hasNext()) {
buf.append(iterator.next().toJson());
if (iterator.hasNext()) {
buf.append(",");
}
}
buf.append("]");
return buf.toString();
}
public List<FCEventBean> getList() {
return this.list;
}
}

View File

@ -24,7 +24,10 @@
<b:commandButton action="#{doneControl.toDelete(b)}" value="Entfernen" look="danger" iconAwesome="trash" />
</b:dataTableColumn>
<b:dataTableColumn label="" value="#{b.timeSummary}" contentStyleClass="doneoverviewtext" style="width: 128px !important" orderable="false" />
<b:dataTableColumn label="" value="#{b.projectNameWithBilling}" contentStyleClass="doneoverviewtextemph" style="width: 160px !important" orderable="false" />
<b:dataTableColumn label="" value="#{b.projectName}" contentStyleClass="doneoverviewtextemph" style="width: 160px !important" orderable="false" />
<b:dataTableColumn label="" orderable="false">
<b:badge value="#{b.billingShortcut}" styleClass="#{b.billingCsskey}" />
</b:dataTableColumn>
<b:dataTableColumn label="" value="#{b.timeDiff}" contentStyleClass="doneoverviewtextemph" style="width: 64px !important" orderable="false" />
<b:dataTableColumn label="" style="width: 100px !important" orderable="false">
<b:commandButton action="#{doneControl.toEdit(b)}" value="Editieren" look="warning" iconAwesome="pencil" />
@ -87,10 +90,8 @@
<b:inputTextarea value="#{doneModel.attach}" readonly="true" />
</p>
</b:tab>
<b:tab title="Kalender">
<b:fullCalendar editable="false" events="#{doneModel.calendarEvents}" height="600"
defaultDate="#{doneModel.dayIso8601}" defaultView="agendaDay" hidden="xs" scrollTime="08:00:00"
slotDuration="00:15:00" span="half" allDaySlot="false" lang="de" />
<b:tab title="Kalender" contentStyle="height: 75vh">
<my:fullcalendar value="#{doneModel.calendarEvents}" defaultDate="#{doneModel.dayIso8601}" />
</b:tab>
<b:tab title="Projekt">
<ui:repeat value="#{doneModel.projects}" var="col">

View File

@ -92,4 +92,8 @@
.version {
font-size: small;
color: silver;
}
}
.fc-content {
cursor: pointer;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,83 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:cc="http://xmlns.jcp.org/jsf/composite"
xmlns:b="http://bootsfaces.net/ui">
<cc:interface>
<cc:attribute name="value" required="true" default="" />
<cc:attribute name="defaultDate" required="true" />
</cc:interface>
<cc:implementation>
<h:outputStylesheet name="jslibs/fullcalendar/main.min.css" />
<h:outputScript library="bsf" name="jq/jquery.js" target="head" />
<h:outputScript library="jslibs" name="fullcalendar/main.min.js" target="head" />
<h:outputScript library="jslibs" name="fullcalendar/locales-all.min.js" target="head" />
<script type="text/javascript">
function lz(x) {
if (x &lt; 10){
return "0" + x;
} else {
return x;
}
}
$(document)
.ready(
function() {
var calendarEl = document
.getElementById('calendar');
var calendar = new FullCalendar.Calendar(
calendarEl,
{
initialView : 'timeGridDay',
initialDate : "#{cc.attrs.defaultDate}",
headerToolbar : {
left : 'prev,next today',
center : 'title',
right : 'dayGridMonth,timeGridWeek,timeGridDay'
},
height : "75vh",
locale : 'de',
events : <h:outputText value="#{cc.attrs.value}" escape="FALSE" />,
eventClick : function(info) {
from = info.event.start;
until = info.event.end;
fromS = from == null ? "?" : lz(from.getHours()) + ":" + lz(from.getMinutes());
untilS = until == null ? "?" : lz(until.getHours()) + ":" + lz(until.getMinutes());
$("#modaltitle").html(fromS + " - " + untilS);
$("#modalsummary").html(info.event.title);
$("#modalproject").html(info.event.project);
$("#modalmodule").html(info.event.module);
$("#modaljob").html(info.event.job);
$("#modalbilling").html(info.event.billing);
$("#calmodal").show();
},
firstDay : 0
});
calendar.setOption('locale', 'de');
calendar.render();
});
$(document).ready(function() {
var calendarEl = document.getElementById('calendar');
});
</script>
<div id="calendar"></div>
<!-- div id="calmodal" class="modal" fade" tabindex="-1" role="dialog" -->
<div id="calmodal" class="modal" tabindex="-1" role="dialog" style="background: rgba(0, 0, 0, 0.5) !important;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="$('#calmodal').hide()">
<span aria-hidden="true" class="fa fa-times"></span>
</button>
<h4 id="modaltitle" class="modal-title"></h4>
</div>
<div class="modal-body">
<h1 id="modalsummary"></h1>
<h2 id="modalproject"></h2>
<h3 id="modalmodule"></h3>
<h4 id="modaljob"></h4>
<div id="modalbilling"></div>
</div>
</div>
</div>
</div>
</cc:implementation>
</ui:composition>