preparations for online camp planning tool

This commit is contained in:
Jottyfan
2026-04-14 20:20:37 +02:00
parent 5dd7829a69
commit f7d726b8a2
10 changed files with 404 additions and 1 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ plugins {
}
group = 'de.jottyfan.camporganizer'
version = '1.0.3'
version = '1.0.4'
description = """CampOrganizer2"""
@@ -0,0 +1,38 @@
package de.jottyfan.camporganizer.module.campside;
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.PathVariable;
import de.jottyfan.camporganizer.module.camplist.CommonController;
/**
*
* @author jotty
*
*/
@Controller
public class CampsideController extends CommonController {
@Autowired
CampsideService service;
@GetMapping("/campside")
public String getCampside(final Model model) {
model.addAttribute("myCampBookings", service.getCampBookingsOf(super.getCurrentUser()));
return "/campside/list";
}
@GetMapping("/campside/{id}")
public String getCampDetails(@PathVariable("id") Integer fkCamp, final Model model) {
// TODO: load content for the camp with fkCamp
return "/campside/camp";
}
@GetMapping("/campside/{id}/plan")
public String getCampPlan(@PathVariable("id") Integer fkCamp, final Model model) {
// TODO: load camp plan from database
return "/campside/plan";
}
}
@@ -0,0 +1,83 @@
package de.jottyfan.camporganizer.module.campside;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.V_CAMP;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
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.Record;
import org.jooq.Record9;
import org.jooq.SelectSeekStep1;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import de.jottyfan.camporganizer.db.jooq.tables.TProfile;
import de.jottyfan.camporganizer.module.campside.model.MyCampBookingBean;
import de.jottyfan.camporganizer.module.campside.model.MyPersonBean;
/**
*
* @author jotty
*/
@Repository
public class CampsideRepository {
private static final Logger LOGGER = LogManager.getLogger(CampsideRepository.class);
@Autowired
private DSLContext jooq;
public List<MyCampBookingBean> getMyCampBookings(String username) {
TProfile REGISTRATOR = TProfile.T_PROFILE.as("registrator");
SelectSeekStep1<Record9<String, String, Double, Boolean, LocalDateTime, LocalDateTime, Integer, String, String>, LocalDateTime> sql = jooq
// @formatter:off
.select(V_CAMP.NAME,
V_CAMP.LOCATION_NAME,
V_CAMP.YEAR,
V_CAMP.IS_OVER,
V_CAMP.ARRIVE,
V_CAMP.DEPART,
V_CAMP.PK,
T_PERSON.FORENAME,
T_PERSON.SURNAME)
.from(T_PROFILE)
.leftJoin(T_PERSON).on(T_PERSON.FK_PROFILE.eq(T_PROFILE.PK))
.leftJoin(REGISTRATOR).on(REGISTRATOR.PK.eq(T_PERSON.FK_REGISTRATOR))
.leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
.where(DSL.trim(T_PROFILE.USERNAME).eq(username == null ? null : username.trim()))
.and(T_PERSON.PK.isNotNull())
.orderBy(V_CAMP.ARRIVE.desc());
// @formatter:on
LOGGER.trace(sql);
Map<Integer, MyCampBookingBean> map = new HashMap<>();
Iterator<Record9<String, String, Double, Boolean, LocalDateTime, LocalDateTime, Integer, String, String>> i = sql.fetch().iterator();
while (i.hasNext()) {
Record r = i.next();
Integer fkCamp = r.get(V_CAMP.PK);
MyCampBookingBean bean = map.get(fkCamp);
if (bean == null) {
bean = new MyCampBookingBean();
map.put(fkCamp, bean);
}
bean.setFkCamp(fkCamp);
bean.setLocationName(r.get(V_CAMP.LOCATION_NAME));
bean.setCampName(String.format("%s %4.0f", r.get(V_CAMP.NAME), r.get(V_CAMP.YEAR)).trim());
bean.setIsOver(r.get(V_CAMP.IS_OVER));
bean.setArrive(r.get(V_CAMP.ARRIVE));
bean.setDepart(r.get(V_CAMP.DEPART));
bean.getPerson().add(MyPersonBean.of(null, r.get(T_PERSON.FORENAME), r.get(T_PERSON.SURNAME)));
}
List<MyCampBookingBean> list = new ArrayList<>(map.values());
list.sort((o1, o2) -> (o1 == null || o1.getArrive() == null || o2 == null) ? 0 : o1.getArrive().compareTo(o2.getArrive()));
return list;
}
}
@@ -0,0 +1,23 @@
package de.jottyfan.camporganizer.module.campside;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.campside.model.MyCampBookingBean;
/**
*
* @author jotty
*
*/
@Service
public class CampsideService {
@Autowired
private CampsideRepository repository;
public List<MyCampBookingBean> getCampBookingsOf(String currentUser) {
return repository.getMyCampBookings(currentUser);
}
}
@@ -0,0 +1,118 @@
package de.jottyfan.camporganizer.module.campside.model;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author jotty
*
*/
public class MyCampBookingBean implements Serializable {
private static final long serialVersionUID = 1L;
private Integer fkCamp;
private String locationName;
private String campName;
private Boolean isOver;
private LocalDateTime arrive;
private LocalDateTime depart;
private final List<MyPersonBean> person;
public MyCampBookingBean() {
person = new ArrayList<>();
}
/**
* @return the fkCamp
*/
public Integer getFkCamp() {
return fkCamp;
}
/**
* @param fkCamp the fkCamp to set
*/
public void setFkCamp(Integer fkCamp) {
this.fkCamp = fkCamp;
}
/**
* @return the locationName
*/
public String getLocationName() {
return locationName;
}
/**
* @param locationName the locationName to set
*/
public void setLocationName(String locationName) {
this.locationName = locationName;
}
/**
* @return the campName
*/
public String getCampName() {
return campName;
}
/**
* @param campName the campName to set
*/
public void setCampName(String campName) {
this.campName = campName;
}
/**
* @return the isOver
*/
public Boolean getIsOver() {
return isOver;
}
/**
* @param isOver the isOver to set
*/
public void setIsOver(Boolean isOver) {
this.isOver = isOver;
}
/**
* @return the person
*/
public List<MyPersonBean> getPerson() {
return person;
}
/**
* @return the arrive
*/
public LocalDateTime getArrive() {
return arrive;
}
/**
* @param arrive the arrive to set
*/
public void setArrive(LocalDateTime arrive) {
this.arrive = arrive;
}
/**
* @return the depart
*/
public LocalDateTime getDepart() {
return depart;
}
/**
* @param depart the depart to set
*/
public void setDepart(LocalDateTime depart) {
this.depart = depart;
}
}
@@ -0,0 +1,66 @@
package de.jottyfan.camporganizer.module.campside.model;
import java.io.Serializable;
/**
*
* @author jotty
*
*/
public class MyPersonBean implements Serializable {
private static final long serialVersionUID = 1L;
private Integer pkPerson;
private String forename;
private String surname;
public static final MyPersonBean of(Integer pkPerson, String forename, String surname) {
MyPersonBean bean = new MyPersonBean();
bean.setPkPerson(pkPerson);
bean.setForename(forename);
bean.setSurname(surname);
return bean;
}
/**
* @return the forename
*/
public String getForename() {
return forename;
}
/**
* @param forename the forename to set
*/
public void setForename(String forename) {
this.forename = forename;
}
/**
* @return the pkPerson
*/
public Integer getPkPerson() {
return pkPerson;
}
/**
* @param pkPerson the pkPerson to set
*/
public void setPkPerson(Integer pkPerson) {
this.pkPerson = pkPerson;
}
/**
* @return the surname
*/
public String getSurname() {
return surname;
}
/**
* @param surname the surname to set
*/
public void setSurname(String surname) {
this.surname = surname;
}
}
@@ -8,6 +8,10 @@
<th:block layout:fragment="content">
<div class="mainpage">
<div th:replace="~{/fragments/camplist.html::camplist(camps=${camps})}"></div>
<br />
<div class="text-center">
<a class="btn btn-outline-secondary titlefont d-inline-block" th:href="@{/campside}">Zu meinen Freizeiten</a>
</div>
</div>
</th:block>
</body>
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Camp Organizer 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="mainpage">
<div class="alert alert-info">
Diese Übersicht wurde noch nicht entwickelt.
</div>
</div>
</th:block>
</body>
</html>
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Camp Organizer 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="mainpage">
<div class="alert alert-warning">
Diese Seite befindet sich noch im Aufbau. Nutze vorübergehend besser <a th:href="@{/camplist}">die Freizeitübersicht</a>.
</div>
<div class="alert alert-primary menufont" th:if="${myCampBookings.size() < 1}">Für diese Freizeit hast du keine Anmeldedaten.</div>
<div class="container">
<div class="row g-2">
<div class="col" th:each="b : ${myCampBookings}">
<div class="card">
<div class="card-header">
<div class="headlinefont" th:text="${b.campName}"></div>
<div class="menufont" th:text="${#temporals.format(b.arrive, 'dd.MM.') + ' - ' + #temporals.format(b.depart, 'dd.MM.')}"></div>
</div>
<div class="card-body">
<span class="menufont">von dir angemeldet:</span><br />
<div th:each="p : ${b.person}">
<span th:text="${p.forename}"></span>&nbsp;<span th:text="${p.surname}"></span>
</div>
</div>
<div class="card-footer">
<a class="btn btn-outline-secondary" th:href="@{/campside/{id}(id=${b.fkCamp})}">Freizeitdetails</a>
<a class="btn btn-outline-secondary" th:href="@{/campside/{id}/plan(id=${b.fkCamp})}">Freizeitplan</a>
</div>
</div>
</div>
</div>
</div>
</div>
</th:block>
</body>
</html>
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Camp Organizer 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="mainpage">
<div class="alert alert-info">
Diese Übersicht wurde noch nicht entwickelt. Sie wird nur für Mitarbeiter verfügbar sein.
</div>
</div>
</th:block>
</body>
</html>