show work times
This commit is contained in:
		| @@ -1,20 +1,239 @@ | ||||
| package de.jottyfan.timetrack.spring.done; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.Duration; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.ZoneId; | ||||
| import java.time.format.DateTimeFormatter; | ||||
| import java.util.Date; | ||||
| import java.util.Map; | ||||
|  | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TBillingRecord; | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TDoneRecord; | ||||
| 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; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author henkej | ||||
|  * | ||||
|  */ | ||||
| public class DoneBean implements Serializable, Comparable<DoneBean>{ | ||||
|  | ||||
| public class DoneBean implements Serializable, Comparable<DoneBean> { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
|  | ||||
| 	@Override | ||||
| 	public int compareTo(DoneBean bean) { | ||||
| 		// TODO: implement | ||||
| 		return 0; | ||||
| 	private static final DateTimeFormatter hhmm = DateTimeFormatter.ofPattern("HH:mm"); | ||||
|  | ||||
| 	private Integer pk; | ||||
| 	private LocalDateTime timeFrom; | ||||
| 	private LocalDateTime timeUntil; | ||||
| 	private TProjectRecord project; | ||||
| 	private TModuleRecord module; | ||||
| 	private TJobRecord activity; | ||||
| 	private TBillingRecord billing; | ||||
|  | ||||
| 	public DoneBean() { | ||||
| 	} | ||||
|  | ||||
| 	public DoneBean(TDoneRecord r, Map<Integer, TProjectRecord> projectMap, Map<Integer, TModuleRecord> moduleMap, | ||||
| 			Map<Integer, TJobRecord> jobMap, Map<Integer, TBillingRecord> billingMap) { | ||||
| 		this.pk = r.getPk(); | ||||
| 		this.timeFrom = r.getTimeFrom(); | ||||
| 		this.timeUntil = r.getTimeUntil(); | ||||
| 		this.project = projectMap.get(r.getFkProject()); | ||||
| 		this.module = moduleMap.get(r.getFkModule()); | ||||
| 		this.activity = jobMap.get(r.getFkJob()); | ||||
| 		this.billing = billingMap.get(r.getFkBilling()); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public final String toString() { | ||||
| 		StringBuilder buf = new StringBuilder("DoneBean{"); | ||||
| 		buf.append("pk=").append(pk); | ||||
| 		buf.append(",timeFrom=").append(timeFrom == null ? "" : timeFrom.format(DateTimeFormatter.ISO_DATE_TIME)); | ||||
| 		buf.append(",timeUntil=").append(timeUntil == null ? "" : timeUntil.format(DateTimeFormatter.ISO_DATE_TIME)); | ||||
| 		buf.append(",project=").append(project == null ? "" : project.getName()); | ||||
| 		buf.append(",module=").append(module == null ? "" : module.getName()); | ||||
| 		buf.append(",activity=").append(activity == null ? "" : activity.getName()); | ||||
| 		buf.append(",billing=").append(billing == null ? "" : billing.getName()); | ||||
| 		buf.append("}"); | ||||
| 		return buf.toString(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * set the day of timeFrom and timeUntil, keeping the times | ||||
| 	 *  | ||||
| 	 * @param day the day | ||||
| 	 */ | ||||
| 	public void setDay(Date day) { | ||||
| 		if (timeFrom != null) { | ||||
| 			LocalDateTime ldt = timeFrom; | ||||
| 			LocalDate date = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); | ||||
| 			ldt = ldt.withYear(date.getYear()).withMonth(date.getMonthValue()).withDayOfMonth(date.getDayOfMonth()); | ||||
| 			timeFrom = ldt; | ||||
| 		} | ||||
| 		if (timeUntil != null) { | ||||
| 			LocalDateTime ldt = timeUntil; | ||||
| 			LocalDate date = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); | ||||
| 			ldt = ldt.withYear(date.getYear()).withMonth(date.getMonthValue()).withDayOfMonth(date.getDayOfMonth()); | ||||
| 			timeUntil = ldt; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public String getTimeSummary() { | ||||
| 		StringBuilder buf = new StringBuilder(); | ||||
| 		if (timeFrom != null) { | ||||
| 			buf.append(timeFrom.format(hhmm)); | ||||
| 		} | ||||
| 		if (timeUntil != null) { | ||||
| 			buf.append(" - "); | ||||
| 			buf.append(timeUntil.format(hhmm)); | ||||
| 		} | ||||
| 		return buf.toString(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public int compareTo(DoneBean o) { | ||||
| 		return o == null || timeFrom == null || o.getTimeFrom() == null ? 0 : timeFrom.compareTo(o.getTimeFrom()); | ||||
| 	} | ||||
|  | ||||
| 	public String getTimeDiff() { | ||||
| 		LocalDateTime earlier = timeFrom != null ? timeFrom : LocalDateTime.now(); | ||||
| 		LocalDateTime later = timeUntil != null ? timeUntil : LocalDateTime.now(); | ||||
| 		Duration diff = Duration.between(earlier, later); | ||||
| 		return String.format("%02d:%02d", diff.toHours(), diff.toMinutes() % 60); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get local date time from s | ||||
| 	 *  | ||||
| 	 * @param s   the HH:mm formatted values | ||||
| 	 * @param ldt the date as basic for that datetime, for today one can use | ||||
| 	 *            LocalDateTime.now(); in fact this is set if ldt is null internally | ||||
| 	 * @return the generated datetime | ||||
| 	 */ | ||||
| 	public LocalDateTime getLocalDateTimeFromHHmm(String s, LocalDateTime ldt) { | ||||
| 		if (s == null || s.trim().isEmpty()) { | ||||
| 			return null; | ||||
| 		} else if (ldt == null) { | ||||
| 			ldt = LocalDateTime.now(); | ||||
| 		} | ||||
| 		String[] hm = s.split(":"); | ||||
| 		Integer hours = 0; | ||||
| 		Integer minutes = 0; | ||||
| 		if (hm.length > 0) { | ||||
| 			hours = Integer.valueOf(hm[0]); | ||||
| 		} | ||||
| 		if (hm.length > 1) { | ||||
| 			minutes = Integer.valueOf(hm[1]); | ||||
| 		} | ||||
| 		ldt = ldt.withHour(hours); | ||||
| 		ldt = ldt.withMinute(minutes); | ||||
| 		ldt = ldt.withSecond(0); | ||||
| 		ldt = ldt.withNano(0); | ||||
| 		return ldt; | ||||
| 	} | ||||
|  | ||||
| 	public String getProjectName() { | ||||
| 		return project == null ? "" : project.getName(); | ||||
| 	} | ||||
|  | ||||
| 	public String getModuleName() { | ||||
| 		return module == null ? "" : module.getName(); | ||||
| 	} | ||||
|  | ||||
| 	public String getJobName() { | ||||
| 		return activity == null ? "" : activity.getName(); | ||||
| 	} | ||||
| 	 | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeFromString(String s) { | ||||
| 		this.timeFrom = getLocalDateTimeFromHHmm(s, null); // use setDay instead | ||||
| 	} | ||||
|  | ||||
| 	public String getTimeUntilString() { | ||||
| 		return timeUntil == null ? "" : timeUntil.format(hhmm); | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeUntilString(String s) { | ||||
| 		this.timeUntil = getLocalDateTimeFromHHmm(s, null); // use setDay instead | ||||
| 	} | ||||
|  | ||||
| 	public Integer getPk() { | ||||
| 		return this.pk; | ||||
| 	} | ||||
|  | ||||
| 	public void setPk(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
|  | ||||
| 	public LocalDateTime getTimeFrom() { | ||||
| 		return timeFrom; | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeFrom(LocalDateTime timeFrom) { | ||||
| 		this.timeFrom = timeFrom; | ||||
| 	} | ||||
|  | ||||
| 	public LocalDateTime getTimeUntil() { | ||||
| 		return timeUntil; | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeUntil(LocalDateTime timeUntil) { | ||||
| 		this.timeUntil = timeUntil; | ||||
| 	} | ||||
|  | ||||
| 	public TProjectRecord getProject() { | ||||
| 		return project; | ||||
| 	} | ||||
|  | ||||
| 	public void setProject(TProjectRecord project) { | ||||
| 		this.project = project; | ||||
| 	} | ||||
|  | ||||
| 	public TModuleRecord getModule() { | ||||
| 		return module; | ||||
| 	} | ||||
|  | ||||
| 	public void setModule(TModuleRecord module) { | ||||
| 		this.module = module; | ||||
| 	} | ||||
|  | ||||
| 	public TJobRecord getActivity() { | ||||
| 		return activity; | ||||
| 	} | ||||
|  | ||||
| 	public void setActivity(TJobRecord activity) { | ||||
| 		this.activity = activity; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the billing | ||||
| 	 */ | ||||
| 	public TBillingRecord getBilling() { | ||||
| 		return billing; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param billing the billing to set | ||||
| 	 */ | ||||
| 	public void setBilling(TBillingRecord billing) { | ||||
| 		this.billing = billing; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.ui.Model; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.ModelAttribute; | ||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.RequestMethod; | ||||
| @@ -36,9 +37,15 @@ public class DoneController { | ||||
|  | ||||
| 	@RolesAllowed("timetrack_user") | ||||
| 	@RequestMapping(value = "/done/list") | ||||
| 	public String getList(Model model) { | ||||
| 		List<DoneBean> list = doneService.getList(); | ||||
| 	public String getList(@ModelAttribute DoneModel doneModel, Model model) { | ||||
| 		String username = doneService.getCurrentUser(request); | ||||
| 		List<DoneBean> list = doneService.getList(doneModel.getDay(), username); | ||||
| 		model.addAttribute("doneList", list); | ||||
| 		model.addAttribute("doneModel", doneModel); | ||||
| 		model.addAttribute("projectList", doneService.getProjects()); | ||||
| 		model.addAttribute("moduleList", doneService.getModules()); | ||||
| 		model.addAttribute("jobList", doneService.getJobs()); | ||||
| 		model.addAttribute("billingList", doneService.getBillings()); | ||||
| 		return "done/list"; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,37 @@ | ||||
| package de.jottyfan.timetrack.spring.done; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDate; | ||||
|  | ||||
| import org.springframework.format.annotation.DateTimeFormat; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author henkej | ||||
|  * | ||||
|  */ | ||||
| public class DoneModel implements Serializable { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
| 	 | ||||
| 	@DateTimeFormat(pattern = "yyyy-MM-dd") | ||||
| 	private LocalDate day; | ||||
| 	 | ||||
| 	public DoneModel() { | ||||
| 		this.day = LocalDate.now(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the day | ||||
| 	 */ | ||||
|  | ||||
| 	public LocalDate getDay() { | ||||
| 		return day; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param day the day to set | ||||
| 	 */ | ||||
| 	public void setDay(LocalDate day) { | ||||
| 		this.day = day; | ||||
| 	} | ||||
| } | ||||
| @@ -1,18 +1,32 @@ | ||||
| package de.jottyfan.timetrack.spring.done; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TBillingRecord; | ||||
| 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; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author henkej | ||||
|  * | ||||
|  */ | ||||
| public interface IDoneService { | ||||
| 	public List<DoneBean> getList(); | ||||
| 	public List<DoneBean> getList(LocalDate day, String username); | ||||
|  | ||||
| 	public DoneBean getBean(Integer id); | ||||
|  | ||||
| 	public String getCurrentUser(HttpServletRequest request); | ||||
|  | ||||
| 	public List<TProjectRecord> getProjects(); | ||||
|  | ||||
| 	public List<TModuleRecord> getModules(); | ||||
|  | ||||
| 	public List<TJobRecord> getJobs(); | ||||
|  | ||||
| 	public List<TBillingRecord> getBillings(); | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,251 @@ | ||||
| package de.jottyfan.timetrack.spring.done.impl; | ||||
|  | ||||
| import static de.jottyfan.timetrack.db.done.Tables.T_BILLING; | ||||
| import static de.jottyfan.timetrack.db.done.Tables.T_DONE; | ||||
| import static de.jottyfan.timetrack.db.done.Tables.T_JOB; | ||||
| import static de.jottyfan.timetrack.db.done.Tables.T_MODULE; | ||||
| import static de.jottyfan.timetrack.db.done.Tables.T_PROJECT; | ||||
| import static de.jottyfan.timetrack.db.profile.Tables.T_LOGIN; | ||||
|  | ||||
| import java.sql.SQLException; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.jooq.DSLContext; | ||||
| import org.jooq.DeleteConditionStep; | ||||
| import org.jooq.Record7; | ||||
| import org.jooq.Result; | ||||
| import org.jooq.SelectConditionStep; | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
|  | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TBillingRecord; | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TDoneRecord; | ||||
| 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.db.profile.tables.records.TLoginRecord; | ||||
| import de.jottyfan.timetrack.spring.done.DoneBean; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Repository | ||||
| public class DoneGateway { | ||||
| 	private static final Logger LOGGER = LogManager.getLogger(DoneGateway.class); | ||||
| 	private final DSLContext jooq; | ||||
|  | ||||
| 	public DoneGateway(@Autowired DSLContext jooq) throws Exception { | ||||
| 		this.jooq = jooq; | ||||
| 	} | ||||
|  | ||||
| 	public DSLContext getJooq() { | ||||
| 		return this.jooq; | ||||
| 	} | ||||
| 	 | ||||
| 	/** | ||||
| 	 * get the user id of the user with username | ||||
| 	 *  | ||||
| 	 * @param username the name of the user | ||||
| 	 * @return the ID of the user; if not found, null | ||||
| 	 */ | ||||
| 	public Integer getUserId(String username) { | ||||
| 		Result<TLoginRecord> r = getJooq().selectFrom(T_LOGIN).where(T_LOGIN.LOGIN.eq(username)).fetch(); | ||||
| 		return r == null || r.size() < 1 ? null : r.get(0).getPk(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all projects from the database | ||||
| 	 *  | ||||
| 	 * @return a list of found projects | ||||
| 	 *  | ||||
| 	 * @throws DataAccessException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws SQLException | ||||
| 	 */ | ||||
| 	public List<TProjectRecord> getAllProjects() throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		return getJooq().selectFrom(T_PROJECT).fetchInto(TProjectRecord.class); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all modules from the database | ||||
| 	 *  | ||||
| 	 * @return a list of found modules | ||||
| 	 *  | ||||
| 	 * @throws DataAccessException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws SQLException | ||||
| 	 */ | ||||
| 	public List<TModuleRecord> getAllModules() throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		return getJooq().selectFrom(T_MODULE).fetchInto(TModuleRecord.class); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all jobs from the database | ||||
| 	 *  | ||||
| 	 * @return a list of found jobs | ||||
| 	 *  | ||||
| 	 * @throws DataAccessException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws SQLException | ||||
| 	 */ | ||||
| 	public List<TJobRecord> getAllJobs() throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		return getJooq().selectFrom(T_JOB).fetchInto(TJobRecord.class); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all billings from the database | ||||
| 	 *  | ||||
| 	 * @return a list of found billings | ||||
| 	 *  | ||||
| 	 * @throws DataAccessException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws SQLException | ||||
| 	 */ | ||||
| 	public List<TBillingRecord> getAllBillings() throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		return getJooq().selectFrom(T_BILLING).fetchInto(TBillingRecord.class); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a project map from the database | ||||
| 	 *  | ||||
| 	 * @return the project map | ||||
| 	 * @throws SQLException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	private Map<Integer, TProjectRecord> getProjectMap() | ||||
| 			throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		Map<Integer, TProjectRecord> map = new HashMap<>(); | ||||
| 		for (TProjectRecord r : getAllProjects()) { | ||||
| 			map.put(r.getPk(), r); | ||||
| 		} | ||||
| 		return map; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a module map from the database | ||||
| 	 *  | ||||
| 	 * @return the module map | ||||
| 	 * @throws SQLException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	private Map<Integer, TModuleRecord> getModuleMap() throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		Map<Integer, TModuleRecord> map = new HashMap<>(); | ||||
| 		for (TModuleRecord r : getAllModules()) { | ||||
| 			map.put(r.getPk(), r); | ||||
| 		} | ||||
| 		return map; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a job map from the database | ||||
| 	 *  | ||||
| 	 * @return the job map | ||||
| 	 * @throws SQLException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	private Map<Integer, TJobRecord> getJobMap() throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		Map<Integer, TJobRecord> map = new HashMap<>(); | ||||
| 		for (TJobRecord r : getAllJobs()) { | ||||
| 			map.put(r.getPk(), r); | ||||
| 		} | ||||
| 		return map; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a billing map from the database | ||||
| 	 *  | ||||
| 	 * @return the billing map | ||||
| 	 * @throws SQLException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	private Map<Integer, TBillingRecord> getBillingMap() | ||||
| 			throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		Map<Integer, TBillingRecord> map = new HashMap<>(); | ||||
| 		for (TBillingRecord r : getAllBillings()) { | ||||
| 			map.put(r.getPk(), r); | ||||
| 		} | ||||
| 		return map; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get list of entries of day | ||||
| 	 *  | ||||
| 	 * @param day the day | ||||
| 	 *  | ||||
| 	 * @return a list (an empty one at least) | ||||
| 	 * @throws SQLException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	public List<DoneBean> getAllOfDay(LocalDate day, Integer userId) | ||||
| 			throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		LocalDateTime dayStart = day.atStartOfDay(); | ||||
| 		LocalDateTime dayEnd = day.atTime(23, 59, 59); | ||||
| 		SelectConditionStep<Record7<Integer, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer>> sql = getJooq() | ||||
| 		// @formatter:off | ||||
| 			.select(T_DONE.PK, | ||||
| 							T_DONE.TIME_FROM, | ||||
| 							T_DONE.TIME_UNTIL, | ||||
| 							T_DONE.FK_PROJECT, | ||||
| 							T_DONE.FK_MODULE, | ||||
| 							T_DONE.FK_JOB, | ||||
| 							T_DONE.FK_BILLING) | ||||
| 			.from(T_DONE) | ||||
| 			.where(T_DONE.TIME_FROM.between(dayStart, dayEnd)) | ||||
| 			.and(T_DONE.TIME_UNTIL.between(dayStart, dayEnd)) | ||||
| 			.and(T_DONE.FK_LOGIN.eq(userId == null ? -999999 : userId)); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug("{}", sql.toString()); | ||||
| 		List<DoneBean> list = new ArrayList<>(); | ||||
| 		Map<Integer, TProjectRecord> projectMap = getProjectMap(); | ||||
| 		Map<Integer, TModuleRecord> moduleMap = getModuleMap(); | ||||
| 		Map<Integer, TJobRecord> jobMap = getJobMap(); | ||||
| 		Map<Integer, TBillingRecord> billingMap = getBillingMap(); | ||||
| 		for (Record7<Integer, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer> r : sql.fetch()) { | ||||
| 			DoneBean bean = new DoneBean(); | ||||
| 			bean.setPk(r.get(T_DONE.PK)); | ||||
| 			bean.setTimeFrom(r.get(T_DONE.TIME_FROM)); | ||||
| 			bean.setTimeUntil(r.get(T_DONE.TIME_UNTIL)); | ||||
| 			bean.setProject(projectMap.get(r.get(T_DONE.FK_PROJECT))); | ||||
| 			bean.setModule(moduleMap.get(r.get(T_DONE.FK_MODULE))); | ||||
| 			bean.setActivity(jobMap.get(r.get(T_DONE.FK_JOB))); | ||||
| 			bean.setBilling(billingMap.get(r.get(T_DONE.FK_BILLING))); | ||||
| 			list.add(bean); | ||||
| 		} | ||||
| 		list.sort((o1, o2) -> o1 == null ? 0 : o1.compareTo(o2)); | ||||
| 		return list; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * delete an entry from the database | ||||
| 	 *  | ||||
| 	 * @param pk the id of the entry | ||||
| 	 * @return the number of affected database rows, should be 1 | ||||
| 	 * @throws SQLException | ||||
| 	 * @throws ClassNotFoundException | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	public Integer delete(Integer pk) throws DataAccessException, ClassNotFoundException, SQLException { | ||||
| 		DeleteConditionStep<TDoneRecord> sql = getJooq() | ||||
| 		// @formatter:off | ||||
| 			.deleteFrom(T_DONE) | ||||
| 			.where(T_DONE.PK.eq(pk)); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug("{}", sql.toString()); | ||||
| 		return sql.execute(); | ||||
| 	} | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| package de.jottyfan.timetrack.spring.done.impl; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| @@ -13,10 +14,12 @@ import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TBillingRecord; | ||||
| 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.spring.done.DoneBean; | ||||
| import de.jottyfan.timetrack.spring.done.IDoneService; | ||||
| import de.jottyfan.timetrack.spring.note.NoteBean; | ||||
| import de.jottyfan.timetrack.spring.note.impl.NoteGateway; | ||||
| import de.jottyfan.timetrack.spring.note.impl.NoteService; | ||||
|  | ||||
| /** | ||||
| @@ -31,21 +34,27 @@ public class DoneService implements IDoneService { | ||||
|  | ||||
| 	@Autowired | ||||
| 	private DSLContext dsl; | ||||
| 	 | ||||
|  | ||||
| 	@Override | ||||
|   public String getCurrentUser(HttpServletRequest request) { | ||||
| 		KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); | ||||
| 	public String getCurrentUser(HttpServletRequest request) { | ||||
| 		KeycloakSecurityContext ksc = (KeycloakSecurityContext) request | ||||
| 				.getAttribute(KeycloakSecurityContext.class.getName()); | ||||
| 		return ksc == null ? "" : ksc.getIdToken().getPreferredUsername(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<DoneBean> getList() { | ||||
| //		try { | ||||
| //			return new DoneGateway(dsl).getAll(); | ||||
| //		} catch (Exception e) { | ||||
| //			LOGGER.error(e); | ||||
| 	public List<DoneBean> getList(LocalDate day, String username) { | ||||
| 		try { | ||||
| 			DoneGateway gw = new DoneGateway(dsl); | ||||
| 			Integer userId = gw.getUserId(username); | ||||
| 			if (userId == null) { | ||||
| 				LOGGER.warn("userId of user {} is null", username); | ||||
| 			} | ||||
| 			return gw.getAllOfDay(day, userId); | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.error(e); | ||||
| 			return new ArrayList<>(); | ||||
| //		} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -54,4 +63,44 @@ public class DoneService implements IDoneService { | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<TProjectRecord> getProjects()	{ | ||||
| 		try { | ||||
| 			return new DoneGateway(dsl).getAllProjects(); | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.error(e); | ||||
| 			return new ArrayList<>(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<TModuleRecord> getModules() { | ||||
| 		try { | ||||
| 			return new DoneGateway(dsl).getAllModules(); | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.error(e); | ||||
| 			return new ArrayList<>(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<TJobRecord> getJobs() { | ||||
| 		try { | ||||
| 			return new DoneGateway(dsl).getAllJobs(); | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.error(e); | ||||
| 			return new ArrayList<>(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<TBillingRecord> getBillings() { | ||||
| 		try { | ||||
| 			return new DoneGateway(dsl).getAllBillings(); | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.error(e); | ||||
| 			return new ArrayList<>(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,226 +0,0 @@ | ||||
| package de.jottyfan.timetrack.modules.done; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.Duration; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.ZoneId; | ||||
| import java.time.format.DateTimeFormatter; | ||||
| import java.util.Date; | ||||
| import java.util.Map; | ||||
|  | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TBillingRecord; | ||||
| import de.jottyfan.timetrack.db.done.tables.records.TDoneRecord; | ||||
| 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.Bean; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author henkej | ||||
|  * | ||||
|  */ | ||||
| public class DoneBean implements Bean, Serializable, Comparable<DoneBean> { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
|  | ||||
| 	private static final DateTimeFormatter hhmm = DateTimeFormatter.ofPattern("HH:mm"); | ||||
|  | ||||
| 	private Integer pk; | ||||
| 	private LocalDateTime timeFrom; | ||||
| 	private LocalDateTime timeUntil; | ||||
| 	private TProjectRecord project; | ||||
| 	private TModuleRecord module; | ||||
| 	private TJobRecord activity; | ||||
| 	private TBillingRecord billing; | ||||
|  | ||||
| 	public DoneBean() { | ||||
| 	} | ||||
|  | ||||
| 	public DoneBean(TDoneRecord r, Map<Integer, TProjectRecord> projectMap, Map<Integer, TModuleRecord> moduleMap, | ||||
| 			Map<Integer, TJobRecord> jobMap, Map<Integer, TBillingRecord> billingMap) { | ||||
| 		this.pk = r.getPk(); | ||||
| 		this.timeFrom = r.getTimeFrom(); | ||||
| 		this.timeUntil = r.getTimeUntil(); | ||||
| 		this.project = projectMap.get(r.getFkProject()); | ||||
| 		this.module = moduleMap.get(r.getFkModule()); | ||||
| 		this.activity = jobMap.get(r.getFkJob()); | ||||
| 		this.billing = billingMap.get(r.getFkBilling()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * set the day of timeFrom and timeUntil, keeping the times | ||||
| 	 *  | ||||
| 	 * @param day the day | ||||
| 	 */ | ||||
| 	public void setDay(Date day) { | ||||
| 		if (timeFrom != null) { | ||||
| 			LocalDateTime ldt = timeFrom; | ||||
| 			LocalDate date = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); | ||||
| 			ldt = ldt.withYear(date.getYear()).withMonth(date.getMonthValue()).withDayOfMonth(date.getDayOfMonth()); | ||||
| 			timeFrom = ldt; | ||||
| 		} | ||||
| 		if (timeUntil != null) { | ||||
| 			LocalDateTime ldt = timeUntil; | ||||
| 			LocalDate date = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); | ||||
| 			ldt = ldt.withYear(date.getYear()).withMonth(date.getMonthValue()).withDayOfMonth(date.getDayOfMonth()); | ||||
| 			timeUntil = ldt; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public String getTimeSummary() { | ||||
| 		StringBuilder buf = new StringBuilder(); | ||||
| 		if (timeFrom != null) { | ||||
| 			buf.append(timeFrom.format(hhmm)); | ||||
| 		} | ||||
| 		if (timeUntil != null) { | ||||
| 			buf.append(" - "); | ||||
| 			buf.append(timeUntil.format(hhmm)); | ||||
| 		} | ||||
| 		return buf.toString(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public int compareTo(DoneBean o) { | ||||
| 		return o == null || timeFrom == null || o.getTimeFrom() == null ? 0 : timeFrom.compareTo(o.getTimeFrom()); | ||||
| 	} | ||||
|  | ||||
| 	public String getTimeDiff() { | ||||
| 		LocalDateTime earlier = timeFrom != null ? timeFrom : LocalDateTime.now(); | ||||
| 		LocalDateTime later = timeUntil != null ? timeUntil : LocalDateTime.now(); | ||||
| 		Duration diff = Duration.between(earlier, later); | ||||
| 		return String.format("%02d:%02d", diff.toHours(), diff.toMinutes() % 60); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get local date time from s | ||||
| 	 *  | ||||
| 	 * @param s   the HH:mm formatted values | ||||
| 	 * @param ldt the date as basic for that datetime, for today one can use | ||||
| 	 *            LocalDateTime.now(); in fact this is set if ldt is null internally | ||||
| 	 * @return the generated datetime | ||||
| 	 */ | ||||
| 	public LocalDateTime getLocalDateTimeFromHHmm(String s, LocalDateTime ldt) { | ||||
| 		if (s == null || s.trim().isEmpty()) { | ||||
| 			return null; | ||||
| 		} else if (ldt == null) { | ||||
| 			ldt = LocalDateTime.now(); | ||||
| 		} | ||||
| 		String[] hm = s.split(":"); | ||||
| 		Integer hours = 0; | ||||
| 		Integer minutes = 0; | ||||
| 		if (hm.length > 0) { | ||||
| 			hours = Integer.valueOf(hm[0]); | ||||
| 		} | ||||
| 		if (hm.length > 1) { | ||||
| 			minutes = Integer.valueOf(hm[1]); | ||||
| 		} | ||||
| 		ldt = ldt.withHour(hours); | ||||
| 		ldt = ldt.withMinute(minutes); | ||||
| 		ldt = ldt.withSecond(0); | ||||
| 		ldt = ldt.withNano(0); | ||||
| 		return ldt; | ||||
| 	} | ||||
|  | ||||
| 	public String getProjectName() { | ||||
| 		return project == null ? "" : project.getName(); | ||||
| 	} | ||||
|  | ||||
| 	public String getModuleName() { | ||||
| 		return module == null ? "" : module.getName(); | ||||
| 	} | ||||
|  | ||||
| 	public String getJobName() { | ||||
| 		return activity == null ? "" : activity.getName(); | ||||
| 	} | ||||
| 	 | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeFromString(String s) { | ||||
| 		this.timeFrom = getLocalDateTimeFromHHmm(s, null); // use setDay instead | ||||
| 	} | ||||
|  | ||||
| 	public String getTimeUntilString() { | ||||
| 		return timeUntil == null ? "" : timeUntil.format(hhmm); | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeUntilString(String s) { | ||||
| 		this.timeUntil = getLocalDateTimeFromHHmm(s, null); // use setDay instead | ||||
| 	} | ||||
|  | ||||
| 	public Integer getPk() { | ||||
| 		return this.pk; | ||||
| 	} | ||||
|  | ||||
| 	public void setPk(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
|  | ||||
| 	public LocalDateTime getTimeFrom() { | ||||
| 		return timeFrom; | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeFrom(LocalDateTime timeFrom) { | ||||
| 		this.timeFrom = timeFrom; | ||||
| 	} | ||||
|  | ||||
| 	public LocalDateTime getTimeUntil() { | ||||
| 		return timeUntil; | ||||
| 	} | ||||
|  | ||||
| 	public void setTimeUntil(LocalDateTime timeUntil) { | ||||
| 		this.timeUntil = timeUntil; | ||||
| 	} | ||||
|  | ||||
| 	public TProjectRecord getProject() { | ||||
| 		return project; | ||||
| 	} | ||||
|  | ||||
| 	public void setProject(TProjectRecord project) { | ||||
| 		this.project = project; | ||||
| 	} | ||||
|  | ||||
| 	public TModuleRecord getModule() { | ||||
| 		return module; | ||||
| 	} | ||||
|  | ||||
| 	public void setModule(TModuleRecord module) { | ||||
| 		this.module = module; | ||||
| 	} | ||||
|  | ||||
| 	public TJobRecord getActivity() { | ||||
| 		return activity; | ||||
| 	} | ||||
|  | ||||
| 	public void setActivity(TJobRecord activity) { | ||||
| 		this.activity = activity; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the billing | ||||
| 	 */ | ||||
| 	public TBillingRecord getBilling() { | ||||
| 		return billing; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param billing the billing to set | ||||
| 	 */ | ||||
| 	public void setBilling(TBillingRecord billing) { | ||||
| 		this.billing = billing; | ||||
| 	} | ||||
| } | ||||
| @@ -28,7 +28,6 @@ body { | ||||
| .tabdivblurred { | ||||
| 	padding: 8px; | ||||
| 	padding-bottom: 0px; | ||||
| 	background-color: rgba(255, 255, 255, 0.5); | ||||
| 	height: calc(100% - 42px); | ||||
| } | ||||
|  | ||||
| @@ -109,32 +108,31 @@ body { | ||||
| 		!important; | ||||
| } | ||||
|  | ||||
| .WP2 { | ||||
| .billing { | ||||
| 	color: black !important; | ||||
| 	background: radial-gradient(#ffff00, #ffe169) !important; | ||||
| 	border: 1px solid darkgray; | ||||
|   padding-top: 8px !important; | ||||
|   border-radius: 16px; | ||||
| 	padding: 4px !important; | ||||
| 	padding-left: 8px !important; | ||||
| 	padding-right: 8px !important; | ||||
| 	font-weight: bolder; | ||||
| 	font-size: smaller | ||||
| } | ||||
|  | ||||
| .WP2 { | ||||
| 	background: radial-gradient(#ffff00, #ffe169) !important; | ||||
| } | ||||
|  | ||||
| .WP4 { | ||||
| 	color: black !important; | ||||
| 	background: radial-gradient(#00ffff, #69c3ff) !important; | ||||
| 	border: 1px solid darkgray; | ||||
|   padding-top: 8px !important; | ||||
| } | ||||
|  | ||||
| .WP5 { | ||||
| 	color: black !important; | ||||
| 	background: radial-gradient(#ff0000, #e396ff) !important; | ||||
| 	border: 1px solid darkgray; | ||||
|   padding-top: 8px !important; | ||||
| } | ||||
|  | ||||
| .TA3 { | ||||
| 	color: black !important; | ||||
| 	background: radial-gradient(#99ff99, #ccffcc) !important; | ||||
| 	border: 1px solid darkgray; | ||||
|   padding-top: 8px !important; | ||||
| } | ||||
|  | ||||
| .left { | ||||
|   | ||||
| @@ -6,6 +6,18 @@ | ||||
| </head> | ||||
| <body> | ||||
| 	<font layout:fragment="title">Arbeitszeit</font> | ||||
| 	<ul layout:fragment="menuitem"> | ||||
| 		<li class="nav-item" sec:authorize="hasRole('timetrack_user')"> | ||||
| 			<form th:action="@{/done/list}" th:object="${doneModel}" method="post"> | ||||
| 				<div class="nav-link" style="padding-top: 0px !important"> | ||||
| 					<div class="input-group input-group-sm mb-3" style="margin-bottom: 0px !important"> | ||||
| 						<input type="date" class="form-control" th:value="*{day}" th:field="*{day}" /> | ||||
| 						<button type="submit" class="btn btn-primary btn-sm">Ok</button> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</form> | ||||
| 		</li> | ||||
| 	</ul> | ||||
| 	<ul layout:fragment="menu"> | ||||
| 		<li class="nav-item" sec:authorize="hasRole('timetrack_user')"><a class="nav-link" th:href="@{/done/add}">Neuer | ||||
| 				Eintrag</a></li> | ||||
| @@ -18,18 +30,104 @@ | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_calendar">Kalender</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_project">Projekt</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_module">Modul</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_job">Tätigkeit</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_budget">Abrechnung</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_job">Aufgabe</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link navlinkstyle" data-bs-toggle="tab" href="#div_billing">Abrechnung</a></li> | ||||
| 		</ul> | ||||
| 		<div class="tabdivblurred tab-content"> | ||||
| 			<div id="div_list" class="tab-pane active">Liste</div> | ||||
| 			<div id="div_list" class="tab-pane active" style="background-color: white"> | ||||
| 				<table class="table table-striped table-condensed"> | ||||
| 					<thead> | ||||
| 						<tr> | ||||
| 							<th>Von</th> | ||||
| 							<th>Bis</th> | ||||
| 							<th>Projekt</th> | ||||
| 							<th>Modul</th> | ||||
| 							<th>Aufgabe</th> | ||||
| 							<th>Abrechnung</th> | ||||
| 						</tr> | ||||
| 					</thead> | ||||
| 					<tbody> | ||||
| 						<tr th:each="done : ${doneList}"> | ||||
| 							<td><span th:text="${#temporals.format(done.timeFrom, 'HH:mm')}"></span></td> | ||||
| 							<td><span th:text="${#temporals.format(done.timeUntil, 'HH:mm')}"></span></td> | ||||
| 							<td><span th:text="${done.project.name}"></span></td> | ||||
| 							<td><span th:text="${done.module.name}"></span></td> | ||||
| 							<td><span th:text="${done.activity.name}"></span></td> | ||||
| 							<td><span th:text="${done.billing.shortcut}" th:class="'billing ' + ${done.billing.csskey}" | ||||
| 								th:if="${done.billing != null}"></span></td> | ||||
| 						</tr> | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 			<div id="div_summary" class="tab-pane fade">Zusammenfassung</div> | ||||
| 			<div id="div_attachment" class="tab-pane fade">Anhang</div> | ||||
| 			<div id="div_calendar" class="tab-pane fade">Kalender</div> | ||||
| 			<div id="div_project" class="tab-pane fade">Projekt</div> | ||||
| 			<div id="div_module" class="tab-pane fade">Modul</div> | ||||
| 			<div id="div_job" class="tab-pane fade">Tätigkeit</div> | ||||
| 			<div id="div_budget" class="tab-pane fade">Abrechnung</div> | ||||
| 			<div id="div_project" class="tab-pane fade" style="background-color: white"> | ||||
| 				<table class="table table-striped table-condensed"> | ||||
| 					<thead> | ||||
| 						<tr> | ||||
| 							<th>Name</th> | ||||
| 							<th>Benutzt</th> | ||||
| 						</tr> | ||||
| 					</thead> | ||||
| 					<tbody> | ||||
| 						<tr th:each="project : ${projectList}"> | ||||
| 							<td><span th:text="${project.name}"></span></td> | ||||
| 							<td>TODO</td> | ||||
| 						</tr> | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 			<div id="div_module" class="tab-pane fade" style="background-color: white"> | ||||
| 				<table class="table table-striped table-condensed"> | ||||
| 					<thead> | ||||
| 						<tr> | ||||
| 							<th>Name</th> | ||||
| 							<th>Benutzt</th> | ||||
| 						</tr> | ||||
| 					</thead> | ||||
| 					<tbody> | ||||
| 						<tr th:each="module : ${moduleList}"> | ||||
| 							<td><span th:text="${module.name}"></span></td> | ||||
| 							<td>TODO</td> | ||||
| 						</tr> | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 			<div id="div_job" class="tab-pane fade" style="background-color: white"> | ||||
| 				<table class="table table-striped table-condensed"> | ||||
| 					<thead> | ||||
| 						<tr> | ||||
| 							<th>Name</th> | ||||
| 							<th>Benutzt</th> | ||||
| 						</tr> | ||||
| 					</thead> | ||||
| 					<tbody> | ||||
| 						<tr th:each="job : ${jobList}"> | ||||
| 							<td><span th:text="${job.name}"></span></td> | ||||
| 							<td>TODO</td> | ||||
| 						</tr> | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 			<div id="div_billing" class="tab-pane fade" style="background-color: white"> | ||||
| 				<table class="table table-striped table-condensed"> | ||||
| 					<thead> | ||||
| 						<tr> | ||||
| 							<th>Name</th> | ||||
| 							<th>Symbol</th> | ||||
| 							<th>Benutzt</th> | ||||
| 						</tr> | ||||
| 					</thead> | ||||
| 					<tbody> | ||||
| 						<tr th:each="billing : ${billingList}"> | ||||
| 							<td><span th:text="${billing.name}"></span></td> | ||||
| 							<td><span th:text="${billing.shortcut}" th:class="'billing ' + ${billing.csskey}"></span></td> | ||||
| 							<td>TODO</td> | ||||
| 						</tr> | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</main> | ||||
| </body> | ||||
|   | ||||
| @@ -38,6 +38,7 @@ | ||||
| 							<li><a class="dropdown-item" th:href="@{/logout}">[[${currentUser}]] abmelden</a></li> | ||||
| 						</ul></li> | ||||
| 					<li class="nav-item"><a class="nav-link titlemod"><font layout:fragment="title"></font></a></li> | ||||
| 					<li layout:fragment="menuitem" style="list-style-type: none"></li> | ||||
| 					<li layout:fragment="menu" style="list-style-type: none"></li> | ||||
| 				</ul> | ||||
| 			</div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user