33 Commits
Author SHA1 Message Date
Jottyfan 1cf2e160eb added camp report for Schweiz 2026 2026-08-14 22:02:24 +02:00
Jottyfan f7d726b8a2 preparations for online camp planning tool 2026-04-14 20:20:37 +02:00
Jottyfan 5dd7829a69 finished reports for olfen ostern 2026 and added grow up 2026 report 2026-04-12 18:31:07 +02:00
Jottyfan d4037491f1 added melissa report 2026-04-12 00:17:16 +02:00
Papa 85b5c5fbf2 corrections 2026-04-11 23:51:12 +02:00
jotty 62846de905 Merge branch 'reports26' 2026-04-10 19:46:31 +02:00
Papa f287fcfc00 added first reports 2026-04-10 19:42:26 +02:00
Jottyfan 2dea45780a forbid camp registration without profile, see #25 2026-03-21 14:49:29 +01:00
Jottyfan f2d327de2e optimized privileges management 2025-11-16 23:03:45 +01:00
Jottyfan a1fc517911 added camp document editor, see #24 2025-10-22 23:04:52 +02:00
Jottyfan 13773f54de fixed bug in navigation 2025-07-25 22:25:48 +02:00
Jottyfan 30c2bd5fc0 restrict business camp overview by current year 2025-07-19 20:29:23 +02:00
Jottyfan 27c1669ed3 make scrolling work again 2025-06-07 19:42:49 +02:00
Jottyfan 3bb68aff1f last finetuning 2025-06-07 18:45:01 +02:00
jotty 4c32a12d2d slide of thumbnails experiments 2025-05-31 10:25:38 +02:00
Jottyfan 485a222be4 stock images description properties file 2025-05-29 17:53:18 +02:00
Jottyfan 82ce501f39 start page final changes 2025-05-29 16:51:55 +02:00
Jottyfan d3389e4813 more welcome page gimmicks 2025-05-13 23:06:17 +02:00
Jottyfan bdfe54a148 some progress on landing page 2025-05-05 22:17:02 +02:00
Jottyfan 1d67aefa4d redesign of the landing page, see #20 2025-04-05 17:48:18 +02:00
Jottyfan a801d3178a changed menu, see #19 2025-04-03 22:45:26 +02:00
Jottyfan 15a4388490 transition on logo hover 2025-03-30 22:25:47 +02:00
Jottyfan 5a891c085d login icon hover effect 2025-03-27 22:29:39 +01:00
Jottyfan 8cf1857c24 logo as button, see #18 2025-03-27 22:14:47 +01:00
Jottyfan 890d46dcdd added image of lars 2025-03-24 21:45:40 +01:00
Jottyfan b26b014225 added lars 2025-03-23 22:44:35 +01:00
Jottyfan 2b2246f2de start booking with time now 2025-03-23 22:32:26 +01:00
jotty 92970d8c63 src/main/resources/templates/fragments/camplist.html aktualisiert
added registration start time info
2025-03-23 08:21:24 +01:00
Jottyfan be4b75eef4 merged progress 2024-11-30 17:35:31 +01:00
Jottyfan 31727e23ac removed ok button on revoked, see #9 2024-11-30 17:33:50 +01:00
Jottyfan 2abb937725 confirmation accepts deletion of revoked registrations 2024-10-26 16:44:26 +02:00
Jottyfan b5403ae20c still buggy on rejecting people and loading old bookings 2024-10-23 22:42:15 +02:00
Jottyfan ae9e2018a8 download of outlays 2024-10-12 23:19:17 +02:00
97 changed files with 2201 additions and 592 deletions
+5 -8
View File
@@ -6,20 +6,17 @@
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/main" path="src/main/resources">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
<attributes>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>camporganizer2</name>
<name>CampOrganizer2</name>
<comment></comment>
<projects>
</projects>
+3 -3
View File
@@ -1,4 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.source=17
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
org.eclipse.jdt.core.compiler.compliance=21
org.eclipse.jdt.core.compiler.source=21
+9 -2
View File
@@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId" project-version="1.5.0">
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="CampOrganizer2">
<property name="context-root" value="CampOrganizer2"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
</wb-module>
</project-modules>
@@ -3,5 +3,5 @@
<fixed facet="jst.java"/>
<fixed facet="jst.web"/>
<installed facet="jst.web" version="2.4"/>
<installed facet="jst.java" version="17"/>
<installed facet="jst.java" version="21"/>
</faceted-project>
+33 -23
View File
@@ -1,6 +1,6 @@
plugins {
id 'org.springframework.boot' version '3.2.4'
id "io.spring.dependency-management" version "1.1.4"
id 'org.springframework.boot' version '3.5.0'
id "io.spring.dependency-management" version "1.1.7"
id 'java'
id 'war'
id 'eclipse'
@@ -8,15 +8,10 @@ plugins {
}
group = 'de.jottyfan.camporganizer'
version = '0.8.5'
version = '1.0.5'
description = """CampOrganizer2"""
sourceCompatibility = 17
targetCompatibility = 17
mainClassName = "de.jottyfan.camporganizer.Main"
repositories {
mavenCentral()
maven {
@@ -27,6 +22,20 @@ repositories {
}
}
tasks.withType(JavaCompile).configureEach {
options.compilerArgs.add("-parameters")
}
application {
mainClass = 'de.jottyfan.camporganizer.Main'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
war {
doFirst {
manifest {
@@ -40,26 +49,27 @@ war {
}
dependencies {
implementation 'org.jooq:jooq:3.19.6'
implementation 'de.jottyfan:COJooq:2024.03.16c'
implementation 'org.jooq:jooq:3.20.8'
implementation 'de.jottyfan:COJooq:2024.10.24'
implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.23.1'
implementation 'org.apache.logging.log4j:log4j-api:2.25.2'
implementation 'org.apache.logging.log4j:log4j-core:2.25.2'
implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.25.2'
implementation 'org.webjars:bootstrap:5.3.2'
implementation 'org.webjars:font-awesome:6.5.1'
implementation 'org.webjars:bootstrap:5.3.8'
implementation 'org.webjars:font-awesome:7.0.1'
implementation 'org.webjars:jquery:3.7.1'
implementation 'org.webjars:popper.js:2.11.7'
implementation 'org.webjars:datatables:1.13.5'
implementation 'org.webjars:select2:4.0.13'
implementation 'org.webjars.npm:fancyapps__fancybox:3.5.7'
implementation 'net.sf.biweekly:biweekly:0.6.7'
implementation 'net.sf.biweekly:biweekly:0.6.8'
// for using the keycloak rest interface
implementation 'org.keycloak:keycloak-server-spi:24.0.1'
implementation 'org.keycloak:keycloak-admin-client:24.0.1'
implementation 'org.jboss.resteasy:resteasy-client:6.2.6.Final'
implementation 'org.keycloak:keycloak-server-spi:26.4.1'
implementation 'org.keycloak:keycloak-admin-client:26.0.7'
implementation 'org.jboss.resteasy:resteasy-client:7.0.0.Final'
// backward compatibility until the complete registration is converted to keycloak
implementation 'org.jasypt:jasypt:1.9.3'
@@ -68,20 +78,20 @@ dependencies {
implementation 'com.rometools:rome:2.1.0'
// mail support
implementation 'commons-validator:commons-validator:1.8.0'
implementation 'commons-validator:commons-validator:1.10.0'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-jooq'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation 'org.springframework.security:spring-security-oauth2-authorization-server:1.2.1'
implementation 'org.springframework.security:spring-security-oauth2-authorization-server:1.5.3'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.4.0'
implementation 'commons-io:commons-io:2.15.1'
implementation 'commons-io:commons-io:2.20.0'
runtimeOnly 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class Main extends SpringBootServletInitializer {
private static final Logger LOGGER = LogManager.getLogger(Main.class);
public static final Logger LOGGER = LogManager.getLogger(Main.class);
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
@@ -31,7 +31,7 @@ public class Main extends SpringBootServletInitializer {
Path path = Paths.get("");
String p = path.toAbsolutePath().toString();
p = p.substring(p.lastIndexOf("/") + 1);
LOGGER.info("running in {}", p);
LOGGER.debug("running in {}", p);
// TODO: put p + "properties" somehow into consideration to load the application.properties
SpringApplication.run(Main.class, args);
}
@@ -11,7 +11,6 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/**
*
@@ -35,13 +34,8 @@ public class SecurityConfiguration {
// @formatter:off
.oauth2Login(o -> o.defaultSuccessUrl("/"))
.logout(o -> o.logoutSuccessHandler(new OidcClientInitiatedLogoutSuccessHandler(crr)))
.authorizeHttpRequests(o -> o.requestMatchers(
AntPathRequestMatcher.antMatcher("/dashboard/**"),
AntPathRequestMatcher.antMatcher("/business/**"),
AntPathRequestMatcher.antMatcher("/confirmation/**"),
AntPathRequestMatcher.antMatcher("/userlogin/**")
).authenticated()
.anyRequest().permitAll())
.authorizeHttpRequests(o -> o.requestMatchers("/", "/impressum", "/datenschutz", "/allgemeines", "/camplist", "/reports/**", "/nachruf", "/verein", "/vereinsmitglieder", "/kontakt", "/css/**", "/js/**", "/images/**", "/fonts/**", "/font-awesome/**", "/webjars/**", "/ical/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
.sessionManagement(o -> o.init(sec));
// @formatter:on
@@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.CampDocumentBean;
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
import de.jottyfan.camporganizer.module.camplist.CommonController;
@@ -192,4 +193,41 @@ public class AdminController extends CommonController {
redirect.addAttribute("error", error);
return error != null ? "redirect:/admin/camp/edit/" + id : "redirect:/admin/camp";
}
@GetMapping("/admin/campdocument")
public String getCampDocumentList(Model model, HttpServletRequest request) {
model.addAttribute("campmap", service.getCampMap());
model.addAttribute("documentmap", service.getDocumentsMap());
model.addAttribute("camps", service.getAllCamps());
model.addAttribute("documents", service.getDocumentsForCamps());
model.addAttribute("campdocuments", service.getAllCampDocuments());
model.addAttribute("campdocument", new CampDocumentBean());
return "/admin/campdocument";
}
@PostMapping("/admin/campdocument/add")
public String addCampDocument(@Valid @ModelAttribute("bean") CampDocumentBean bean, final BindingResult bindingResult,
Model model, HttpServletRequest request, RedirectAttributes redirect) {
if (bindingResult.hasErrors()) {
for (ObjectError error : bindingResult.getAllErrors()) {
LOGGER.error("error {}: {}", error.getCode(), error.getDefaultMessage());
}
model.addAttribute("campmap", service.getCampMap());
model.addAttribute("documentmap", service.getDocumentsMap());
model.addAttribute("camps", service.getAllCamps());
model.addAttribute("documents", service.getDocumentsForCamps());
model.addAttribute("campdocuments", service.getAllCampDocuments());
return "/admin/campdocument";
}
service.upsertCampDocument(bean);
return "redirect:/admin/campdocument";
}
@GetMapping("/admin/campdocument/delete/{id}")
public String deleteCampDocument(@PathVariable("id") Integer id, Model model, HttpServletRequest request,
RedirectAttributes redirect) {
service.deleteCampDocument(id);
return "redirect:/admin/campdocument";
}
}
@@ -12,6 +12,7 @@ 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.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
@@ -54,6 +55,7 @@ public class AdminPrivilegesController extends CommonController {
model.addAttribute("selected", role);
model.addAttribute("container", service.getPersonCampMappingByModule(EnumModule.valueOf(role)));
model.addAttribute("pagedest", "_admin_privileges_rolebased_" + role);
model.addAttribute("module", role);
return "/admin/privileges/rolebased";
}
@@ -70,6 +72,7 @@ public class AdminPrivilegesController extends CommonController {
model.addAttribute("selected", campname);
model.addAttribute("container", service.getPersonModuleMappingByCamp(campid));
model.addAttribute("pagedest", "_admin_privileges_campbased_" + campid);
model.addAttribute("fkCamp", campid);
return "/admin/privileges/campbased";
}
@@ -86,6 +89,7 @@ public class AdminPrivilegesController extends CommonController {
model.addAttribute("selected", selected);
model.addAttribute("container", service.getModuleCampMappingByPerson(userid));
model.addAttribute("pagedest", "_admin_privileges_userbased_" + userid);
model.addAttribute("fkProfile", userid);
return "/admin/privileges/userbased";
}
@@ -96,9 +100,12 @@ public class AdminPrivilegesController extends CommonController {
}
@GetMapping("/admin/privileges/add/{pagedest}")
public String prepareAdd(Model model, @PathVariable("pagedest") String pagedest) {
public String prepareAdd(Model model, @PathVariable("pagedest") String pagedest,
@RequestParam(name = "fkCamp", required = false) Integer fkCamp,
@RequestParam(name = "fkProfile", required = false) Integer fkProfile,
@RequestParam(name = "module", required = false) String module) {
model.addAttribute("pagedest", pagedest);
model.addAttribute("bean", new CampProfileBean());
model.addAttribute("bean", CampProfileBean.of(fkCamp, fkProfile, module));
model.addAttribute("profiles", service.getProfiles());
model.addAttribute("camps", service.getAllCamps());
model.addAttribute("modules", service.getAllModules());
@@ -1,6 +1,7 @@
package de.jottyfan.camporganizer.module.admin;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPDOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENTROLE;
@@ -38,6 +39,7 @@ import org.jooq.UpdateSetMoreStep;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@@ -46,6 +48,7 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampdocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampprofileRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentroleRecord;
@@ -53,6 +56,7 @@ import de.jottyfan.camporganizer.db.jooq.tables.records.TLocationRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.CampDocumentBean;
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
import de.jottyfan.camporganizer.module.admin.model.IntKeyValueBean;
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
@@ -70,11 +74,17 @@ import jakarta.validation.Valid;
@Transactional(transactionManager = "transactionManager")
public class AdminRepository {
private final GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak;
private static final Logger LOGGER = LogManager.getLogger(AdminRepository.class);
@Autowired
private DSLContext jooq;
AdminRepository(GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak) {
this.userAuthoritiesMapperForKeycloak = userAuthoritiesMapperForKeycloak;
}
/**
* get the document with that ID
*
@@ -96,7 +106,7 @@ public class AdminRepository {
.groupBy(T_DOCUMENT.PK, T_DOCUMENT.NAME, T_DOCUMENT.DOCTYPE, T_DOCUMENT.FILETYPE)
.orderBy(T_DOCUMENT.PK);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
Record5<Integer, String, EnumDocument, EnumFiletype, EnumCamprole[]> r = sql.fetchOne();
if (r != null) {
DocumentBean bean = new DocumentBean();
@@ -129,7 +139,7 @@ public class AdminRepository {
.groupBy(T_DOCUMENT.PK, T_DOCUMENT.NAME, T_DOCUMENT.DOCTYPE, T_DOCUMENT.FILETYPE)
.orderBy(T_DOCUMENT.PK);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql.toString());
List<DocumentBean> list = new ArrayList<>();
Iterator<Record5<Integer, String, EnumDocument, EnumFiletype, EnumCamprole[]>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -162,7 +172,7 @@ public class AdminRepository {
.from(T_DOCUMENT)
.where(condition);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<DocumentBean> list = new ArrayList<>();
Iterator<Record4<Integer, String, EnumDocument, EnumFiletype>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -202,7 +212,7 @@ public class AdminRepository {
.set(T_DOCUMENT.FILETYPE, bean.getFiletype())
.where(T_DOCUMENT.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
} else {
InsertResultStep<TDocumentRecord> sql = DSL.using(c)
@@ -215,7 +225,7 @@ public class AdminRepository {
.values(bean.getName(), bean.getDoctype(), bean.getDocument(), bean.getFiletype())
.returning(T_DOCUMENT.PK);
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
pk = sql.fetchOne().get(T_DOCUMENT.PK);
lrw.add(1);
}
@@ -233,7 +243,7 @@ public class AdminRepository {
.onConflict(T_DOCUMENTROLE.FK_DOCUMENT, T_DOCUMENTROLE.CAMPROLE)
.doNothing();
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
removeCandidates.remove(role);
} catch (IllegalArgumentException e) {
@@ -246,7 +256,7 @@ public class AdminRepository {
.where(T_DOCUMENTROLE.FK_DOCUMENT.eq(pk))
.and(T_DOCUMENTROLE.CAMPROLE.in(removeCandidates));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
});
return lrw.getCounter();
@@ -268,7 +278,7 @@ public class AdminRepository {
.set(T_CAMP.FK_DOCUMENT, (Integer) null)
.where(T_CAMP.FK_DOCUMENT.eq(pk));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
UpdateConditionStep<TLocationRecord> sql1 = DSL.using(t)
@@ -277,7 +287,7 @@ public class AdminRepository {
.set(T_LOCATION.FK_DOCUMENT, (Integer) null)
.where(T_LOCATION.FK_DOCUMENT.eq(pk));
// @formatter:on
LOGGER.debug("{}", sql1.toString());
LOGGER.trace(sql1);
lrw.add(sql1.execute());
DeleteConditionStep<TDocumentroleRecord> sql2 = DSL.using(t)
@@ -285,7 +295,7 @@ public class AdminRepository {
.deleteFrom(T_DOCUMENTROLE)
.where(T_DOCUMENTROLE.FK_DOCUMENT.eq(pk));
// @formatter:on
LOGGER.debug("{}", sql2.toString());
LOGGER.trace(sql2);
lrw.add(sql2.execute());
DeleteConditionStep<TDocumentRecord> sql3 = DSL.using(t)
@@ -293,7 +303,7 @@ public class AdminRepository {
.deleteFrom(T_DOCUMENT)
.where(T_DOCUMENT.PK.eq(pk));
// @formatter:on
LOGGER.debug("{}", sql3.toString());
LOGGER.trace(sql3);
lrw.add(sql3.execute());
});
return lrw.getCounter();
@@ -306,7 +316,7 @@ public class AdminRepository {
*/
public List<LocationBean> getLocations() {
SelectWhereStep<TLocationRecord> sql = jooq.selectFrom(T_LOCATION);
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<LocationBean> list = new ArrayList<>();
for (TLocationRecord r : sql.fetch()) {
list.add(LocationBean.of(r));
@@ -322,7 +332,7 @@ public class AdminRepository {
*/
public LocationBean getLocation(Integer id) {
SelectConditionStep<TLocationRecord> sql = jooq.selectFrom(T_LOCATION).where(T_LOCATION.PK.eq(id));
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return LocationBean.of(sql.fetchOne());
}
@@ -334,7 +344,7 @@ public class AdminRepository {
*/
public Integer deleteLocation(Integer id) {
DeleteConditionStep<TLocationRecord> sql = jooq.deleteFrom(T_LOCATION).where(T_LOCATION.PK.eq(id));
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.execute();
}
@@ -356,7 +366,7 @@ public class AdminRepository {
T_LOCATION.URL)
.values(bean.getName(), bean.getFkDocument(), bean.getUrl());
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
} else {
UpdateConditionStep<TLocationRecord> sql = DSL.using(t)
@@ -367,7 +377,7 @@ public class AdminRepository {
.set(T_LOCATION.URL, bean.getUrl())
.where(T_LOCATION.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
}
});
@@ -381,7 +391,7 @@ public class AdminRepository {
*/
public List<CampBean> getAllCamps() {
SelectWhereStep<TCampRecord> sql = jooq.selectFrom(T_CAMP);
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<CampBean> list = new ArrayList<>();
for (TCampRecord r : sql.fetch()) {
list.add(CampBean.of(r));
@@ -397,7 +407,7 @@ public class AdminRepository {
*/
public CampBean getCamp(Integer id) {
SelectConditionStep<TCampRecord> sql = jooq.selectFrom(T_CAMP).where(T_CAMP.PK.eq(id));
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return CampBean.of(sql.fetchOne());
}
@@ -411,11 +421,11 @@ public class AdminRepository {
LambdaResultWrapper lrw = new LambdaResultWrapper();
jooq.transaction(t -> {
SelectConditionStep<TPersonRecord> sql1 = DSL.using(t).selectFrom(T_PERSON).where(T_PERSON.FK_CAMP.eq(id));
LOGGER.debug(sql1.toString());
LOGGER.trace(sql1);
Integer registrations = sql1.fetch().size();
if (registrations < 1) {
DeleteConditionStep<TCampRecord> sql2 = DSL.using(t).deleteFrom(T_CAMP).where(T_CAMP.PK.eq(id));
LOGGER.debug(sql2.toString());
LOGGER.trace(sql2);
sql2.execute();
} else {
lrw.putString("error", String
@@ -435,10 +445,8 @@ public class AdminRepository {
jooq.transaction(t -> {
LocalDate arriveDate = bean.getArrive();
LocalDate departDate = bean.getDepart();
LocalDate startBookingDate = bean.getStartBooking();
LocalDateTime arrive = arriveDate == null ? null : arriveDate.atStartOfDay();
LocalDateTime depart = departDate == null ? null : departDate.atStartOfDay();
LocalDateTime startBooking = startBookingDate == null ? null : startBookingDate.atStartOfDay();
if (bean.getPk() == null) {
InsertValuesStep16<TCampRecord, LocalDateTime, String, LocalDateTime, Integer, Integer, Integer, Boolean, Integer, Integer, String, String, Integer, Integer, Integer, Integer, LocalDateTime> sql = DSL
.using(t)
@@ -462,9 +470,9 @@ public class AdminRepository {
T_CAMP.START_BOOKING)
.values(arrive, bean.getCountries(), depart, bean.getFkDocument(), bean.getFkLocation(), bean.getFkProfile(),
bean.getLockSales() != null ? bean.getLockSales() : false, bean.getMaxAge(), bean.getMinAge(), bean.getName(), bean.getPrice(),
bean.getBedsFemale(), bean.getBedsMale(), bean.getBlockedBedsFemale(), bean.getBlockedBedsMale(), startBooking);
bean.getBedsFemale(), bean.getBedsMale(), bean.getBlockedBedsFemale(), bean.getBlockedBedsMale(), bean.getStartBooking());
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
sql.execute();
} else {
UpdateConditionStep<TCampRecord> sql = DSL.using(t)
@@ -485,10 +493,10 @@ public class AdminRepository {
.set(T_CAMP.BEDS_MALE, bean.getBedsMale())
.set(T_CAMP.BLOCKED_BEDS_FEMALE, bean.getBlockedBedsFemale())
.set(T_CAMP.BLOCKED_BEDS_MALE, bean.getBlockedBedsMale())
.set(T_CAMP.START_BOOKING, startBooking)
.set(T_CAMP.START_BOOKING, bean.getStartBooking())
.where(T_CAMP.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
sql.execute();
}
});
@@ -502,7 +510,7 @@ public class AdminRepository {
*/
public List<ProfileBean> getProfiles() {
SelectWhereStep<TProfileRecord> sql = jooq.selectFrom(T_PROFILE);
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<ProfileBean> list = new ArrayList<>();
for (TProfileRecord r : sql.fetch()) {
list.add(ProfileBean.of(r));
@@ -542,7 +550,7 @@ public class AdminRepository {
.where(T_CAMPPROFILE.MODULE.eq(module))
.orderBy(T_CAMP.ARRIVE, T_PROFILE.PK);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
PrivilegesContainerBean pcb = new PrivilegesContainerBean();
Iterator<Record4<Integer, String, LocalDateTime, String>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -579,7 +587,7 @@ public class AdminRepository {
.where(T_CAMPPROFILE.FK_PROFILE.eq(user))
.orderBy(T_CAMP.ARRIVE);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
PrivilegesContainerBean pcb = new PrivilegesContainerBean();
Iterator<Record4<Integer, String, LocalDateTime, EnumModule>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -618,7 +626,7 @@ public class AdminRepository {
.where(T_CAMPPROFILE.FK_CAMP.eq(camp))
.orderBy(T_PROFILE.SURNAME, T_PROFILE.FORENAME);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
PrivilegesContainerBean pcb = new PrivilegesContainerBean();
Iterator<Record4<Integer, String, String, EnumModule>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -644,7 +652,7 @@ public class AdminRepository {
*/
public void deleteFromCampProfile(Integer id) {
DeleteConditionStep<TCampprofileRecord> sql = jooq.deleteFrom(T_CAMPPROFILE).where(T_CAMPPROFILE.PK.eq(id));
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
sql.execute();
}
@@ -668,7 +676,67 @@ public class AdminRepository {
T_CAMPPROFILE.MODULE)
.doNothing();
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
sql.execute();
}
/**
* get all camp document beans
*
* @return a list of camp document beans; an empty list at least
*/
public List<CampDocumentBean> getAllCampDocuments() {
SelectWhereStep<TCampdocumentRecord> sql = jooq.selectFrom(T_CAMPDOCUMENT);
LOGGER.trace(sql);
return sql.fetchInto(CampDocumentBean.class);
}
/**
* add a camp document combination; if the combination already exists, ignore this
*
* @param fkCamp the camp id
* @param fkDocument the document id
*/
public void insertCampDocument(Integer fkCamp, Integer fkDocument) {
InsertReturningStep<TCampdocumentRecord> sql = jooq
// @formatter:off
.insertInto(T_CAMPDOCUMENT,
T_CAMPDOCUMENT.FK_CAMP,
T_CAMPDOCUMENT.FK_DOCUMENT)
.values(fkCamp, fkDocument)
.onConflict(T_CAMPDOCUMENT.FK_CAMP, T_CAMPDOCUMENT.FK_DOCUMENT)
.doNothing();
// @formatter:off
LOGGER.trace(sql);
sql.execute();
}
/**
* update the camp document combination
*
* @param pk the ID of the combination
* @param fkCamp the ID of the camp
* @param fkDocument the ID of the document
*/
public void updateCampDocument(Integer pk, Integer fkCamp, Integer fkDocument) {
UpdateConditionStep<TCampdocumentRecord> sql = jooq
// @formatter:off
.update(T_CAMPDOCUMENT)
.set(T_CAMPDOCUMENT.FK_CAMP, fkCamp)
.set(T_CAMPDOCUMENT.FK_DOCUMENT, fkDocument)
.where(T_CAMPDOCUMENT.PK.eq(pk));
// @formatter:on
LOGGER.trace(sql);
sql.execute();
}
/**
* delete from camp document
* @param id the ID of the camp document
*/
public void deleteCampDocument(Integer id) {
DeleteConditionStep<TCampdocumentRecord> sql = jooq.deleteFrom(T_CAMPDOCUMENT).where(T_CAMPDOCUMENT.PK.eq(id));
LOGGER.trace(sql);
sql.execute();
}
}
@@ -4,8 +4,9 @@ import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
import java.io.IOException;
import java.util.List;
import jakarta.validation.Valid;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -14,11 +15,13 @@ import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.CampDocumentBean;
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
import de.jottyfan.camporganizer.module.admin.model.ProfileBean;
import de.jottyfan.camporganizer.module.mail.MailBean;
import de.jottyfan.camporganizer.module.mail.MailRepository;
import jakarta.validation.Valid;
/**
*
@@ -186,4 +189,62 @@ public class AdminService {
public String deleteCamp(Integer id) {
return adminRepository.deleteCamp(id);
}
/**
* get all camp documents
*
* @return a list of camp documents; an empty list at least
*/
public List<CampDocumentBean> getAllCampDocuments() {
return adminRepository.getAllCampDocuments();
}
/**
* get all documents for camps
*
* @return the list of documents for camps
*/
public List<DocumentBean> getDocumentsForCamps() {
return adminRepository.getAllDocumentsWith(T_DOCUMENT.DOCTYPE.eq(EnumDocument.camp));
}
/**
* get a map of camp beans
*
* @return the map
*/
public Map<Integer, CampBean> getCampMap() {
return getAllCamps().stream().collect(Collectors.toMap(CampBean::getPk, Function.identity()));
}
/**
* get a map of document beans
*
* @return the map
*/
public Map<Integer, DocumentBean> getDocumentsMap() {
return getAllDocuments().stream().collect(Collectors.toMap(DocumentBean::getPk, Function.identity()));
}
/**
* upsert a camp document bean
*
* @param bean the bean
*/
public void upsertCampDocument(@Valid CampDocumentBean bean) {
if (bean.getPk() == null) {
adminRepository.insertCampDocument(bean.getFkCamp(), bean.getFkDocument());
} else {
adminRepository.updateCampDocument(bean.getPk(), bean.getFkCamp(), bean.getFkDocument());
}
}
/**
* delete the camp document combination
*
* @param id the ID of the combination
*/
public void deleteCampDocument(Integer id) {
adminRepository.deleteCampDocument(id);
}
}
@@ -58,8 +58,8 @@ public class CampBean implements Serializable {
@Min(value = 0)
private Integer blockedBedsMale;
@NotNull
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startBooking;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime startBooking;
/**
* generate a camp bean out of r
@@ -74,7 +74,6 @@ public class CampBean implements Serializable {
CampBean bean = new CampBean();
LocalDateTime arrive = r.getArrive();
LocalDateTime depart = r.getDepart();
LocalDateTime startBooking = r.getStartBooking();
bean.setArrive(arrive == null ? null : arrive.toLocalDate());
bean.setCountries(r.getCountries());
bean.setDepart(depart == null ? null : depart.toLocalDate());
@@ -91,10 +90,15 @@ public class CampBean implements Serializable {
bean.setBedsMale(r.getBedsMale());
bean.setBlockedBedsFemale(r.getBlockedBedsFemale());
bean.setBlockedBedsMale(r.getBlockedBedsMale());
bean.setStartBooking(startBooking == null ? null : startBooking.toLocalDate());
bean.setStartBooking(r.getStartBooking());
return bean;
}
@Override
public String toString() {
return getFullname();
}
public String getFullname() {
return new StringBuilder().append(name).append(" ").append(arrive == null ? "?" : arrive.format(DateTimeFormatter.ofPattern("yyyy"))).toString();
}
@@ -353,14 +357,14 @@ public class CampBean implements Serializable {
/**
* @return the startBooking
*/
public LocalDate getStartBooking() {
public LocalDateTime getStartBooking() {
return startBooking;
}
/**
* @param startBooking the startBooking to set
*/
public void setStartBooking(LocalDate startBooking) {
public void setStartBooking(LocalDateTime startBooking) {
this.startBooking = startBooking;
}
}
@@ -0,0 +1,58 @@
package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable;
/**
*
* @author jotty
*
*/
public class CampDocumentBean implements Serializable {
private static final long serialVersionUID = 1L;
private Integer pk;
private Integer fkCamp;
private Integer fkDocument;
/**
* @return the pk
*/
public Integer getPk() {
return pk;
}
/**
* @param pk the pk to set
*/
public void setPk(Integer pk) {
this.pk = pk;
}
/**
* @return the fkCamp
*/
public Integer getFkCamp() {
return fkCamp;
}
/**
* @param fkCamp the fkCamp to set
*/
public void setFkCamp(Integer fkCamp) {
this.fkCamp = fkCamp;
}
/**
* @return the fkDocument
*/
public Integer getFkDocument() {
return fkDocument;
}
/**
* @param fkDocument the fkDocument to set
*/
public void setFkDocument(Integer fkDocument) {
this.fkDocument = fkDocument;
}
}
@@ -22,6 +22,14 @@ public class CampProfileBean implements Serializable {
@NotBlank
private String module;
public static final CampProfileBean of(Integer fkCamp, Integer fkProfile, String module) {
CampProfileBean bean = new CampProfileBean();
bean.setFkCamp(fkCamp);
bean.setFkProfile(fkProfile);
bean.setModule(module);
return bean;
}
/**
* @return the pk
*/
@@ -51,6 +51,13 @@ public class DocumentBean implements Serializable{
}
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(name).append(".").append(filetype);
return buf.toString();
}
public Integer getPk() {
return pk;
}
@@ -1,10 +1,11 @@
package de.jottyfan.camporganizer.module.business.bookings;
import jakarta.annotation.security.RolesAllowed;
import java.time.LocalDate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import de.jottyfan.camporganizer.module.business.bookings.model.AddPaymentBean;
import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;
import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.annotation.security.RolesAllowed;
/**
*
@@ -29,38 +31,41 @@ public class BookingsController extends CommonController {
@Autowired
private BookingsService bookingsService;
@GetMapping("/business/bookings")
@GetMapping("/business/bookings/{year}")
@RolesAllowed({"business_booking"})
public String getBookings(Model model) {
model.addAttribute("bookers", bookingsService.getBookers(getCurrentUser()));
public String getBookings(Model model, @Nullable @PathVariable("year") Integer year, @RequestParam(name = "search", defaultValue = "") String search) {
model.addAttribute("bookers", bookingsService.getBookers(getCurrentUser(), year));
model.addAttribute("addBean", new AddPaymentBean());
model.addAttribute("year", year == null ? LocalDate.now().getYear() : year);
model.addAttribute("years", bookingsService.getAllCampYears());
model.addAttribute("search", search);
return "business/bookings";
}
@GetMapping("/business/bookings/{id}")
@GetMapping("/business/bookings_by_id/{id}")
@RolesAllowed({"business_booking"})
public String getBooking(Model model, @PathVariable("id") Integer id) {
BookerBean bean = bookingsService.getBooker(id, getCurrentUser());
model.addAttribute("booker", bean);
model.addAttribute("addBean", new AddPaymentBean());
return bean == null ? getBookings(model) : "business/booker";
return bean == null ? getBookings(model, LocalDate.now().getYear(), null) : "business/booker";
}
@PostMapping("/business/bookings/payment/{id}")
@RolesAllowed({"business_booking"})
public String addPayment(Model model, @ModelAttribute("bean") AddPaymentBean bean, @PathVariable("id") Integer id) {
public String addPayment(Model model, @ModelAttribute("bean") AddPaymentBean bean, @PathVariable(name = "id") Integer id) {
Double payment = bean.getPayment();
bookingsService.addPayment(id, payment);
return getBooking(model, id);
}
@PostMapping("/business/bookings/listpayment/{id}")
@PostMapping("/business/bookings/listpayment/{id}/{year}")
@RolesAllowed({"business_booking"})
public String addListPayment(Model model, @ModelAttribute("bean") AddPaymentBean bean, @PathVariable("id") Integer id, @RequestParam(defaultValue = "") String search) {
public String addListPayment(Model model, @ModelAttribute("bean") AddPaymentBean bean, @PathVariable(name = "id") Integer id, @PathVariable(name = "year") Integer year, @RequestParam(name = "search", defaultValue = "") String search) {
Double payment = bean.getPayment();
bookingsService.addPayment(id, payment);
LOGGER.debug("search is {}", search);
model.addAttribute("search", search);
return getBookings(model);
return "redirect:/business/bookings/" + year + "?search=" + search;
}
}
@@ -1,8 +1,8 @@
package de.jottyfan.camporganizer.module.business.bookings;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
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.T_CAMPPROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.V_CAMP;
import java.math.BigDecimal;
@@ -15,11 +15,14 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Record10;
import org.jooq.Record13;
import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1;
import org.jooq.SelectSeekStep4;
import org.jooq.UpdateConditionStep;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@@ -27,6 +30,7 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.EnumConverter;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;
@@ -48,29 +52,32 @@ public class BookingsRepository {
* get a list of all registered persons that have booked the camp
*
* @param username the name of the current user in this session
* @param year the year of the camp; may be null
*
* @return a list of bookings; an empty one at least
*/
public List<BookerBean> getBookings(String username) {
SelectSeekStep4<Record10<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>, EnumCamprole, EnumSex, String, String> sql = jooq
public List<BookerBean> getBookings(String username, Integer year) {
SelectSeekStep4<Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>, EnumCamprole, EnumSex, String, String> sql = jooq
// @formatter:off
.select(T_PERSON.PK, T_PERSON.ACCEPT, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED, V_CAMP.NAME, V_CAMP.YEAR)
.select(T_PERSON.PK, T_PERSON.PROGRESS, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED, V_CAMP.NAME, V_CAMP.YEAR)
.from(T_PERSON)
.leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)).and(T_CAMPPROFILE.MODULE.eq(EnumModule.business))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.where(T_PROFILE.USERNAME.eq(username))
.and(year != null ? V_CAMP.YEAR.eq(year.doubleValue()) : DSL.trueCondition())
.orderBy(T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.SURNAME, T_PERSON.FORENAME);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<BookerBean> list = new ArrayList<>();
Iterator<Record10<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>> i = sql.fetch().iterator();
Iterator<Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>> i = sql.fetch().iterator();
while (i.hasNext()) {
Record10<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double> r = i.next();
Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double> r = i.next();
String forename = r.get(T_PERSON.FORENAME);
String surname = r.get(T_PERSON.SURNAME);
EnumCamprole role = r.get(T_PERSON.CAMPROLE);
EnumSex sex = r.get(T_PERSON.SEX);
EnumProgress progress = r.get(T_PERSON.PROGRESS);
String campName = r.get(V_CAMP.NAME);
Double campYear = r.get(V_CAMP.YEAR);
BookerBean bean = new BookerBean();
@@ -79,7 +86,7 @@ public class BookingsRepository {
bean.setRole(EnumConverter.role2GermanNames(role));
bean.setSex(EnumConverter.sex2GermanNames(sex));
bean.setBookingDate(r.get(T_PERSON.CREATED));
bean.setAccept(r.get(T_PERSON.ACCEPT));
bean.setProgress(progress == null ? null : progress.getLiteral());
bean.setPaid(r.get(T_PERSON.PAID));
bean.setCamp(String.format("%s %4.0f", campName, campYear));
list.add(bean);
@@ -95,10 +102,10 @@ public class BookingsRepository {
* @return the booker bean or null
*/
public BookerBean getBooking(Integer id, String username) {
SelectConditionStep<Record13<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, BigDecimal, String, Double, String, Integer>> sql = jooq
SelectConditionStep<Record13<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, BigDecimal, String, Double, String, Integer>> sql = jooq
// @formatter:off
.select(T_PERSON.PK,
T_PERSON.ACCEPT,
T_PERSON.PROGRESS,
T_PERSON.PAID,
T_PERSON.FORENAME,
T_PERSON.SURNAME,
@@ -117,7 +124,7 @@ public class BookingsRepository {
.where(T_PROFILE.USERNAME.eq(username))
.and(T_PERSON.PK.eq(id));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
Record r = sql.fetchOne();
if (r == null) {
return null;
@@ -126,6 +133,7 @@ public class BookingsRepository {
String surname = r.get(T_PERSON.SURNAME);
EnumCamprole role = r.get(T_PERSON.CAMPROLE);
EnumSex sex = r.get(T_PERSON.SEX);
EnumProgress progress = r.get(T_PERSON.PROGRESS);
BigDecimal requiredPrice = r.get(T_PERSON.REQUIRED_PRICE);
String campName = r.get(V_CAMP.NAME);
Double campYear = r.get(V_CAMP.YEAR);
@@ -136,7 +144,7 @@ public class BookingsRepository {
bean.setRole(EnumConverter.role2GermanNames(role));
bean.setSex(EnumConverter.sex2GermanNames(sex));
bean.setBookingDate(r.get(T_PERSON.CREATED));
bean.setAccept(r.get(T_PERSON.ACCEPT));
bean.setProgress(progress == null ? null : progress.getLiteral());
bean.setPaid(r.get(T_PERSON.PAID));
bean.setCamp(String.format("%s %4.0f", campName, campYear));
bean.setCampId(campId);
@@ -162,7 +170,23 @@ public class BookingsRepository {
.set(T_PERSON.PAID, value)
.where(T_PERSON.PK.eq(pk));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.execute();
}
/**
* get an ordered list of all camp years found in the database
*
* @return the list of camp years
*/
public List<Integer> getAllCampYears() {
SelectSeekStep1<Record1<Double>, Double> sql = jooq
// @formatter:off
.selectDistinct(V_CAMP.YEAR)
.from(V_CAMP)
.orderBy(V_CAMP.YEAR);
// @formatter:on
LOGGER.trace(sql);
return sql.fetchInto(Integer.class);
}
}
@@ -18,8 +18,8 @@ public class BookingsService {
@Autowired
private BookingsRepository bookingsGateway;
public List<BookerBean> getBookers(String username) {
return bookingsGateway.getBookings(username);
public List<BookerBean> getBookers(String username, Integer year) {
return bookingsGateway.getBookings(username, year);
}
public BookerBean getBooker(Integer id, String username) {
@@ -29,4 +29,8 @@ public class BookingsService {
public Integer addPayment(Integer id, Double payment) {
return bookingsGateway.addPayment(id, payment);
}
public List<Integer> getAllCampYears() {
return bookingsGateway.getAllCampYears();
}
}
@@ -10,13 +10,13 @@ import java.time.LocalDateTime;
*
*/
public class BookerBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2L;
private Integer pk;
private String name;
private String role;
private String sex;
private Boolean accept;
private String progress;
private LocalDateTime bookingDate;
private BigDecimal paid;
private String camp;
@@ -24,20 +24,6 @@ public class BookerBean implements Serializable {
private Integer campId;
private BigDecimal requiredPrice;
/**
* @return the accept
*/
public Boolean getAccept() {
return accept;
}
/**
* @param accept the accept to set
*/
public void setAccept(Boolean accept) {
this.accept = accept;
}
/**
* @return the paid
*/
@@ -141,4 +127,18 @@ public class BookerBean implements Serializable {
public void setRequiredPrice(BigDecimal requiredPrice) {
this.requiredPrice = requiredPrice;
}
/**
* @return the progress
*/
public String getProgress() {
return progress;
}
/**
* @param progress the progress to set
*/
public void setProgress(String progress) {
this.progress = progress;
}
}
@@ -65,7 +65,7 @@ public class BusinessRepository {
.where(T_PROFILE.USERNAME.eq(username))
.orderBy(T_CAMP.ARRIVE);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<CampBudgetBean> list = new ArrayList<>();
Iterator<Record4<BigDecimal, String, Double, Integer>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -105,7 +105,7 @@ public class BusinessRepository {
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.where(T_PROFILE.USERNAME.eq(username));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<BusinessBean> list = new ArrayList<>();
Iterator<Record9<Integer, String, String, String, String, LocalDateTime, String, BigDecimal, BigDecimal>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -140,7 +140,7 @@ public class BusinessRepository {
.set(T_PERSON.PAID, bean.getPaid())
.where(T_PERSON.PK.eq(bean.getFkPerson()));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
count += sql.execute();
}
}
@@ -25,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.EnumConverter;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.module.business.camp.model.CampBean;
import de.jottyfan.camporganizer.module.business.camp.model.PersonBean;
@@ -60,7 +61,7 @@ public class CampRepository {
.where(V_CAMP.PK.eq(pk))
.and(T_PROFILE.USERNAME.eq(username));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
CampBean bean = new CampBean();
Iterator<Record6<String, Double, LocalDateTime, LocalDateTime, String, String>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -84,9 +85,9 @@ public class CampRepository {
* @return a list of bookings; an empty one at least
*/
public List<PersonBean> getBookings(Integer pk, String username) {
SelectSeekStep4<Record8<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime>, EnumCamprole, EnumSex, String, String> sql = jooq
SelectSeekStep4<Record8<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime>, EnumCamprole, EnumSex, String, String> sql = jooq
// @formatter:off
.select(T_PERSON.PK, T_PERSON.ACCEPT, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED)
.select(T_PERSON.PK, T_PERSON.PROGRESS, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED)
.from(T_PERSON)
.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)).and(T_CAMPPROFILE.MODULE.eq(EnumModule.business))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
@@ -94,22 +95,23 @@ public class CampRepository {
.and(T_PROFILE.USERNAME.eq(username))
.orderBy(T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.SURNAME, T_PERSON.FORENAME);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<PersonBean> list = new ArrayList<>();
Iterator<Record8<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime>> i = sql.fetch().iterator();
Iterator<Record8<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime>> i = sql.fetch().iterator();
while (i.hasNext()) {
Record8<Integer, Boolean, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime> r = i.next();
Record8<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime> r = i.next();
String forename = r.get(T_PERSON.FORENAME);
String surname = r.get(T_PERSON.SURNAME);
EnumCamprole role = r.get(T_PERSON.CAMPROLE);
EnumSex sex = r.get(T_PERSON.SEX);
EnumProgress progress = r.get(T_PERSON.PROGRESS);
PersonBean bean = new PersonBean();
bean.setPk(r.get(T_PERSON.PK));
bean.setName(String.format("%s %s", forename, surname));
bean.setRole(EnumConverter.role2GermanNames(role));
bean.setSex(EnumConverter.sex2GermanNames(sex));
bean.setBookingDate(r.get(T_PERSON.CREATED));
bean.setAccept(r.get(T_PERSON.ACCEPT));
bean.setProgress(progress == null ? null : progress.getLiteral());
bean.setPaid(r.get(T_PERSON.PAID));
list.add(bean);
}
@@ -6,6 +6,7 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.module.business.camp.model.BookingBean;
import de.jottyfan.camporganizer.module.business.camp.model.CampBean;
import de.jottyfan.camporganizer.module.business.camp.model.PersonBean;
@@ -31,12 +32,12 @@ public class CampService {
Integer rejected = 0;
BigDecimal paid = new BigDecimal(0);
for (PersonBean p : campGateway.getBookings(id, username)) {
Boolean acceptence = p.getAccept();
if (acceptence == null) {
String progress = p.getProgress();
if (EnumProgress.requested.getLiteral().equals(progress)) {
open += 1;
} else if (acceptence) {
} else if (EnumProgress.approved.getLiteral().equals(progress)) {
approved += 1;
} else {
} else if (EnumProgress.rejected.getLiteral().equals(progress)) {
rejected += 1;
}
paid = paid.add(p.getPaid() == null ? new BigDecimal(0) : p.getPaid());
@@ -16,24 +16,10 @@ public class PersonBean implements Serializable {
private String name;
private String role;
private String sex;
private Boolean accept;
private String progress;
private LocalDateTime bookingDate;
private BigDecimal paid;
/**
* @return the accept
*/
public Boolean getAccept() {
return accept;
}
/**
* @param accept the accept to set
*/
public void setAccept(Boolean accept) {
this.accept = accept;
}
/**
* @return the paid
*/
@@ -99,4 +85,18 @@ public class PersonBean implements Serializable {
public void setBookingDate(LocalDateTime bookingDate) {
this.bookingDate = bookingDate;
}
/**
* @return the progress
*/
public String getProgress() {
return progress;
}
/**
* @param progress the progress to set
*/
public void setProgress(String progress) {
this.progress = progress;
}
}
@@ -3,6 +3,10 @@ package de.jottyfan.camporganizer.module.business.outlay;
import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@@ -15,6 +19,7 @@ import de.jottyfan.camporganizer.module.business.outlay.model.OutlayBean;
import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.core.HttpHeaders;
/**
*
@@ -30,7 +35,8 @@ public class OutlayController extends CommonController {
@GetMapping("/business/outlay")
public String getOutlayDashboard(Model model, Principal principal) {
model.addAttribute("list", service.getListOfUser(super.getCurrentUser(principal)));
model.addAttribute("list", service.getListOf(super.getCurrentUser(principal), null));
model.addAttribute("camps", service.getAllCamps());
return "/business/outlay/list";
}
@@ -63,4 +69,19 @@ public class OutlayController extends CommonController {
service.deleteIfAllowedFor(super.getCurrentUser(principal), id);
return "redirect:/business/outlay";
}
@GetMapping("/business/outlay/summary/{campid}")
public String getSummaryOfCamp(@PathVariable("campid") Integer campId, Model model, Principal principal) {
model.addAttribute("campid", campId);
model.addAttribute("list", service.getListOf(null, campId));
return "/business/outlay/summary";
}
@GetMapping("/business/outlay/download/{campid}")
public ResponseEntity<Resource> generateCsv(@PathVariable("campid") Integer campId) {
String filename = String.format("rechnung_camp_%d.csv", campId);
InputStreamResource file = new InputStreamResource(service.getCsv(campId));
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
.contentType(MediaType.parseMediaType("application/csv")).body(file);
}
}
@@ -16,11 +16,12 @@ import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.InsertValuesStep8;
import org.jooq.Record10;
import org.jooq.Record11;
import org.jooq.Record4;
import org.jooq.Record7;
import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1;
import org.jooq.UpdateConditionStep;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@@ -68,8 +69,8 @@ public class OutlayRepository {
return list;
}
public List<OutlayBean> getListOf(String username) {
SelectConditionStep<Record7<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime>> sql = jooq
public List<OutlayBean> getListOf(String username, Integer campId) {
SelectConditionStep<Record11<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime, String, String, String, String>> sql = jooq
// @formatter:off
.select(T_SALES.PK,
T_SALES.TRADER,
@@ -77,26 +78,35 @@ public class OutlayRepository {
T_CAMP.ARRIVE,
T_LOCATION.NAME,
T_SALES.CASH,
T_SALES.BUYDATE)
T_SALES.BUYDATE,
T_SALES.PROVIDER,
T_SALES.RECIPENUMBER,
T_SALES.INCREDIENTS,
T_SALES.RECIPENOTE)
.from(T_SALES)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_SALES.FK_CAMP))
.leftJoin(T_LOCATION).on(T_LOCATION.PK.eq(T_CAMP.FK_LOCATION))
.where(T_SALES.PROVIDER.eq(username));
.where(username == null ? DSL.trueCondition() : T_SALES.PROVIDER.eq(username))
.and(campId == null ? DSL.trueCondition() : T_SALES.FK_CAMP.eq(campId));
// @formatter:on
LOGGER.trace(sql);
List<OutlayBean> list = new ArrayList<>();
Iterator<Record7<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime>> i = sql.fetch()
Iterator<Record11<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime, String, String, String, String>> i = sql.fetch()
.iterator();
while (i.hasNext()) {
Record7<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime> r = i.next();
Record11<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime, String, String, String, String> r = i.next();
String campname = String
.format("%s %s %d", r.get(T_CAMP.NAME), r.get(T_LOCATION.NAME), r.get(T_CAMP.ARRIVE).getYear()).trim();
OutlayBean bean = new OutlayBean();
bean.setId(r.get(T_SALES.PK));
bean.setTrader(r.get(T_SALES.TRADER));
bean.setCampname(campname);
bean.setProvider(r.get(T_SALES.PROVIDER));
bean.setCash(r.get(T_SALES.CASH));
bean.setBuydate(r.get(T_SALES.BUYDATE));
bean.setRecipenumber(r.get(T_SALES.RECIPENUMBER));
bean.setIngredients(r.get(T_SALES.INCREDIENTS));
bean.setRecipenote(r.get(T_SALES.RECIPENOTE));
list.add(bean);
}
return list;
@@ -1,5 +1,7 @@
package de.jottyfan.camporganizer.module.business.outlay;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,8 +22,8 @@ public class OutlayService {
@Autowired
private OutlayRepository repository;
public List<OutlayBean> getListOfUser(String username) {
return repository.getListOf(username);
public List<OutlayBean> getListOf(String username, Integer campId) {
return repository.getListOf(username, campId);
}
public List<CampBean> getAllCamps() {
@@ -43,4 +45,13 @@ public class OutlayService {
public void deleteIfAllowedFor(String username, Integer id) {
repository.deleteBeanIfAllowedFor(username, id);
}
public InputStream getCsv(Integer campId) {
List<OutlayBean> list = repository.getListOf(null, campId);
StringBuilder buf = new StringBuilder("Beleg-Nummer;Einkauf bei;Freizeit;bezahlt von;Betrag;Bestandteile;Rechnungsdatum\n");
for (OutlayBean bean : list) {
buf.append(bean.toCsvLine());
}
return new ByteArrayInputStream(buf.toString().getBytes());
}
}
@@ -2,7 +2,9 @@ package de.jottyfan.camporganizer.module.business.outlay.model;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.time.LocalDateTime;
import java.util.Locale;
import org.springframework.format.annotation.DateTimeFormat;
@@ -38,6 +40,26 @@ public class OutlayBean implements Serializable {
return this;
}
private static final String quoted(String s) {
return s == null ? "" : String.format("\"%s\"", s.replace("\"", "'"));
}
private static final String cashed(BigDecimal money) {
return money == null ? "" : String.format("%s €", NumberFormat.getNumberInstance(Locale.GERMAN).format(money));
}
public String toCsvLine() {
StringBuilder buf = new StringBuilder();
buf.append(recipenumber).append(";");
buf.append(quoted(trader)).append(";");
buf.append(quoted(campname)).append(";");
buf.append(quoted(provider)).append(";");
buf.append(cashed(cash)).append(";");
buf.append(quoted(ingredients)).append(";");
buf.append(buydate).append("\n");
return buf.toString();
}
/**
* @return the id
*/
@@ -50,7 +50,7 @@ public class BusinessPrivilegesRepository {
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.orderBy(V_CAMP.ARRIVE, T_PROFILE.SURNAME, T_PROFILE.FORENAME);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<BusinessPrivilegesBean> list = new ArrayList<>();
for (Record8<String, String, LocalDateTime, String, Integer, Integer, String, Double> r : sql.fetch()) {
BusinessPrivilegesBean bean = new BusinessPrivilegesBean();
@@ -80,7 +80,7 @@ public class BusinessPrivilegesRepository {
.where((allowed == null || allowed.size() < 1) ? DSL.trueCondition() : T_PERSON.CAMPROLE.in(allowed))
.orderBy(T_PROFILE.SURNAME, T_PROFILE.FORENAME, T_PROFILE.DUEDATE);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<ProfileBean> list = new ArrayList<>();
for (Record5<Integer, String, String, LocalDateTime, String> r : sql.fetch()) {
ProfileBean bean = new ProfileBean();
@@ -102,7 +102,7 @@ public class BusinessPrivilegesRepository {
.onConflict(T_CAMPPROFILE.FK_CAMP, T_CAMPPROFILE.FK_PROFILE, T_CAMPPROFILE.MODULE)
.doNothing();
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.execute();
}
@@ -118,7 +118,7 @@ public class BusinessPrivilegesRepository {
.from(T_PROFILE)
.where(T_PROFILE.USERNAME.eq(currentUser))));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.execute();
}
}
@@ -34,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.tables.TProfile;
import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord;
import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
@@ -54,7 +55,7 @@ public class CamplistRepository {
public Stream<VCampRecord> getAllCamps(Condition condition) {
SelectSeekStep1<VCampRecord, LocalDateTime> sql = jooq.selectFrom(V_CAMP).where(condition).orderBy(V_CAMP.ARRIVE);
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.fetchStream();
}
@@ -76,7 +77,7 @@ public class CamplistRepository {
T_PERSON.CREATED,
T_PERSON.EMAIL,
T_PERSON.SEX,
T_PERSON.ACCEPT,
T_PERSON.PROGRESS,
T_PERSON.FK_CAMP,
T_PROFILE.FORENAME,
T_PROFILE.SURNAME,
@@ -99,13 +100,15 @@ public class CamplistRepository {
.and(T_PERSON.PK.isNotNull())
.orderBy(V_CAMP.ARRIVE.desc(), T_PERSON.CREATED);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<BookingBean> list = new ArrayList<>();
Iterator<Record> i = sql.fetch().iterator();
while (i.hasNext()) {
Record r = i.next();
Integer fkCamp = r.get(T_PERSON.FK_CAMP);
BookingBean bean = new BookingBean();
EnumProgress progress = r.get(T_PERSON.PROGRESS);
bean.setProgress(progress == null ? null : progress.getLiteral());
bean.setPk(r.get(T_PERSON.PK));
bean.setForename(r.get(T_PERSON.FORENAME));
bean.setSurname(r.get(T_PERSON.SURNAME));
@@ -129,7 +132,6 @@ public class CamplistRepository {
bean.setUrl(r.get(V_CAMP.URL));
bean.setIsOver(r.get(V_CAMP.IS_OVER));
bean.setCampName(r.get(V_CAMP.NAME));
bean.setAccept(r.get(T_PERSON.ACCEPT));
StringBuilder buf = new StringBuilder();
String forename = r.get(REGISTRATOR.FORENAME);
String surname = r.get(REGISTRATOR.SURNAME);
@@ -185,7 +187,7 @@ public class CamplistRepository {
.from(T_DOCUMENT)
.where(T_DOCUMENT.DOCTYPE.eq(EnumDocument.camppass)));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
Map<Integer, String> map = new HashMap<>(); // no duplicate on using a map
Iterator<Record2<String, Integer>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -41,7 +41,7 @@ public class BookingBean implements Serializable {
private Boolean isOver;
private String campName;
private String registrator;
private Boolean accept;
private String progress;
private String subscriber;
private List<DocumentBean> documents;
@@ -396,14 +396,6 @@ public class BookingBean implements Serializable {
this.registrator = registrator;
}
public Boolean getAccept() {
return accept;
}
public void setAccept(Boolean accept) {
this.accept = accept;
}
public String getSubscriber() {
return subscriber;
}
@@ -416,4 +408,18 @@ public class BookingBean implements Serializable {
return documents;
}
/**
* @return the progress
*/
public String getProgress() {
return progress;
}
/**
* @param progress the progress to set
*/
public void setProgress(String progress) {
this.progress = progress;
}
}
@@ -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;
}
}
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.module.confirmation.board.model.CampBean;
import de.jottyfan.camporganizer.module.confirmation.board.model.PersonBean;
@@ -93,7 +94,8 @@ public class ConfirmationBoardRepository {
while (i.hasNext()) {
Record r = i.next();
PersonBean bean = new PersonBean();
bean.setAccept(r.get(T_PERSON.ACCEPT));
EnumProgress progress = r.get(T_PERSON.PROGRESS);
bean.setProgress(progress == null ? null : progress.getLiteral());
bean.setBirthDate(r.get(T_PERSON.BIRTHDATE));
bean.setCamprole(r.get(T_PERSON.CAMPROLE) == null ? null : r.get(T_PERSON.CAMPROLE).getLiteral());
bean.setCity(r.get(T_PERSON.CITY));
@@ -61,7 +61,7 @@ public class List2CSVService {
append(buf, bean.getSex());
append(buf, bean.getCamprolle());
append(buf, bean.getBirthDate());
append(buf, bean.getAccept());
append(buf, bean.getProgress());
append(buf, bean.getCreated());
append(buf, bean.getConsentCatalogPhoto());
append(buf, bean.getComment());
@@ -22,7 +22,7 @@ public class PersonBean implements Serializable {
private String email;
private LocalDate birthDate;
private String camprole;
private Boolean accept;
private String progress;
private LocalDateTime created;
private String sex;
private Double paid;
@@ -181,20 +181,6 @@ public class PersonBean implements Serializable {
this.camprole = camprole;
}
/**
* @return the accept
*/
public Boolean getAccept() {
return accept;
}
/**
* @param accept the accept to set
*/
public void setAccept(Boolean accept) {
this.accept = accept;
}
/**
* @return the created
*/
@@ -264,4 +250,18 @@ public class PersonBean implements Serializable {
public void setConsentCatalogPhoto(Boolean consentCatalogPhoto) {
this.consentCatalogPhoto = consentCatalogPhoto;
}
/**
* @return the progress
*/
public String getProgress() {
return progress;
}
/**
* @param progress the progress to set
*/
public void setProgress(String progress) {
this.progress = progress;
}
}
@@ -7,6 +7,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.PathVariable;
import de.jottyfan.camporganizer.module.camplist.CommonController;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOverviewBean;
@@ -20,22 +21,32 @@ import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOver
public class ConfirmationController extends CommonController {
@Autowired
private ConfirmationService indexService;
private ConfirmationService service;
@GetMapping("/confirmation")
public String getIndex(Model model) {
List<CampOverviewBean> campoverview = indexService.getCampOverview(super.getCurrentUser());
List<CampOverviewBean> campoverview = service.getCampOverview(super.getCurrentUser());
CampOverviewBean campoverviewsummary = new CampOverviewBean(LocalDate.now(), "summary");
for (CampOverviewBean bean : campoverview) {
campoverviewsummary.setApproved(bean.getApproved() + campoverviewsummary.getApproved());
campoverviewsummary.setRejected(bean.getRejected() + campoverviewsummary.getRejected());
campoverviewsummary.setUntouched(bean.getUntouched() + campoverviewsummary.getUntouched());
campoverviewsummary.setRevoked(bean.getRevoked() + campoverviewsummary.getRevoked());
}
model.addAttribute("campoverview", campoverview);
model.addAttribute("campoverviewsummary", campoverviewsummary);
model.addAttribute("untouched", indexService.getUntouched(super.getCurrentUser()));
model.addAttribute("approved", indexService.getApproved(super.getCurrentUser()));
model.addAttribute("rejected", indexService.getRejected(super.getCurrentUser()));
model.addAttribute("untouched", service.getUntouched(super.getCurrentUser()));
model.addAttribute("approved", service.getApproved(super.getCurrentUser()));
model.addAttribute("rejected", service.getRejected(super.getCurrentUser()));
model.addAttribute("revoked", service.getRevoked(super.getCurrentUser()));
return "confirmation/confirmation";
}
@GetMapping("/registration/remove/{id}")
public String revoke(@PathVariable("id") Integer id, final Model model) {
service.removeBooking(id);
return "redirect:/confirmation";
}
}
@@ -3,10 +3,13 @@ package de.jottyfan.camporganizer.module.confirmation.confirmation;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSONDOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_RSS;
import static de.jottyfan.camporganizer.db.jooq.Tables.V_CAMP;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -18,12 +21,17 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.InsertValuesStep2;
import org.jooq.Name;
import org.jooq.Record4;
import org.jooq.Record5;
import org.jooq.Record7;
import org.jooq.Record8;
import org.jooq.SelectConditionStep;
import org.jooq.SelectHavingStep;
import org.jooq.SelectSeekStep1;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@@ -31,6 +39,11 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersondocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord;
import de.jottyfan.camporganizer.module.camplist.model.LambdaResultWrapper;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.BookingBean;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOverviewBean;
@@ -55,10 +68,10 @@ public class ConfirmationRepository {
*/
public List<CampOverviewBean> getCampOverviewBeans(String currentUser) {
Name COUNT = DSL.name("count");
SelectHavingStep<Record4<Integer, Boolean, String, LocalDateTime>> sql = jooq
SelectHavingStep<Record4<Integer, EnumProgress, String, LocalDateTime>> sql = jooq
// @formatter:off
.select(DSL.count(T_PERSON.PK).as(COUNT),
T_PERSON.ACCEPT,
T_PERSON.PROGRESS,
T_CAMP.NAME,
T_CAMP.ARRIVE)
.from(T_PERSON)
@@ -68,15 +81,15 @@ public class ConfirmationRepository {
.where(T_CAMP.ARRIVE.isNotNull())
.and(T_CAMPPROFILE.MODULE.eq(EnumModule.registration))
.and(T_PROFILE.USERNAME.eq(currentUser))
.groupBy(T_CAMP.NAME, T_CAMP.ARRIVE, T_PERSON.ACCEPT);
.groupBy(T_CAMP.NAME, T_CAMP.ARRIVE, T_PERSON.PROGRESS);
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
Map<LocalDateTime, CampOverviewBean> map = new HashMap<>();
Iterator<Record4<Integer, Boolean, String, LocalDateTime>> i = sql.fetch().iterator();
Iterator<Record4<Integer, EnumProgress, String, LocalDateTime>> i = sql.fetch().iterator();
while (i.hasNext()) {
Record4<Integer, Boolean, String, LocalDateTime> r = i.next();
Record4<Integer, EnumProgress, String, LocalDateTime> r = i.next();
Integer count = r.get(COUNT, Integer.class);
Boolean accept = r.get(T_PERSON.ACCEPT);
EnumProgress progress = r.get(T_PERSON.PROGRESS);
String campname = r.get(T_CAMP.NAME);
LocalDateTime arrive = r.get(T_CAMP.ARRIVE);
CampOverviewBean bean = map.get(arrive);
@@ -84,12 +97,14 @@ public class ConfirmationRepository {
bean = new CampOverviewBean(arrive.toLocalDate(), campname);
map.put(arrive, bean);
}
if (accept == null) {
if (progress == null || EnumProgress.requested.equals(progress)) {
bean.setUntouched(count);
} else if (accept) {
} else if (EnumProgress.approved.equals(progress)) {
bean.setApproved(count);
} else {
} else if (EnumProgress.rejected.equals(progress)) {
bean.setRejected(count);
} else if (EnumProgress.revoked.equals(progress)) {
bean.setRevoked(count);
}
}
List<CampOverviewBean> list = new ArrayList<>(map.values());
@@ -117,7 +132,7 @@ public class ConfirmationRepository {
.and(T_PROFILE.USERNAME.eq(currentUser))
.orderBy(T_PERSON.CREATED.desc());
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<BookingBean> list = new ArrayList<>();
Iterator<Record7<Integer, String, String, String, LocalDateTime, EnumCamprole, LocalDateTime>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -145,7 +160,7 @@ public class ConfirmationRepository {
* @return a list of booking beans; an empty one at least
*/
public List<BookingBean> getUntouched(String currentUser) {
return getListWithCondition(currentUser, T_PERSON.ACCEPT.isNull());
return getListWithCondition(currentUser, T_PERSON.PROGRESS.eq(EnumProgress.requested));
}
/**
@@ -155,7 +170,7 @@ public class ConfirmationRepository {
* @return a list of booking beans; an empty one at least
*/
public List<BookingBean> getApproved(String currentUser) {
return getListWithCondition(currentUser, T_PERSON.ACCEPT.isTrue());
return getListWithCondition(currentUser, T_PERSON.PROGRESS.eq(EnumProgress.approved));
}
/**
@@ -165,7 +180,17 @@ public class ConfirmationRepository {
* @return a list of booking beans; an empty one at least
*/
public List<BookingBean> getRejected(String currentUser) {
return getListWithCondition(currentUser, T_PERSON.ACCEPT.isFalse());
return getListWithCondition(currentUser, T_PERSON.PROGRESS.eq(EnumProgress.rejected));
}
/**
* get all revoked bookings that this user can manage
*
* @param currentUser the current user
* @return a list of booking beans; an empty one at least
*/
public List<BookingBean> getRevoked(String currentUser) {
return getListWithCondition(currentUser, T_PERSON.PROGRESS.eq(EnumProgress.revoked));
}
/**
@@ -183,9 +208,9 @@ public class ConfirmationRepository {
.or(V_CAMP.YEAR.cast(String.class).containsIgnoreCase(needle));
// @formatter:on
SelectSeekStep1<Record8<Integer, String, String, EnumCamprole, String, Double, String, Boolean>, LocalDateTime> sql = jooq
SelectSeekStep1<Record8<Integer, String, String, EnumCamprole, String, Double, String, EnumProgress>, LocalDateTime> sql = jooq
// @formatter:off
.select(T_PERSON.PK, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, V_CAMP.NAME, V_CAMP.YEAR, V_CAMP.LOCATION_NAME, T_PERSON.ACCEPT)
.select(T_PERSON.PK, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, V_CAMP.NAME, V_CAMP.YEAR, V_CAMP.LOCATION_NAME, T_PERSON.PROGRESS)
.from(T_PERSON)
.leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP))
@@ -195,24 +220,82 @@ public class ConfirmationRepository {
.and(T_PROFILE.USERNAME.eq(currentUser))
.orderBy(T_PERSON.CREATED.desc());
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<BookingBean> list = new ArrayList<>();
Iterator<Record8<Integer, String, String, EnumCamprole, String, Double, String, Boolean>> i = sql.fetch().iterator();
Iterator<Record8<Integer, String, String, EnumCamprole, String, Double, String, EnumProgress>> i = sql.fetch().iterator();
while (i.hasNext()) {
Record8<Integer, String, String, EnumCamprole, String, Double, String, Boolean> r = i.next();
Record8<Integer, String, String, EnumCamprole, String, Double, String, EnumProgress> r = i.next();
Integer pkPerson = r.get(T_PERSON.PK);
String forename = r.get(T_PERSON.FORENAME);
String surname = r.get(T_PERSON.SURNAME);
EnumCamprole role = r.get(T_PERSON.CAMPROLE);
String campname = r.get(V_CAMP.NAME);
Double year = r.get(V_CAMP.YEAR);
Boolean accept = r.get(T_PERSON.ACCEPT);
EnumProgress progress = r.get(T_PERSON.PROGRESS);
BookingBean bean = new BookingBean(pkPerson, null, String.format("%s %4.0f", campname, year));
bean.setRole(role == null ? null : role.getLiteral());
bean.setFullname(new StringBuilder().append(forename).append(" ").append(surname).toString());
bean.setAccept(accept);
bean.setProgress(progress == null ? null : progress.getLiteral());
list.add(bean);
}
return list;
}
/**
* remove the booking and all of its dependencies
*
* @param id the pk of t_person
* @return number of affected database rows, should be 1
*/
public Integer removeBooking(Integer id) {
LambdaResultWrapper lrw = new LambdaResultWrapper();
jooq.transaction(t -> {
SelectConditionStep<Record5<String, String, String, String, LocalDateTime>> sql0 = DSL.using(t)
// @formatter:off
.select(T_PROFILE.USERNAME, T_PERSON.FORENAME, T_PERSON.SURNAME, T_CAMP.NAME, T_CAMP.ARRIVE)
.from(T_PERSON)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_PERSON.FK_PROFILE))
.where(T_PERSON.PK.eq(id));
// @formatter:on
LOGGER.trace(sql0.toString());
Record5<String, String, String, String, LocalDateTime> r = sql0.fetchOne();
if (r == null) {
throw new DataAccessException("no such entry in t_person with id = " + id);
}
String username = r.get(T_PROFILE.USERNAME);
String forename = r.get(T_PERSON.FORENAME);
String surname = r.get(T_PERSON.SURNAME);
String campname = r.get(T_CAMP.NAME);
LocalDateTime arrive = r.get(T_CAMP.ARRIVE);
StringBuilder rssMessage = new StringBuilder(username);
rssMessage.append(" hat die Stornierung der Buchung von ");
rssMessage.append(forename).append(" ").append(surname);
rssMessage.append(" an ");
rssMessage.append(campname).append(" ")
.append(arrive == null ? "" : arrive.format(DateTimeFormatter.ofPattern("YYYY")));
rssMessage.append(" bestätigt und den Datensatz gelöscht.");
DeleteConditionStep<TPersondocumentRecord> sql1 = DSL.using(t).deleteFrom(T_PERSONDOCUMENT)
.where(T_PERSONDOCUMENT.FK_PERSON.eq(id));
LOGGER.trace(sql1.toString());
sql1.execute();
DeleteConditionStep<TPersonRecord> sql2 = DSL.using(t).deleteFrom(T_PERSON).where(T_PERSON.PK.eq(id));
LOGGER.trace(sql2.toString());
lrw.add(sql2.execute());
InsertValuesStep2<TRssRecord, String, String> sql3 = DSL.using(t)
// @formatter:off
.insertInto(T_RSS,
T_RSS.MSG,
T_RSS.RECIPIENT)
.values(rssMessage.toString(), "registrator");
// @formatter:on
LOGGER.trace("{}", sql3.toString());
sql3.execute();
});
return lrw.getCounter();
}
}
@@ -5,6 +5,7 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.BookingBean;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOverviewBean;
@@ -16,35 +17,41 @@ import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOver
@Service
public class ConfirmationService {
@Autowired
private ConfirmationRepository gateway;
private ConfirmationRepository repository;
public List<CampOverviewBean> getCampOverview(String currentUser) {
return gateway.getCampOverviewBeans(currentUser);
return repository.getCampOverviewBeans(currentUser);
}
public List<BookingBean> getUntouched(String currentUser) {
return gateway.getUntouched(currentUser);
return repository.getUntouched(currentUser);
}
public List<BookingBean> getApproved(String currentUser) {
return gateway.getApproved(currentUser);
return repository.getApproved(currentUser);
}
public List<BookingBean> getRejected(String currentUser) {
return gateway.getRejected(currentUser);
return repository.getRejected(currentUser);
}
public List<BookingBean> getRevoked(String currentUser) {
return repository.getRevoked(currentUser);
}
public String search(String needle, String linkURL, String currentUser) {
StringBuilder buf = new StringBuilder(
"<table class=\"table table-striped\"><thead><tr><th>Dabei</th><th>Name</th><th>Freizeit</th><th>Rolle</th></tr><tbody>");
for (BookingBean bean : gateway.getSearchResult(needle, currentUser)) {
for (BookingBean bean : repository.getSearchResult(needle, currentUser)) {
String acceptHtml = "";
if (bean.getAccept() == null) {
if (EnumProgress.requested.equals(bean.getProgress())) {
acceptHtml = "<i class=\"fas fa-question framed framed-orange\"></i>";
} else if (bean.getAccept()) {
} else if (EnumProgress.approved.equals(bean.getProgress())) {
acceptHtml = "<i class=\"fas fa-check framed framed-green\"></i>";
} else {
} else if (EnumProgress.rejected.equals(bean.getProgress())) {
acceptHtml = "<i class=\"fas fa-ban framed framed-red\"></i>";
} else if (EnumProgress.revoked.equals(bean.getProgress())) {
acceptHtml = "<i class=\"fas fa-skull framed framed-pink\"></i>";
}
buf.append(String.format("<tr><td>%s</td><td><a href=\"%s/%d\">%s</a></td><td>%s</td><td>%s</td></tr>",
acceptHtml, linkURL, bean.getPkPerson(), bean.getFullname(), bean.getCamp(), bean.getRolename()));
@@ -52,4 +59,14 @@ public class ConfirmationService {
buf.append("</tbody></table>");
return buf.toString();
}
/**
* remove the booking and all of its dependencies
*
* @param id the id of the booking (t_person.pk)
*/
public Boolean removeBooking(Integer id) {
return repository.removeBooking(id) > 0;
}
}
@@ -20,7 +20,7 @@ public class BookingBean implements Serializable, Comparable<BookingBean> {
private String fullname;
private String role;
private LocalDateTime registered;
private Boolean accept;
private String progress;
public BookingBean(Integer pkPerson, LocalDate date, String camp) {
this.pkPerson = pkPerson;
@@ -114,11 +114,17 @@ public class BookingBean implements Serializable, Comparable<BookingBean> {
return pkPerson;
}
public Boolean getAccept() {
return accept;
/**
* @return the progress
*/
public String getProgress() {
return progress;
}
public void setAccept(Boolean accept) {
this.accept = accept;
/**
* @param progress the progress to set
*/
public void setProgress(String progress) {
this.progress = progress;
}
}
@@ -16,6 +16,7 @@ public class CampOverviewBean implements Serializable, Comparable<CampOverviewBe
private Integer approved;
private Integer rejected;
private Integer untouched;
private Integer revoked;
public CampOverviewBean(LocalDate date, String camp) {
this.date = date;
@@ -23,6 +24,7 @@ public class CampOverviewBean implements Serializable, Comparable<CampOverviewBe
this.approved = 0;
this.rejected = 0;
this.untouched = 0;
this.revoked = 0;
}
@Override
@@ -85,4 +87,18 @@ public class CampOverviewBean implements Serializable, Comparable<CampOverviewBe
public LocalDate getDate() {
return date;
}
/**
* @return the revoked
*/
public Integer getRevoked() {
return revoked;
}
/**
* @param revoked the revoked to set
*/
public void setRevoked(Integer revoked) {
this.revoked = revoked;
}
}
@@ -32,6 +32,7 @@ public class PersonController extends CommonController {
model.addAttribute("camps", personService.getCamps(username));
model.addAttribute("annotations", personService.getAnnotations(pk));
model.addAttribute("campPrice", personService.getCampPrice(pk));
model.addAttribute("progresses", personService.getProgresses());
return "confirmation/person";
}
@@ -43,6 +44,7 @@ public class PersonController extends CommonController {
model.addAttribute("camps", personService.getCamps(username));
model.addAttribute("annotations", personService.getAnnotations(bean.getPk()));
model.addAttribute("campPrice", personService.getCampPrice(bean.getPk()));
model.addAttribute("progresses", personService.getProgresses());
return "confirmation/person";
}
personService.updatePerson(bean, username);
@@ -33,6 +33,7 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.tables.TProfile;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
@@ -78,7 +79,7 @@ public class PersonRepository {
.and(T_PROFILE.USERNAME.eq(username))
.orderBy(T_CAMP.ARRIVE.desc());
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
List<CampBean> list = new ArrayList<>();
Iterator<Record4<Integer, String, LocalDateTime, String>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -112,13 +113,14 @@ public class PersonRepository {
.and(T_CAMPPROFILE.MODULE.eq(EnumModule.registration))
.and(T_PROFILE.USERNAME.eq(username));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
Iterator<Record> i = sql.fetch().iterator();
while (i.hasNext()) {
Record r = i.next();
PersonBean bean = new PersonBean();
bean.setPk(pk);
bean.setAccept(r.get(T_PERSON.ACCEPT));
EnumProgress progress = r.get(T_PERSON.PROGRESS);
bean.setProgress(progress == null ? null : progress.getLiteral());
bean.setBirthdate(r.get(T_PERSON.BIRTHDATE));
bean.setCamprole(r.get(T_PERSON.CAMPROLE));
bean.setCity(r.get(T_PERSON.CITY));
@@ -151,21 +153,21 @@ public class PersonRepository {
jooq.transaction(t -> {
SelectConditionStep<TProfileRecord> sql0 = jooq.selectFrom(T_PROFILE).where(T_PROFILE.USERNAME.eq(registrator));
LOGGER.debug(sql0.toString());
LOGGER.trace(sql0);
Integer fkRegistrator = sql0.fetchOne(T_PROFILE.PK);
// get old accept value for comparison
SelectConditionStep<TPersonRecord> sql1 = jooq.selectFrom(T_PERSON).where(T_PERSON.PK.eq(bean.getPk()));
LOGGER.debug(sql1.toString());
LOGGER.trace(sql1);
TPersonRecord r = sql1.fetchOne();
lrw.putBoolean("acceptOld", r == null ? null : r.getAccept());
lrw.putBoolean("acceptNew", bean.getAccept());
lrw.putBoolean("acceptOld", r == null ? null : EnumProgress.approved.equals(r.getProgress()));
lrw.putBoolean("acceptNew", EnumProgress.approved.getLiteral().equals(bean.getProgress()));
Integer fkCamp = r == null ? null : r.getFkCamp();
String email = r.getEmail(); // use the old one, too
lrw.putString("oldEmail", email);
SelectConditionStep<TCampRecord> sql2 = jooq.selectFrom(T_CAMP).where(T_CAMP.PK.eq(fkCamp));
LOGGER.debug(sql2.toString());
LOGGER.trace(sql2);
TCampRecord rc = sql2.fetchOne();
String campName = rc == null ? null : rc.getName();
LocalDateTime arrive = rc == null ? null : rc.getArrive();
@@ -186,13 +188,13 @@ public class PersonRepository {
.set(T_PERSON.PHONE, bean.getPhone())
.set(T_PERSON.EMAIL, bean.getEmail())
.set(T_PERSON.COMMENT, bean.getComment())
.set(T_PERSON.ACCEPT, bean.getAccept())
.set(T_PERSON.PROGRESS, EnumProgress.lookupLiteral(bean.getProgress()))
.set(T_PERSON.CAMPROLE, bean.getCamprole())
.set(T_PERSON.FK_REGISTRATOR, fkRegistrator)
.set(T_PERSON.REQUIRED_PRICE, bean.getRequiredPrice())
.where(T_PERSON.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug(sql3.toString());
LOGGER.trace(sql3);
lrw.add(sql3.execute());
// always
@@ -208,7 +210,7 @@ public class PersonRepository {
T_RSS.RECIPIENT)
.values(buf.toString(), "registrator");
// @formatter:on
LOGGER.debug("{}", sql4.toString());
LOGGER.trace(sql4);
sql4.execute();
});
@@ -301,7 +303,7 @@ public class PersonRepository {
.leftJoin(REGISTRATOR).on(REGISTRATOR.PK.eq(T_PERSON.FK_REGISTRATOR))
.where(T_PERSON.PK.eq(pk));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
StringBuilder buf = new StringBuilder();
Iterator<Record11<LocalDate, LocalDateTime, EnumCamprole, LocalDateTime, LocalDateTime, Integer, Integer, String, String, String, String>> i = sql
.fetch().iterator();
@@ -1,10 +1,12 @@
package de.jottyfan.camporganizer.module.confirmation.person;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.module.confirmation.person.model.CampBean;
import de.jottyfan.camporganizer.module.confirmation.person.model.PersonBean;
@@ -43,4 +45,12 @@ public class PersonService {
public String getCampPrice(Integer pk) {
return gateway.getCampprice(pk);
}
public List<String> getProgresses() {
List<String> list = new ArrayList<>();
for (EnumProgress p : EnumProgress.values()) {
list.add(p.getLiteral());
}
return list;
}
}
@@ -32,7 +32,7 @@ public class PersonBean implements Serializable {
private String email;
private Integer fkCamp;
private Integer fkProfile;
private Boolean accept;
private String progress;
private LocalDateTime created;
private EnumSex sex;
private Integer fkRegistrator;
@@ -194,20 +194,6 @@ public class PersonBean implements Serializable {
this.fkProfile = fkProfile;
}
/**
* @return the accept
*/
public Boolean getAccept() {
return accept;
}
/**
* @param accept the accept to set
*/
public void setAccept(Boolean accept) {
this.accept = accept;
}
/**
* @return the created
*/
@@ -291,4 +277,18 @@ public class PersonBean implements Serializable {
public void setRequiredPrice(BigDecimal requiredPrice) {
this.requiredPrice = requiredPrice;
}
/**
* @return the progress
*/
public String getProgress() {
return progress;
}
/**
* @param progress the progress to set
*/
public void setProgress(String progress) {
this.progress = progress;
}
}
@@ -79,7 +79,7 @@ public class DashboardRepository {
.deleteFrom(T_PERSONDOCUMENT)
.where(T_PERSONDOCUMENT.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
lrw.add(sql.execute());
StringBuilder buf = new StringBuilder("Dokument ");
@@ -92,7 +92,7 @@ public class DashboardRepository {
T_RSS.RECIPIENT)
.values(buf.toString(), "registrator");
// @formatter:on
LOGGER.debug("{}", sql2.toString());
LOGGER.trace(sql);
sql2.execute();
});
return lrw.getCounter();
@@ -117,7 +117,7 @@ public class DashboardRepository {
)
.values(bean.getName(), bean.getFiletype(), bean.getFkPerson(), bean.getDocument());
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
sql.execute();
StringBuilder buf = new StringBuilder("Dokument ");
@@ -130,7 +130,7 @@ public class DashboardRepository {
T_RSS.RECIPIENT)
.values(buf.toString(), "registrator");
// @formatter:on
LOGGER.debug("{}", sql2.toString());
LOGGER.trace(sql2);
sql2.execute();
});
}
@@ -37,7 +37,7 @@ public class DocumentRepository {
.selectFrom(T_DOCUMENT)
.where(T_DOCUMENT.PK.eq(id));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
TDocumentRecord r = sql.fetchOne();
if (r == null) {
return null;
@@ -54,7 +54,7 @@ public class ICalRepository {
.from(T_CAMP)
.leftJoin(T_LOCATION).on(T_LOCATION.PK.eq(T_CAMP.FK_LOCATION));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
ICalendar ical = new ICalendar();
ical.getTimezoneInfo().setDefaultTimezone(TimezoneAssignment.download(TimeZone.getTimeZone("Europe/Berlin"), false));
Iterator<Record5<String, LocalDateTime, LocalDateTime, String, String>> i = sql.fetch().iterator();
@@ -37,7 +37,7 @@ public class MigrationRepository {
.from(T_PROFILE)
.where(T_PROFILE.USERNAME.eq(username));
// @formatter:on
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.fetchOne(T_PROFILE.PASSWORD);
}
}
@@ -119,7 +119,7 @@ public class KeycloakRepository {
Response response = resource.create(user);
Boolean success = Status.CREATED.equals(response.getStatusInfo());
if (success) {
LOGGER.info("created new keycloak user {}", user.getUsername());
LOGGER.debug("created new keycloak user {}", user.getUsername());
} else {
LOGGER.error("error on creating keycloak user {}: {}", user.getUsername(), response.getStatus());
}
@@ -99,9 +99,15 @@ public class RegistrationController extends CommonController {
return "/registration/cancellation";
}
@GetMapping("/registration/remove/{id}")
public String remove(@PathVariable("id") Integer id, final Model model) {
service.removeBooking(id);
@GetMapping("/registration/revoke/{id}")
public String revoke(@PathVariable("id") Integer id, final Model model) {
service.revokeBooking(id, true);
return "redirect:/dashboard";
}
@GetMapping("/registration/unrevoke/{id}")
public String unrevoke(@PathVariable("id") Integer id, final Model model) {
service.revokeBooking(id, false);
return "redirect:/dashboard";
}
@@ -2,7 +2,6 @@ package de.jottyfan.camporganizer.module.registration;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSONDOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILEROLE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_RSS;
@@ -41,9 +40,9 @@ import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersondocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord;
@@ -147,7 +146,7 @@ public class RegistrationRepository {
T_RSS.RECIPIENT)
.values(new StringBuilder(bean.getFullname()).append(" hat sich als Nutzer im CampOrganizer2 registriert.").toString(), "admin");
// @formatter:on
LOGGER.debug("{}", sql2.toString());
LOGGER.trace(sql2);
sql2.execute();
} else {
SelectConditionStep<Record1<Integer>> sql1 = DSL.using(t)
@@ -251,13 +250,11 @@ public class RegistrationRepository {
}
/**
* remove the booking and all of its dependencies
* set the revoke state of a booking
*
* @param id the pk of t_person
* @return number of affected database rows, should be 1
* @param id the ID of the booking
*/
public Integer removeBooking(Integer id) {
LambdaResultWrapper lrw = new LambdaResultWrapper();
public void revokeBooking(Integer id) {
jooq.transaction(t -> {
SelectConditionStep<Record5<String, String, String, String, LocalDateTime>> sql0 = DSL.using(t)
// @formatter:off
@@ -279,33 +276,32 @@ public class RegistrationRepository {
LocalDateTime arrive = r.get(T_CAMP.ARRIVE);
StringBuilder rssMessage = new StringBuilder(username);
rssMessage.append(" hat die Buchung von ");
rssMessage.append(" hat die Stornierung der Buchung von ");
rssMessage.append(forename).append(" ").append(surname);
rssMessage.append(" an ");
rssMessage.append(campname).append(" ")
.append(arrive == null ? "" : arrive.format(DateTimeFormatter.ofPattern("YYYY")));
rssMessage.append(" storniert.");
rssMessage.append(" ausgelöst.");
DeleteConditionStep<TPersondocumentRecord> sql1 = DSL.using(t).deleteFrom(T_PERSONDOCUMENT)
.where(T_PERSONDOCUMENT.FK_PERSON.eq(id));
LOGGER.trace(sql1.toString());
UpdateConditionStep<TPersonRecord> sql1 = jooq
// @formatter:off
.update(T_PERSON)
.set(T_PERSON.PROGRESS, EnumProgress.revoked)
.where(T_PERSON.PK.eq(id));
// @formatter:on
LOGGER.trace(sql1);
sql1.execute();
DeleteConditionStep<TPersonRecord> sql2 = DSL.using(t).deleteFrom(T_PERSON).where(T_PERSON.PK.eq(id));
LOGGER.trace(sql2.toString());
lrw.add(sql2.execute());
InsertValuesStep2<TRssRecord, String, String> sql3 = DSL.using(t)
InsertValuesStep2<TRssRecord, String, String> sql2 = DSL.using(t)
// @formatter:off
.insertInto(T_RSS,
T_RSS.MSG,
T_RSS.RECIPIENT)
.values(rssMessage.toString(), "registrator");
// @formatter:on
LOGGER.trace("{}", sql3.toString());
sql3.execute();
LOGGER.trace("{}", sql2.toString());
sql2.execute();
});
return lrw.getCounter();
}
/**
@@ -494,4 +490,59 @@ public class RegistrationRepository {
Iterator<Integer> i = sql.fetch(T_PERSON.PK).iterator();
return i.hasNext();
}
/**
* turn back the revocation and set state to requested
*
* @param id
*/
public void unrevokeBooking(Integer id) {
jooq.transaction(t -> {
SelectConditionStep<Record5<String, String, String, String, LocalDateTime>> sql0 = DSL.using(t)
// @formatter:off
.select(T_PROFILE.USERNAME, T_PERSON.FORENAME, T_PERSON.SURNAME, T_CAMP.NAME, T_CAMP.ARRIVE)
.from(T_PERSON)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_PERSON.FK_PROFILE))
.where(T_PERSON.PK.eq(id));
// @formatter:on
LOGGER.trace(sql0.toString());
Record5<String, String, String, String, LocalDateTime> r = sql0.fetchOne();
if (r == null) {
throw new DataAccessException("no such entry in t_person with id = " + id);
}
String username = r.get(T_PROFILE.USERNAME);
String forename = r.get(T_PERSON.FORENAME);
String surname = r.get(T_PERSON.SURNAME);
String campname = r.get(T_CAMP.NAME);
LocalDateTime arrive = r.get(T_CAMP.ARRIVE);
StringBuilder rssMessage = new StringBuilder(username);
rssMessage.append(" hat die Stornierung der Buchung von ");
rssMessage.append(forename).append(" ").append(surname);
rssMessage.append(" an ");
rssMessage.append(campname).append(" ")
.append(arrive == null ? "" : arrive.format(DateTimeFormatter.ofPattern("YYYY")));
rssMessage.append(" rückgängig gemacht.");
UpdateConditionStep<TPersonRecord> sql1 = jooq
// @formatter:off
.update(T_PERSON)
.set(T_PERSON.PROGRESS, EnumProgress.requested)
.where(T_PERSON.PK.eq(id));
// @formatter:on
LOGGER.trace(sql1);
sql1.execute();
InsertValuesStep2<TRssRecord, String, String> sql2 = DSL.using(t)
// @formatter:off
.insertInto(T_RSS,
T_RSS.MSG,
T_RSS.RECIPIENT)
.values(rssMessage.toString(), "registrator");
// @formatter:on
LOGGER.trace("{}", sql2.toString());
sql2.execute();
});
}
}
@@ -75,12 +75,18 @@ public class RegistrationService {
}
/**
* remove the booking and all of its dependencies
* revoke a booking
*
* @param id the id of the booking (t_person.pk)
* @param id the ID of the booking
* @parem doRevoke if true, revoke the booking; if false, unrevoke and set it to
* requested again
*/
public Boolean removeBooking(Integer id) {
return repository.removeBooking(id) > 0;
public void revokeBooking(Integer id, Boolean doRevoke) {
if (doRevoke) {
repository.revokeBooking(id);
} else {
repository.unrevokeBooking(id);
}
}
public void toggleConsent(Integer id) {
@@ -47,7 +47,7 @@ public class RssRepository {
.from(T_RSS)
.where(T_RSS.RECIPIENT.eq(recipientCode));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
List<RssBean> list = new ArrayList<>();
Iterator<Record3<Integer, String, LocalDateTime>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -63,7 +63,7 @@ public class RssRepository {
public List<String> getAllFeeds() {
SelectJoinStep<Record1<String>> sql = jooq.selectDistinct(T_RSS.RECIPIENT).from(T_RSS);
LOGGER.debug(sql.toString());
LOGGER.trace(sql);
return sql.fetch(T_RSS.RECIPIENT);
}
@@ -76,7 +76,7 @@ public class RssRepository {
T_RSS.REGDATE)
.from(T_RSS);
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
List<RssBean> list = new ArrayList<>();
Iterator<Record4<Integer, String, String, LocalDateTime>> i = sql.fetch().iterator();
while (i.hasNext()) {
@@ -96,7 +96,7 @@ public class RssRepository {
.deleteFrom(T_RSS)
.where(T_RSS.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
sql.execute();
}
@@ -107,7 +107,7 @@ public class RssRepository {
.set(T_RSS.MSG, bean.getMessage())
.where(T_RSS.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug("{}", sql.toString());
LOGGER.trace(sql);
sql.execute();
}
}
@@ -1,6 +1,8 @@
package de.jottyfan.camporganizer.module.staticpages;
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;
@@ -14,6 +16,9 @@ import de.jottyfan.camporganizer.module.camplist.CommonController;
@Controller
public class StaticPagesController extends CommonController {
@Autowired
private StaticPagesService service;
/**
* load the index page
*
@@ -21,7 +26,7 @@ public class StaticPagesController extends CommonController {
*/
@GetMapping("/")
public String getIndex() {
return "/index";
return "redirect:/allgemeines";
}
/**
@@ -44,6 +49,16 @@ public class StaticPagesController extends CommonController {
return "/verein";
}
/**
* load the vereinsmitglieder page
*
* @return the vereinsmitglieder page
*/
@GetMapping("/vereinsmitglieder")
public String getVereinsmitglieder() {
return "/vereinsmitglieder";
}
/**
* load the kontakt page
*
@@ -60,7 +75,8 @@ public class StaticPagesController extends CommonController {
* @return the allgemeines page
*/
@GetMapping("/allgemeines")
public String getAllgemeines() {
public String getAllgemeines(final Model model) {
model.addAttribute("title", service.getStockDescription());
return "/allgemeines";
}
@@ -0,0 +1,37 @@
package de.jottyfan.camporganizer.module.staticpages;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.Main;
/**
*
* @author jotty
*
*/
@Service
public class StaticPagesService {
@Autowired
private ResourceLoader resourceLoader;
public Properties getStockDescription() {
Resource resource = resourceLoader.getResource(
"classpath:/static/images/stock.properties"
);
Properties properties = new Properties();
try {
properties.load(resource.getInputStream());
} catch (IOException e) {
Main.LOGGER.error(e.getMessage());
}
return properties;
}
}
File diff suppressed because one or more lines are too long
+94 -12
View File
@@ -49,6 +49,48 @@ body {
background-repeat: no-repeat;
}
.logo {
width: 128px;
height: 55px;
background-size: 128px;
background-repeat: no-repeat;
background-image: url('../images/logo.png');
margin-left: 8px;
display: inline-block;
position: relative;
overflow: hidden;
}
.logo::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('../images/logo_hover.png');
background-size: cover;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.logo:hover::before {
opacity: 1;
}
.icon_profile {
width: 32px;
height: 32px;
background-size: 32px;
background-repeat: no-repeat;
background-image: url('../images/Icon_Profil.svg');
display: inline-block;
}
.icon_profile:hover {
background-image: url('../images/Icon_Profil_hover.svg');
}
a {
font-family: 'Fira Sans';
}
@@ -121,19 +163,20 @@ div {
!important;
}
.acc_true {
background-image: linear-gradient(to bottom right, #cfc, #afa)
!important;
.acc_approved {
background: #aaffaa !important;
}
.acc_false {
background-image: linear-gradient(to bottom right, #fcc, #faa)
!important;
.acc_rejected {
background: #ffaaaa !important;
}
.acc_null {
background-image: linear-gradient(to bottom right, #fdb, #fca)
!important;
.acc_revoked {
background: rgb(220, 138, 221) !important;
}
.acc_requested {
background: rgb(255, 190, 111) !important;
}
.right-dist {
@@ -303,6 +346,17 @@ div {
margin: 0px 2px 0px 2px;
}
.badgeerror {
border-radius: 8px;
border: 1px solid black;
color: white;
font-weight: bolder;
background-image: linear-gradient(to right bottom, rgb(246, 97, 81),
rgb(165, 29, 45));
padding: 2px 4px 2px 4px;
margin: 0px 2px 0px 2px;
}
.dist8 {
margin: 8px;
}
@@ -333,19 +387,19 @@ div {
}
.framed-green {
background: linear-gradient(to bottom right, lime, darkgreen);
background: linear-gradient(to bottom right, darkgreen, lime);
color: white;
border: 1px solid green;
}
.framed-red {
background: linear-gradient(to bottom right, red, darkred);
background: linear-gradient(to bottom right, darkred, red);
color: white;
border: 1px solid red;
}
.framed-orange {
background: linear-gradient(to bottom right, orange, #bf6c06);
background: linear-gradient(to bottom right, #bf6c06, orange);
color: white;
border: 1px solid orange;
}
@@ -356,6 +410,12 @@ div {
border: 1px solid black;
}
.framed-pink {
background: linear-gradient(to bottom right, #3b115b, #d18be8);
color: #f8effb;
border: 1px solid #4a0084;
}
.nomaxwidth {
max-width: none !important;
}
@@ -457,3 +517,25 @@ div {
margin: 0px !important;
padding: 100px;
}
.hoverlink {
color: #333333;
text-decoration: none;
}
.hoverlink:hover {
color: #0d6efd;
text-decoration: underline;
}
.thumbswipe {
-webkit-overflow-scrolling: touch;
max-height: 100px;
overflow-x: scroll;
overflow-y: hidden;
scrollbar-width: none;
}
.thumbswipe::-webkit-scrollbar {
display: none;
}
@@ -148,3 +148,13 @@ h4 {
h5 {
font-size: 14px;
}
.preview-content {
min-height: 200px;
max-height: 200px;
overflow-y: auto;
}
.accordion-collapse:not(.show) {
height: 200px !important;
}
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 71.44 71.44">
<defs>
<style>
.cls-1 {
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: .5px;
}
.cls-1, .cls-2 {
stroke: #008b8b;
}
.cls-1, .cls-2, .cls-3 {
fill: none;
}
.cls-4 {
clip-path: url(#clippath);
}
.cls-2 {
stroke-miterlimit: 10;
}
</style>
<clipPath id="clippath">
<circle class="cls-3" cx="35.72" cy="35.72" r="35.22"/>
</clipPath>
</defs>
<g id="Ebene_1-2" data-name="Ebene 1">
<circle class="cls-2" cx="35.72" cy="35.72" r="35.22"/>
<g class="cls-4">
<g>
<path class="cls-1" d="m26.52,40.74c.1.18,2.36-1.36,2.36-1.36.7,1.22-8.38,7.81-7.67,9.05,1.64,2.84,16.71-10.45,17.06-9.85,0,0-19.7,11.6-17.8,14.89,1.1,1.91,23.09-13.33,23.09-13.33.64,1.11-24.96,17.34-24.23,18.61l27.74-16.02c1.97,3.41-28.1,17.71-26.74,20.06,1.11,1.92,28.55-18.65,29.48-17.02l-29.78,21.81,31.45-18.16c1.78,3.08-31.55,21.22-30.85,22.43.13.22,31.02-17.91,31.02-17.91,2.48,4.3-28.57,20.86-28.46,21.05l27.99-16.16c1.06,1.84-18.76,15.45-18.76,15.45.49.85,18.85-10.88,18.85-10.88l-10.45,10.65s9.95-7.97,10.91-6.3c.39.68-4.82,5.99-4.21,7.05.28.48,3.6-2.08,3.6-2.08"/>
<path class="cls-1" d="m27.35,10.64c.22.37,2.8-2.3,3.09-1.79l-9.63,10.18c.69,1.2,18.44-11.86,18.96-10.95,0,0-19,13.93-18.28,15.17.99,1.71,20.42-16.02,22.25-12.85,1.25,2.16-24.58,13.41-22.24,17.46,2.06,3.57,26.29-15.18,26.29-15.18,2.08,3.6-27.69,14.43-25.02,19.06l25.92-14.96s-23.11,17.33-22.84,17.8c2.55,4.41,24.69-14.25,24.69-14.25,0,0-19.83,14.92-19.34,15.78.46.79,18.25-14.34,19.9-11.49,0,0-15.86,10.69-14.52,13l11.54-6.66"/>
<path class="cls-2" d="m35.72,37.67h0c8.62,0,15.61,6.99,15.61,15.61v21.27h-31.22v-21.27c0-8.62,6.99-15.61,15.61-15.61Z"/>
<circle class="cls-2" cx="35.72" cy="22.8" r="14.87"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

@@ -0,0 +1,10 @@
stock02 = Heimfreizeit - Spiele im Wertwiesenpark
stock25 = Olfen - Gutes Essen
stock27 = Olfen - Miniaturdorf im Wald
stock29 = Olfen - Die Frohe Stunde am Morgen
stock52 = Rehe 2 - Kreative Ideen
stock63 = Schweiz - Blaue Bergseen
stock71 = Heimfreizeit - Kreuzkirche ECG Heilbronn
stock83 = Schweiz - Aufstieg in die Berge
stock84 = Hohenhaslach - Mitten in den Weinbergen
stock85 = Rehe 2 - Gemeinsame Andacht
@@ -120,7 +120,8 @@
</div>
<label for="startBooking" class="col-sm-2 col-form-label mb-2">Buchungsbeginn</label>
<div class="col-sm-4 mb-2">
<span class="error" th:each="error : ${#fields.errors('startBooking')}">[[${error}]]<br /></span> <input id="startBooking" type="date" th:field="*{startBooking}"
<span class="error" th:each="error : ${#fields.errors('startBooking')}">[[${error}]]<br /></span>
<input id="startBooking" type="datetime-local" th:field="*{startBooking}"
th:class="${'form-control ' + (#fields.hasErrors('startBooking') ? 'inputerror' : '')}" />
</div>
<label for="inputBedsFemale" class="col-sm-2 col-form-label mb-2">Anzahl Betten für Mädchen</label>
@@ -0,0 +1,63 @@
<!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">
<body>
<th:block layout:fragment="content">
<div class="tablebox" sec:authorize="hasRole('admin')">
<div class="alert alert-info">
Hier werden zusätzliche Dokumente für Freizeiten bereitgestellt. Die Wegbeschreibung, die Bestätigung und die Jahrespläne sind davon nicht betroffen.
</div>
<form th:action="@{/admin/campdocument/add}" method="post" th:object="${campdocument}">
<table id="cammpdocs" class="table table-striped" style="width: 100% !important">
<thead>
<tr>
<td>ID</td>
<td>Freizeit</td>
<td>Dokument</td>
</tr>
</thead>
<tbody>
<tr th:each="cd : ${campdocuments}">
<td>
<div class="dropdown" style="display: inline">
<button class="btn btn-outline-danger dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-trash-alt"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" th:href="@{/admin/campdocument/delete/{id}(id=${cd.pk})}">Kombination endgültig löschen</a>
</ul>
</div>
</td>
<td th:text="${campmap.get(cd.fkCamp)}"></td>
<td th:text="${documentmap.get(cd.fkDocument)}"></td>
</tr>
</tbody>
<tfoot>
<tr>
<td><button type="submit" class="btn btn-outline-primary"><i class="fa fa-plus"></i></button></td>
<td>
<select id="campnew" th:field="*{fkCamp}" class="form-select select2-single">
<option value="" label="--- bitte wählen ---" />
<option th:each="c : ${camps}" th:value="${c.pk}" th:label="${c}" />
</select>
</td>
<td>
<select id="documentnew" th:field="*{fkDocument}" class="form-select select2-single">
<option value="" label="--- bitte wählen ---" />
<option th:each="d : ${documents}" th:value="${d.pk}" th:label="${d}" />
</select>
</td>
</tr>
</tfoot>
</table>
</form>
<script>
$(document).ready(function() {
$("#campdocs").DataTable({
language : locale_de
});
});
</script>
</div>
</th:block>
</body>
</html>
@@ -51,7 +51,7 @@
</tbody>
<tfoot>
<tr>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}(d=${pagedest})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}?fkCamp={c}(d=${pagedest},c=${fkCamp})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
</tr>
</tfoot>
</table>
@@ -46,7 +46,7 @@
</tbody>
<tfoot>
<tr>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}(d=${pagedest})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}?module={m}(d=${pagedest},m=${module})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
</tr>
</tfoot>
</table>
@@ -51,7 +51,7 @@
</tbody>
<tfoot>
<tr>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}(d=${pagedest})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}?fkProfile={u}(d=${pagedest},u=${fkProfile})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
</tr>
</tfoot>
</table>
+154 -120
View File
@@ -7,140 +7,174 @@
<body>
<th:block layout:fragment="content">
<div class="container">
<div class="row">
<div class="col">
<div class="blocktext">
<h1 class="titlefont">Statt Langeweile zu Hause Action auf einer Freizeit!</h1>
<p>
Ferien sind cool! Das steht auf jeden Fall fest. Doch wenn die Freunde alle im Urlaub sind und du alleine zu Hause bleibst,
kommt schnell Langeweile auf. <strong>Warum also nicht einfach die Ferien mit vielen anderen Gleichaltrigen
verbringen und jede Menge Spaß erleben?</strong> Genau dafür stehen <strong>Onkel Werner Freizeiten!</strong>
</p>
<h2 class="headlinefont">Das sind Onkel Werner Freizeiten</h2>
<p>
<strong>Onkel Werner Freizeiten sind immer ein Erlebnis der besonderen Art.</strong> Je nach Freizeit verbringst du entweder
ein verlängertes Wochenende, eine Woche oder sogar zwei Wochen zusammen mit einer Hand voll Mitarbeitern und jeder Menge
Kindern oder Teenies.
</p>
<p>Im Freizeitheim angekommen, wirst du erst einmal ordentlich begrüßt und beziehst zusammen mit deinen (neuen) Freunden
dein Zimmer. Und dann kann die Freizeit auch schon starten. Hier erlebst du jede Menge Spaß, knüpfst neue Freundschaften,
bekommst ein tolles Programm geboten, hörst spannende Geschichten aus der Bibel und noch viel mehr.</p>
<p>
<strong>Preise, Termine und weitere Infos findest du in unserem Buchungsportal.</strong>
</p>
<p>
<a th:href="@{/camplist}">Jetzt Freizeiten entdecken</a>
</p>
<div id="welcomeblock" class="row displayblock">
<div class="col-12">
<h2 class="headlinefont center">Willkommen bei den</h2>
<h1 class="titlefont center">Mittelpunkt<br />Freizeiten</h1>
</div>
<div class="col-12 center">
<a th:href="@{/camplist}" class="btn btn-outline-danger">zur Anmeldung</a>
</div>
<div class="col-12 center mt-3">
<a href="#" onclick="$('#welcomeblock').slideUp('slow'); $('#fotoblock').fadeIn(1000);" class="btn btn-outline-secondary"><i class="fas fa-arrow-down"></i>&nbsp;weitere Informationen</a>
</div>
</div>
<div id="fotoblock" class="row displayblock" style="display: none">
<div class="col-12 center mb-3">
<a href="#" onclick="$('#fotoblock').fadeOut(1000); $('#welcomeblock').slideDown('slow');" class="btn btn-outline-secondary"><i class="fas fa-arrow-up"></i>&nbsp;Startseite</a>
</div>
<div class="col-12 thumbswipe">
<div style="min-width: 13000px"><!-- omits line break -->
<th:block th:each="n : ${#numbers.sequence(1, 85)}" th:with="i=${#strings.toString(n < 10 ? '0' + n : n)}">
<a th:href="'https://www.onkelwernerfreizeiten.de/stock/stock' + ${i} + '.jpg'" data-fancybox="gallery" th:data-caption="${title['stock' + i]}"><img th:src="'https://www.onkelwernerfreizeiten.de/stock/thumb_stock' + ${i} + '.jpg'" height="96px" class="m-1" th:title="${title['stock' + i]}" /></a>
</th:block>
</div>
<script th:inline="javascript">
Fancybox.bind('[data-fancybox="gallery"]', {
});
function selectCamp(name) {
$('#fotoblock').slideUp('slow');
$('#infoblock').fadeIn(1000);
$('.description').hide();
$('.campbtn').removeClass('btn-secondary').removeClass('text-white').addClass("btn-secondary-outline");
$('#camp_' + name).show();
$('#btn_' + name).addClass('btn-secondary').removeClass("btn-secondary-outline").addClass('text-white');
}
</script>
</div>
<div class="col-12 center">
<h2 class="headlinefont">Unsere Freizeiten</h2>
</div>
<div class="col-12 center">
<!-- waiting for content from Jan
<button class="btn btn-outline-secondary" onclick="selectCamp('rehe')">Rehe</button>
-->
<button class="btn btn-outline-secondary" onclick="selectCamp('olfen')">Olfen</button>
<button class="btn btn-outline-secondary" onclick="selectCamp('growup')">Grow Up</button>
<button class="btn btn-outline-secondary" onclick="selectCamp('schweiz')">Schweiz</button>
<button class="btn btn-outline-secondary" onclick="selectCamp('heimfreizeit')">Heimfreizeit</button>
<button class="btn btn-outline-secondary" onclick="selectCamp('hohenhaslach')">Hohenhaslach</button>
</div>
<div class="col-12 center mt-3">
<a th:href="@{/camplist}" class="btn btn-outline-danger">zur Anmeldung</a>
</div>
</div>
<div id="infoblock" class="row displayblock" style="display: none">
<div class="col-12 center mb-3">
<a href="#" onclick="$('#infoblock').fadeOut(1000); $('#fotoblock').slideDown('slow');" class="btn btn-outline-secondary"><i class="fas fa-arrow-up"></i>&nbsp;Bilder</a>
</div>
<div class="col-12 center">
<h2 class="headlinefont">Unsere Freizeiten</h2>
</div>
<div class="col-12 center">
<!-- waiting for content from Jan
<button id="btn_rehe" class="btn btn-outline-secondary campbtn" onclick="selectCamp('rehe')">Rehe</button>
-->
<button id="btn_olfen" class="btn btn-outline-secondary campbtn" onclick="selectCamp('olfen')">Olfen</button>
<button id="btn_growup" class="btn btn-outline-secondary campbtn" onclick="selectCamp('growup')">Grow Up</button>
<button id="btn_schweiz" class="btn btn-outline-secondary campbtn" onclick="selectCamp('schweiz')">Schweiz</button>
<button id="btn_heimfreizeit" class="btn btn-outline-secondary campbtn" onclick="selectCamp('heimfreizeit')">Heimfreizeit</button>
<button id="btn_hohenhaslach" class="btn btn-outline-secondary campbtn" onclick="selectCamp('hohenhaslach')">Hohenhaslach</button>
</div>
<div id="camp_rehe" class="col-12 description" style="display: none">
<h2 class="headlinefont">Wochenendfreizeit in Rehe</h2>
<p>TODO: Jan & Melanie</p>
</div>
<div id="camp_olfen" class="col-12 description" style="display: none">
<h2 class="headlinefont">Kinderfreizeiten in Olfen</h2>
<p>Jede Menge Kinder in deinem Alter, viele coole Spiele, Sommerrodeln und spannende biblische Geschichten…</p>
<p>…das und noch viel mehr erwartet dich auf den Kinderfreizeiten in Olfen in den Oster- und Herbstferien. Olfen ist ein
kleines Dorf in Hessen. Im großen Freizeitheim mit noch größerem Gelände drumherum erlebst du eine Woche voller Action,
Zusammenhalt und Gottes Wort.</p>
<p>Der Tag startet mit einer Stillen Zeit zusammen mit deiner Zimmergruppe und einem Mitarbeiter. Gemeinsam nehmt ihr
Abschnitte aus der Bibel unter die Lupe.</p>
<p>Nach dem Frühstück kannst du in der “Frohen Stunde” Lieder singen, eine spannende Geschichte aus der Bibel hören und
Bibelverse lernen. Das kann dir in deinem Leben eine große Hilfe sein.</p>
<p>Am Nachmittag gibt es immer etwas zu Erleben. Zum Beispiel bei Geländespielen, in verschiedenen AGs oder Spielen im
Dorf. Mit Sicherheit ist auch etwas Interessantes für dich dabei! Würdest du zum Beispiel verkleidete Mitarbeiter im Dorf
direkt erkennen? Bist du bereit, dich den Aufgaben zu stellen?</p>
<p>Am Abend erwarten dich Spiele- oder Themenabende, tolle Filme oder auch mal ein spannender Bericht von einem Missionar.
Und ja, manchmal sind wir sogar nachts unterwegs…auf Nachtwanderungen oder bei aufregenden Geländespielen im Dunkeln.</p>
<p>Ein besonderer Höhepunkt ist die Wanderung zur Sommerrodelbahn. Und natürlich das anschließende Rodeln! Wer traut sich,
mit voller Geschwindigkeit zu fahren? Oder wer hat es lieber etwas langsamer und macht dabei ein entspanntes Gesicht auf dem
Foto, das geschossen wird?</p>
<p>…das und noch viel mehr erwartet dich auf den Kinderfreizeiten in Olfen in den Oster- und Herbstferien. Olfen ist ein kleines Dorf in Hessen. Im großen Freizeitheim mit noch
größerem Gelände drumherum erlebst du eine Woche voller Action, Zusammenhalt und Gottes Wort.</p>
<p>Der Tag startet mit einer Stillen Zeit zusammen mit deiner Zimmergruppe und einem Mitarbeiter. Gemeinsam nehmt ihr Abschnitte aus der Bibel unter die Lupe.</p>
<p>Nach dem Frühstück kannst du in der “Frohen Stunde” Lieder singen, eine spannende Geschichte aus der Bibel hören und Bibelverse lernen. Das kann dir in deinem Leben eine große
Hilfe sein.</p>
<p>Am Nachmittag gibt es immer etwas zu Erleben. Zum Beispiel bei Geländespielen, in verschiedenen AGs oder Spielen im Dorf. Mit Sicherheit ist auch etwas Interessantes für dich
dabei! Würdest du zum Beispiel verkleidete Mitarbeiter im Dorf direkt erkennen? Bist du bereit, dich den Aufgaben zu stellen?</p>
<p>Am Abend erwarten dich Spiele- oder Themenabende, tolle Filme oder auch mal ein spannender Bericht von einem Missionar. Und ja, manchmal sind wir sogar nachts unterwegs…auf
Nachtwanderungen oder bei aufregenden Geländespielen im Dunkeln.</p>
<p>Ein besonderer Höhepunkt ist die Wanderung zur Sommerrodelbahn. Und natürlich das anschließende Rodeln! Wer traut sich, mit voller Geschwindigkeit zu fahren? Oder wer hat es lieber
etwas langsamer und macht dabei ein entspanntes Gesicht auf dem Foto, das geschossen wird?</p>
<p>Sei dabei und lerne neue Freunde kennen, die du bestimmt auch in der nächsten Freizeit wieder treffen wirst!</p>
</div>
<div id="camp_growup" class="col-12 description" style="display: none">
<h2 class="headlinefont">Jugendfreizeit Grow Up</h2>
<p>Grow Up kann man mit heranwachsen übersetzen und genau darum geht es auf dieser Freizeit! Möchtest du im Glauben
heranwachsen, Gottes Wort begierig studieren und Gott noch tiefer erkennen? Dann bist du auf dieser Freizeit genau richtig!</p>
<p>Grow Up kann man mit heranwachsen übersetzen und genau darum geht es auf dieser Freizeit! Möchtest du im Glauben heranwachsen, Gottes Wort begierig studieren und Gott noch
tiefer erkennen? Dann bist du auf dieser Freizeit genau richtig!</p>
<p>Auf der Grow Up möchten wir dir unter anderem folgende Möglichkeiten bieten:</p>
<ul>
<li>Selbstständiges Arbeiten mit deiner Bibel</li>
<li>Austausch mit anderen Teilnehmern und Mitarbeitern</li>
<li>Jede Menge Gedanken, um im Glauben zu wachsen</li>
</ul>
<p>Und natürlich Jugendliche und junge Erwachsene kennenzulernen, die das gleiche Anliegen haben wie du. Nämlich Jesus
Christus immer ähnlicher zu werden und ein treuer Nachfolger zu sein.</p>
<p>Den Tag startest du erstmal ganz alleine mit Gott, deiner Bibel und deinem Freizeit-Heft. Hier geht es um ein Bibelbuch
oder biblisches Thema, das du anhand von Fragen tiefergehend studieren kannst.</p>
<p>Nach dem Frühstück treffen wir uns in kleinen Gruppen. &nbsp;Hier kannst du dich über die Dinge austauschen, die du im
Bibeltext gelernt hast. Welche Fragen sind bei dir aufgetaucht? Was verstehst du überhaupt nicht? Was ist dir besonders
wichtig geworden? Und wie kannst du es im Alltag anwenden? Der Austausch bereichert dich durch Gedanken von Anderen, auf die
du selber vielleicht gar nicht gekommen wärst.</p>
<p>Am Nachmittag erwarten dich interessante Seminare zu relevanten Themen, Workshops, Geländespiele. Aber auch besondere
Aktionen, wie ein Missionseinsatz oder ein stiller Nachmittag.</p>
<p>Abends kannst du dich auf Vorträge freuen, die dich in deinem Glaubensleben herausfordern und zum Wachstum anspornen
sollen. Oder wir verbringen gemütliche Zeiten am Lagerfeuer und führen interessante Gespräche bei einer Nachtwanderung. Denn
neben der intensiver Beschäftigung mit Gottes Wort wirst du auf der Grow Up auch die Möglichkeit haben, intensive
<p>Und natürlich Jugendliche und junge Erwachsene kennenzulernen, die das gleiche Anliegen haben wie du. Nämlich Jesus Christus immer ähnlicher zu werden und ein treuer Nachfolger zu
sein.</p>
<p>Den Tag startest du erstmal ganz alleine mit Gott, deiner Bibel und deinem Freizeit-Heft. Hier geht es um ein Bibelbuch oder biblisches Thema, das du anhand von Fragen tiefergehend
studieren kannst.</p>
<p>Nach dem Frühstück treffen wir uns in kleinen Gruppen. &nbsp;Hier kannst du dich über die Dinge austauschen, die du im Bibeltext gelernt hast. Welche Fragen sind bei dir
aufgetaucht? Was verstehst du überhaupt nicht? Was ist dir besonders wichtig geworden? Und wie kannst du es im Alltag anwenden? Der Austausch bereichert dich durch Gedanken von Anderen,
auf die du selber vielleicht gar nicht gekommen wärst.</p>
<p>Am Nachmittag erwarten dich interessante Seminare zu relevanten Themen, Workshops, Geländespiele. Aber auch besondere Aktionen, wie ein Missionseinsatz oder ein stiller Nachmittag.</p>
<p>Abends kannst du dich auf Vorträge freuen, die dich in deinem Glaubensleben herausfordern und zum Wachstum anspornen sollen. Oder wir verbringen gemütliche Zeiten am Lagerfeuer und
führen interessante Gespräche bei einer Nachtwanderung. Denn neben der intensiver Beschäftigung mit Gottes Wort wirst du auf der Grow Up auch die Möglichkeit haben, intensive
Freundschaften zu starten oder von Anderen zu lernen.</p>
<p>
Zwischen den Programmpunkten bleibt viel Zeit, um sich kennenzulernen, zusammen <em>Nacht in Palermo</em>, Volleyball oder <em>Capture
the Flag </em>zu spielen oder auch ein Gespräch mit einem Mitarbeiter zu suchen, wenn dich etwas total beschäftigt oder du
Hilfe und Rat brauchst.
</p>
<p>Du möchtest im Glauben herausgefordert werden, die Bibel und deinen Herrn besser kennenlernen und wirklich wachsen?
Dann komm zur Grow Up und sei bereit, dich von Gott verändern zu lassen!</p>
<h2 class="headlinefont">Teeniefreizeit Schweiz</h2>
<p>Ab in die Berge zwei Wochen Schweiz: mit Bergen, Seen, Kühen und allem, was das Herz begehrt. Mit Sicherheit wirst du
dort eine ganze Menge erleben, denn das Programm ist vielfältig und voller Action!</p>
<p>Du wirst in diesen zwei Wochen die Möglichkeit haben, spannende Bibelarbeiten und gute Gemeinschaft mit Gott zu
erleben. Wir beginnen jeden Morgen in kleinen Gruppen mit gemeinsamer Stillen Zeit das bedeutet, dass wir über
verschiedene Bibelstellen nachdenken und uns über unsere Leben und die Erfahrungen, die wir mit Gott gemacht haben,
austauschen.</p>
<p>Nach dem Frühstück folgt die „Frohe Stunde“ mit einer Bibelarbeit. In dieser Zeit konzentrieren wir uns gemeinsam
darauf, was uns verschiedene Mitarbeiter über Gott und die Bibel mitteilen werden. Es gibt viel zu lernen.</p>
<p>In einer langen Mittagspause hast du Zeit für Fußball und Volleyball, Halli Galli oder andere Gesellschaftsspiele.</p>
<p>Später am Nachmittag gibt es ein abwechslungsreiches Programm, beispielsweise Geländespiele vor Ort oder im Wald,
Wasserspiele, Workshops und verschiedene AG's.</p>
<p>Den Abend verbringen wir mit coolen kreativen Spielen wie z.B. einem Kochduell, Kiental sucht die Superband, Wetten
Dass…?, und Die perfekte Minute. Oder aber wir sehen uns nach einem anstrengenden Tag ganz entspannt einen Film an und
lassen den Abend mit angeregten Gesprächen ausklingen.</p>
<p>Außerdem gibt es noch etwas ganz Besonderes: Unse Tagestouren! Die Mitarbeiter wecken dich bereits im frühen
Morgengrauen und dann wird der Gipfel auf 2820 Metern Höhe gestürmt. Oder wir fahren an den wunderschönen Thunersee. Hier
kannst du nicht nur Schwimmen, sondern auch aus zehn Metern Höhe ins Wasser springen, Beachvolleyball spielen und viele
andere coole Dinge machen.</p>
<p>Bist du bereit, auf eine Woche voller Spaß und Action? Bist du bereit, dich verändern zu lassen? Bist du neugierig
geworden? Dann sehen wir uns in der Schweiz!</p>
<h2 class="headlinefont">Heimfreizeit für Kinder in Heilbronn</h2>
<p>Du hast noch zwei Wochen Sommerferien, wohnst in Heilbronn und hast noch nichts vor? Wie wäre es mit einem coolen und
abwechslungsreichen Tagesprogramm zusammen mit vielen anderen Kindern in deinem Alter?</p>
<p>Auf der Heimfreizeit hast du die Möglichkeit schon einmal ein bisschen hineinzuschnuppern, wie es sich anfühlt, auf
einer Freizeit dabei zu sein. Denn du übernachtest zwar zu Hause, aber zwei Wochen lang verbringst du den Tag mit vielen
anderen Kindern und Mitarbeitern bei einem tollen Programm.</p>
<p>Wir starten mit Liedern und einer spannenden Geschichte aus der Bibel in den Tag. Natürlich gibts auch gemeinsames
Mittagessen. Auch Zeit zum spielen gibt es genug. Zum Beispiel auf dem Spielplatz oder beim Völkerball auf dem Alten
Friedhof. Nachmittags erwarten dich viele coole Aktionen. Wie wäre es zum Beispiel mit einer Schnitzeljagd? Oder einer
kleinen Wanderung auf den Wartberg? Mit vielen tollen Spielen im Pfühlpark? Oder mit einer schönen Abkühlung und viel Spaß
im Freibad?</p>
<p>Nach einem kleinen Nachmittagssnack verabschieden wir uns bis zum nächsten Morgen.</p>
<p>Besondere Höhepunkte sind ein Besuch im Indoor-Spielplatz oder der Experimenta. Manchmal lässt Axel (Erlebnispädagoge)
sich auch etwas ganz Besonderes einfallen, wie zum Beispiel Flag-Football.</p>
<p>Das klingt doch viel besser, als sich zu Hause zu langweilen, oder?</p>
<h2 class="headlinefont">Teeniefreizeit Hohenhaslach</h2>
<p>Eine Woche Herbstferien und das hast keine Lust, sie zu verschwenden oder alleine zu Hause zu sitzen? Wie wärs mit
einer herausfordernden Woche zusammen mit anderen Jugendlichen in den schönen Weinbergen von Baden-Württemberg?</p>
<p>Das Freizeitheim in Hohenhaslach liegt tatsächlich mitten in den Weinbergen, die natürlich im Herbst am schönsten
aussehen. Hier lassen sich wunderbar Geländespiele oder Spaziergänge erleben.</p>
<p>Aber nicht die schöne Landschaft allein macht die gute Atmosphäre aus. In dieser Woche hast du die Möglichkeit, Leute
in deinem Alter kennenzulernen, die mit Jesus leben wollen und dir zu guten Freunden werden können. Außerdem kannst du durch
tiefgehende Bibelarbeiten und Stille Zeiten mit deiner Zimmergruppe und einem Mitarbeiter auch Gott besser kennenlernen oder
Fragen stellen, die du dir schon lange gestellt hast.</p>
<p>Der Morgen ist gefüllt mit der Beschäftigung mit Gottes Wort, was es uns für unser Leben zu sagen hat und wie wir für
Gott leben können. Du wirst herausgefordert, ganz persönlich darüber nachzudenken, wo du in Bezug auf Gott stehst und was du
gerne ändern oder auch fördern möchtest. Durch das Lernen von wichtigen Bibelversen bekommst du einen guten Schutz, wenn du
im Alltag mit Problemen zu kämpfen hast.</p>
<p>Am Nachmittag bleibt viel Zeit für tolle Spiele, Herausforderungen beim Sport, Taktieren im Geländespiel oder Erkunden
des Dorfes und der Weinberge.</p>
<p>Abends erwarten dich Gameshows, ein Film oder auch mal ein gemütliches Dinner. Auch nachts sind die Weinberge nicht
immer sicher vor uns, egal ob es sich um eine Nachtwanderung oder ein spannendes Nachtspiel handelt.</p>
<p>Gemeinschaft und Spaß kommen auf jeden Fall nicht zu kurz und auch im Glauben bietet dir die Hohenhaslach-Freizeit
Herausforderung, wenn du dazu bereit bist!</p>
<p>
<strong>Preise, Termine und weitere Infos findest du in unserem Buchungsportal.</strong>
</p>
<p>
<a th:href="@{/camplist}">Jetzt Freizeiten entdecken</a>
Zwischen den Programmpunkten bleibt viel Zeit, um sich kennenzulernen, zusammen <em>Nacht in Palermo</em>, Volleyball oder <em>Capture the Flag </em>zu spielen oder auch ein Gespräch mit
einem Mitarbeiter zu suchen, wenn dich etwas total beschäftigt oder du Hilfe und Rat brauchst.
</p>
<p>Du möchtest im Glauben herausgefordert werden, die Bibel und deinen Herrn besser kennenlernen und wirklich wachsen? Dann komm zur Grow Up und sei bereit, dich von Gott verändern zu
lassen!</p>
</div>
<div id="camp_schweiz" class="col-12 description" style="display: none">
<h2 class="headlinefont">Teeniefreizeit Schweiz</h2>
<p>Ab in die Berge zwei Wochen Schweiz: mit Bergen, Seen, Kühen und allem, was das Herz begehrt. Mit Sicherheit wirst du dort eine ganze Menge erleben, denn das Programm ist
vielfältig und voller Action!</p>
<p>Du wirst in diesen zwei Wochen die Möglichkeit haben, spannende Bibelarbeiten und gute Gemeinschaft mit Gott zu erleben. Wir beginnen jeden Morgen in kleinen Gruppen mit
gemeinsamer Stillen Zeit das bedeutet, dass wir über verschiedene Bibelstellen nachdenken und uns über unsere Leben und die Erfahrungen, die wir mit Gott gemacht haben, austauschen.</p>
<p>Nach dem Frühstück folgt die „Frohe Stunde“ mit einer Bibelarbeit. In dieser Zeit konzentrieren wir uns gemeinsam darauf, was uns verschiedene Mitarbeiter über Gott und die Bibel
mitteilen werden. Es gibt viel zu lernen.</p>
<p>In einer langen Mittagspause hast du Zeit für Fußball und Volleyball, Halli Galli oder andere Gesellschaftsspiele.</p>
<p>Später am Nachmittag gibt es ein abwechslungsreiches Programm, beispielsweise Geländespiele vor Ort oder im Wald, Wasserspiele, Workshops und verschiedene AG's.</p>
<p>Den Abend verbringen wir mit coolen kreativen Spielen wie z.B. einem Kochduell, Kiental sucht die Superband, Wetten Dass…?, und Die perfekte Minute. Oder aber wir sehen uns nach
einem anstrengenden Tag ganz entspannt einen Film an und lassen den Abend mit angeregten Gesprächen ausklingen.</p>
<p>Außerdem gibt es noch etwas ganz Besonderes: Unse Tagestouren! Die Mitarbeiter wecken dich bereits im frühen Morgengrauen und dann wird der Gipfel auf 2820 Metern Höhe gestürmt.
Oder wir fahren an den wunderschönen Thunersee. Hier kannst du nicht nur Schwimmen, sondern auch aus zehn Metern Höhe ins Wasser springen, Beachvolleyball spielen und viele andere coole
Dinge machen.</p>
<p>Bist du bereit, auf eine Woche voller Spaß und Action? Bist du bereit, dich verändern zu lassen? Bist du neugierig geworden? Dann sehen wir uns in der Schweiz!</p>
</div>
<div id="camp_heimfreizeit" class="col-12 description" style="display: none">
<h2 class="headlinefont">Heimfreizeit für Kinder in Heilbronn</h2>
<p>Du hast noch zwei Wochen Sommerferien, wohnst in Heilbronn und hast noch nichts vor? Wie wäre es mit einem coolen und abwechslungsreichen Tagesprogramm zusammen mit vielen anderen
Kindern in deinem Alter?</p>
<p>Auf der Heimfreizeit hast du die Möglichkeit schon einmal ein bisschen hineinzuschnuppern, wie es sich anfühlt, auf einer Freizeit dabei zu sein. Denn du übernachtest zwar zu Hause,
aber zwei Wochen lang verbringst du den Tag mit vielen anderen Kindern und Mitarbeitern bei einem tollen Programm.</p>
<p>Wir starten mit Liedern und einer spannenden Geschichte aus der Bibel in den Tag. Natürlich gibts auch gemeinsames Mittagessen. Auch Zeit zum spielen gibt es genug. Zum Beispiel
auf dem Spielplatz oder beim Völkerball auf dem Alten Friedhof. Nachmittags erwarten dich viele coole Aktionen. Wie wäre es zum Beispiel mit einer Schnitzeljagd? Oder einer kleinen
Wanderung auf den Wartberg? Mit vielen tollen Spielen im Pfühlpark? Oder mit einer schönen Abkühlung und viel Spaß im Freibad?</p>
<p>Nach einem kleinen Nachmittagssnack verabschieden wir uns bis zum nächsten Morgen.</p>
<p>Besondere Höhepunkte sind ein Besuch im Indoor-Spielplatz oder der Experimenta. Manchmal lässt Axel (Erlebnispädagoge) sich auch etwas ganz Besonderes einfallen, wie zum Beispiel
Flag-Football.</p>
<p>Das klingt doch viel besser, als sich zu Hause zu langweilen, oder?</p>
</div>
<div id="camp_hohenhaslach" class="col-12 description" style="display: none">
<h2 class="headlinefont">Teeniefreizeit Hohenhaslach</h2>
<p>Eine Woche Herbstferien und das hast keine Lust, sie zu verschwenden oder alleine zu Hause zu sitzen? Wie wärs mit einer herausfordernden Woche zusammen mit anderen Jugendlichen in
den schönen Weinbergen von Baden-Württemberg?</p>
<p>Das Freizeitheim in Hohenhaslach liegt tatsächlich mitten in den Weinbergen, die natürlich im Herbst am schönsten aussehen. Hier lassen sich wunderbar Geländespiele oder
Spaziergänge erleben.</p>
<p>Aber nicht die schöne Landschaft allein macht die gute Atmosphäre aus. In dieser Woche hast du die Möglichkeit, Leute in deinem Alter kennenzulernen, die mit Jesus leben wollen und
dir zu guten Freunden werden können. Außerdem kannst du durch tiefgehende Bibelarbeiten und Stille Zeiten mit deiner Zimmergruppe und einem Mitarbeiter auch Gott besser kennenlernen oder
Fragen stellen, die du dir schon lange gestellt hast.</p>
<p>Der Morgen ist gefüllt mit der Beschäftigung mit Gottes Wort, was es uns für unser Leben zu sagen hat und wie wir für Gott leben können. Du wirst herausgefordert, ganz persönlich
darüber nachzudenken, wo du in Bezug auf Gott stehst und was du gerne ändern oder auch fördern möchtest. Durch das Lernen von wichtigen Bibelversen bekommst du einen guten Schutz, wenn du
im Alltag mit Problemen zu kämpfen hast.</p>
<p>Am Nachmittag bleibt viel Zeit für tolle Spiele, Herausforderungen beim Sport, Taktieren im Geländespiel oder Erkunden des Dorfes und der Weinberge.</p>
<p>Abends erwarten dich Gameshows, ein Film oder auch mal ein gemütliches Dinner. Auch nachts sind die Weinberge nicht immer sicher vor uns, egal ob es sich um eine Nachtwanderung oder
ein spannendes Nachtspiel handelt.</p>
<p>Gemeinschaft und Spaß kommen auf jeden Fall nicht zu kurz und auch im Glauben bietet dir die Hohenhaslach-Freizeit Herausforderung, wenn du dazu bereit bist!</p>
</div>
<div class="col-12 center mt-3">
<a th:href="@{/camplist}" class="btn btn-outline-danger">zur Anmeldung</a>
</div>
</div>
</div>
@@ -36,8 +36,8 @@
<td th:text="${#temporals.format(booker.bookingDate, 'dd.MM.yyyy')}"></td>
</tr>
<tr>
<th>Bestätigt</th>
<td th:text="${booker.accept == null ? '' : (booker.accept ? 'Ja' : 'abgelehnt')}"></td>
<th>Status</th>
<td th:text="${booker.progress}"></td>
</tr>
<tr>
<th>Freizeitpreis</th>
@@ -8,7 +8,14 @@
<th:block layout:fragment="content">
<div class="mainpage">
<div class="card" sec:authorize="hasRole('business_booking')">
<div class="card-header">Angemeldete Personen</div>
<div class="card-header d-flex justify-content-between">
<div>Angemeldete Personen</div>
<div>im Jahr
<span class="ml-2" th:each="y : ${years}">
<a th:class="${year == y ? '' : 'tablelink'}" th:href="@{/business/bookings/{year}(year=${y})}" th:text="${y}"></a>
</span>
</div>
</div>
<div class="card-body">
<table id="bookers" class="table table-striped">
<thead>
@@ -18,19 +25,19 @@
<th>Freizeit</th>
<th>Rolle</th>
<th>Kontostand</th>
<th>Angemeldet</th>
<th>Status</th>
<th>Bestätigt</th>
</tr>
</thead>
<tbody>
<th:block th:each="b : ${bookers}">
<tr>
<td class="middled"><a class="tablelink" th:href="@{/business/bookings/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td>
<td class="middled"><a class="tablelink" th:href="@{/business/bookings_by_id/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td>
<td class="middled" th:text="${b.sex}"></td>
<td class="middled" th:text="${b.camp}"></td>
<td class="middled" th:text="${b.role}"></td>
<td class="middled">
<form action="#" th:action="@{'/business/bookings/listpayment/' + ${b.pk}}" th:object="${addBean}" method="post">
<form action="#" th:action="@{/business/bookings/listpayment/{id}/{year}(id=${b.pk},year=${year})}" th:object="${addBean}" method="post">
<input th:id="'searchfield' + ${b.pk}" type="hidden" th:name="search" />
<div class="container">
<div class="row">
@@ -48,7 +55,7 @@
</form>
</td>
<td class="middled" th:text="${#temporals.format(b.bookingDate, 'dd.MM.yyyy')}"></td>
<td class="middled" th:text="${b.accept == null ? '' : (b.accept ? 'Ja' : 'abgelehnt')}"></td>
<td class="middled" th:text="${b.progress}"></td>
</tr>
</th:block>
</tbody>
@@ -63,19 +63,19 @@
<th>Geschlecht</th>
<th>Rolle</th>
<th>Kontostand</th>
<th>Angemeldet</th>
<th>Status</th>
<th>Bestätigt</th>
</tr>
</thead>
<tbody>
<th:block th:each="b : ${bookers}">
<tr>
<td><a class="tablelink" th:href="@{/business/bookings/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td>
<td><a class="tablelink" th:href="@{/business/bookings_by_id/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td>
<td th:text="${b.sex}"></td>
<td th:text="${b.role}"></td>
<td><span th:text="${#numbers.formatDecimal(b.paid, 1, 2) + ' €'}" th:if="${b.paid != null}"></span></td>
<td th:text="${#temporals.format(b.bookingDate, 'dd.MM.yyyy')}"></td>
<td th:text="${b.accept == null ? '' : (b.accept ? 'Ja' : 'abgelehnt')}"></td>
<td th:text="${b.progress}"></td>
</tr>
</th:block>
</tbody>
@@ -31,6 +31,11 @@
</tr>
</tfoot>
</table>
<div class="row">
<div class="col" th:each="c : ${camps}">
<a th:href="@{/business/outlay/summary/{id}(id=${c.id})}" class="btn btn-outline-primary" th:text="${c.campname}"></a>
</div>
</div>
<script>
$(document).ready(function() {
$("#table").DataTable({
@@ -0,0 +1,53 @@
<!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 Business</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="mainpage">
<div class="container" style="max-width: 100%" sec:authorize="hasRole('business_outlay')">
<table id="table" class="table table-striped">
<thead>
<tr>
<th>Kassenzettelnummer</th>
<th>Händler / Shop</th>
<th>Freizeit</th>
<th>Auslegender</th>
<th>Betrag</th>
<th>Tag / Uhrzeit</th>
<th>Kurzbeschreibung</th>
<th>Bemerkungen</th>
</tr>
</thead>
<tbody>
<tr th:each="o : ${list}">
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.recipenumber}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.trader}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.campname}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.provider}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${#numbers.formatDecimal(o.cash, 1, 2, 'COMMA')} + ' €'"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${#temporals.format(o.buydate, 'dd.MM.yyyy, HH:mm.ss')}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.ingredients}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.recipenote}"></a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="8"><a th:href="@{/business/outlay/download/{campid}(campid=${campid})}" class="btn btn-outline-primary form-control">herunterladen</a></td>
</tr>
</tfoot>
</table>
<script>
$(document).ready(function() {
$("#table").DataTable({
language : locale_de
});
});
</script>
</div>
</div>
</th:block>
</body>
</html>
@@ -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>
@@ -39,7 +39,7 @@
<div th:text="${p.camprolle}"></div></td>
<td><div th:text="${#temporals.format(p.birthDate, 'dd.MM.yyyy')}"></div>
<div th:text="${p.getAge(campStartDate)} + ' Jahre'"></div></td>
<td th:text="${p.accept}"></td>
<td th:text="${p.progress}"></td>
<td th:text="${#temporals.format(p.created, 'dd.MM.yyyy HH:mm')}"></td>
<td><span th:text="${p.consentCatalogPhoto ? 'ja' : 'nein'}" th:if="${p.consentCatalogPhoto}"></span></td>
<td th:text="${p.comment}"></td>
@@ -44,7 +44,7 @@
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="approvedpanel">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#approveddiv" aria-expanded="true" aria-control="approveddiv">kürzlich bestätigte
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#approveddiv" aria-expanded="true" aria-control="approveddiv">bestätigte
Anmeldungen</button>
</h2>
<div id="approveddiv" class="accordion-collapse collapse dist8" aria-labelled="approvedpanel" data-bs-parent="#mainacc">
@@ -79,7 +79,7 @@
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="rejectedpanel">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#rejecteddiv" aria-expanded="true" aria-control="rejecteddiv">kürzlich abgelehnte
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#rejecteddiv" aria-expanded="true" aria-control="rejecteddiv">abgelehnte
Anmeldungen</button>
</h2>
<div id="rejecteddiv" class="accordion-collapse collapse dist8" aria-labelled="rejectedpanel" data-bs-parent="#mainacc">
@@ -112,6 +112,41 @@
</script>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="revokedpanel">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#revokeddiv" aria-expanded="true" aria-control="revokeddiv">stornierte
Anmeldungen</button>
</h2>
<div id="revokeddiv" class="accordion-collapse collapse dist8" aria-labelled="revokedpanel" data-bs-parent="#mainacc">
<table id="revoked">
<thead>
<tr>
<th>Freizeit</th>
<th>Name</th>
<th>Rolle</th>
<th>Anmeldedatum</th>
</tr>
</thead>
<tbody>
<tr th:each="u : ${revoked}">
<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.camp} + ' ' + ${#temporals.format(u.date, 'yyyy')}"></a></td>
<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.fullname}"></a></td>
<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.rolename}"></a></td>
<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${#temporals.format(u.registered, 'yyyy-MM-dd')}"></a></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
$(document).ready(function() {
$("#revoked").DataTable({
language : locale_de,
pageLength : 5,
lengthMenu : [ [ 5, 25, 50, -1 ], [ 5, 25, 50, "Alle" ] ]
});
});
</script>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="overviewpanel">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#overviewdiv" aria-expanded="true" aria-control="overviewdiv">Freizeitenübersicht</button>
@@ -127,14 +162,14 @@
<tbody>
<tr th:each="o : ${campoverview}">
<td th:text="${o.camp} + ' ' + ${#temporals.format(o.date, 'yyyy')}"></td>
<td><span th:text="${o.untouched}" class="badgetodo"></span> / <span th:text="${o.rejected}" class="badgewarn"></span> / <span th:text="${o.approved}" class="badgeinfo"></span></td>
<td><span th:text="${o.untouched}" class="badgetodo"></span> / <span th:text="${o.rejected}" class="badgewarn"></span> / <span th:text="${o.approved}" class="badgeinfo"></span> / <span th:text="${o.revoked}" class="badgeerror"></span></td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Zusammenfassung</td>
<td><span th:text="${campoverviewsummary.untouched}" class="badgetodo"></span> / <span th:text="${campoverviewsummary.rejected}" class="badgewarn"></span> / <span
th:text="${campoverviewsummary.approved}" class="badgeinfo"></span></td>
th:text="${campoverviewsummary.approved}" class="badgeinfo"></span> / <span th:text="${campoverviewsummary.revoked}" class="badgeerror"></span></td>
</tfoot>
</table>
<script type="text/javascript">
@@ -146,7 +181,7 @@
});
});
</script>
<span>Legende:</span><span class="badgetodo">unbearbeitet</span><span class="badgewarn">abgelehnt</span><span class="badgeinfo">bestätigt</span>
<span>Legende:</span><span class="badgetodo">unbearbeitet</span><span class="badgewarn">abgelehnt</span><span class="badgeinfo">bestätigt</span><span class="badgeerror">storniert</span>
</div>
</div>
<div class="accordion-item">
@@ -8,7 +8,7 @@
<th:block layout:fragment="content">
<div class="mainpage">
<div class="container" style="max-width: 100%" sec:authorize="hasRole('registrator')">
<form action="#" th:action="@{/confirmation/person/update}" th:object="${person}" method="post" th:if="${person != null}">
<form id="form" action="#" th:action="@{/confirmation/person/update}" th:object="${person}" method="post" th:if="${person != null}">
<div class="row mb-2">
<label for="outputPk" class="col-sm-2 col-form-label">ID</label>
<div class="col-sm-10">
@@ -112,26 +112,65 @@
</div>
<div class="row mb-2">
<label for="inputAccept" class="col-sm-2 col-form-label">Status</label>
<div class="col-sm-10">
<div class="form-group">
<input type="radio" class="btn-check" id="accept1" name="accept1" value="" th:field="*{accept}" /> <label class="btn btn-outline-primary" for="accept1"><i
class="fas fa-question"></i>&nbsp;offen</label> <input type="radio" class="btn-check" id="accept2" name="accept2" value="true" th:field="*{accept}" /> <label class="btn btn-outline-success"
for="accept2"><i class="fas fa-check"></i>&nbsp;bestätigt</label> <input type="radio" class="btn-check" id="accept3" name="accept3" value="false" th:field="*{accept}" /> <label
class="btn btn-outline-danger" for="accept3"><i class="fas fa-ban"></i>&nbsp;abgelehnt</label>
<span class="col-sm-2 btn btn-outline-warning" th:if="${person.progress} == 'requested'">offen</span>
<span class="col-sm-2 btn btn-outline-success" th:if="${person.progress} == 'approved'">bestätigt</span>
<span class="col-sm-2 btn btn-outline-danger" th:if="${person.progress} == 'rejected'">abgelehnt</span>
<span class="col-sm-2 btn btn-outline-secondary" th:if="${person.progress} == 'revoked'">storniert</span>
<div class="col-sm-8" th:if="${#lists.contains({'requested', 'approved', 'rejected'}, person.progress)}">
<select th:field="*{progress}" class="form-select">
<option value="requested">offen</option>
<option value="approved">bestätigt</option>
<option value="rejected">abgelehnt</option>
</select>
</div>
<div class="col-sm-8" th:if="${person.progress} == 'revoked'">
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash"></i>&nbsp;endgültig löschen
</button>
</div>
</div>
<div class="row mb-2">
<label for="inputAccept" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<button type="submit" class="btn btn-primary" onclick="progress.start()">Ok</button>
<button type="submit" class="btn btn-primary" onclick="progress.start()" th:if="${person.progress} != 'revoked'">Ok</button>
<a th:href="@{/confirmation}" class="btn btn-secondary">Abbrechen</a>
</div>
</div>
</form>
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger">
<h1 class="modal-title fs-5" id="deleteModalLabel">Löschen der Anmeldung</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Willst du wirklich die Anmeldung von <span th:text="${person.forename}" class="text-danger"></span>&nbsp;<span th:text="${person.surname}" class="text-danger"></span> an der Freizeit
<select class="form-select locked" th:field="${person.fkCamp}" disabled="disabled">
<option th:each="c : ${camps}" th:value="${c.pk}" th:text="${c.name} + ' ' + ${#temporals.format(c.arrive, 'yyyy')} + ' in ' + ${c.location}"></option>
</select> löschen? Sie geht damit unwiederbringlich verloren.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Abbrechen</button>
<a th:href="@{/registration/remove/{id}(id=${person.pk})}" class="btn btn-danger">ja, wirklich löschen</a>
</div>
</div>
</div>
</div>
<div th:if="${person == null}" class="error">In der Datenbank wurde keine Person mit entsprechender ID gefunden.</div>
</div>
</div>
<script type="text/javascript">
/**
* forbid enter in form to allow disabling the ok button for revoked bookings
*/
$(document).on('keyup keypress', 'form', function(e) {
if (e.keyCode == 13) {
e.preventDefault();
return false;
}
});
</script>
</th:block>
</body>
</html>
+18 -9
View File
@@ -32,11 +32,13 @@
<div class="accordion" id="acc" th:if="${mybookings.size() > 0}" style="max-width: 800px; margin-left: auto; margin-right: auto">
<div class="accordion-item" th:each="b : ${mybookings}">
<h2 class="accordion-header" th:id="'acc-head-' + ${b.pk}" th:if="${b.pk}">
<button th:class="'accordion-button collapsed acc_' + ${b.isOver ? 'over' : b.accept}" type="button" data-bs-toggle="collapse" th:data-bs-target="'#acc-body-' + ${b.pk}"
<button th:class="'accordion-button collapsed acc_' + ${b.isOver ? 'over' : b.progress}" type="button" data-bs-toggle="collapse" th:data-bs-target="'#acc-body-' + ${b.pk}"
aria-expanded="true" th:aria-controls="'#acc-body-' + ${b.pk}">
<i class="fas fa-check framed framed-green" th:if="${b.accept}"></i> <i class="fas fa-ban framed framed-red" th:if="${b.accept} == false"></i> <i
class="fas fa-question framed framed-orange" th:if="${b.accept} == null"></i>
<span th:text="${b.forename + ' ' + b.surname + ' für ' + b.campName + ' ' + #numbers.formatInteger(b.year, 4)}" class="headlinefont"></span>
<i class="fas fa-check framed framed-green" th:if="${b.progress} == 'approved'"></i>
<i class="fas fa-ban framed framed-red" th:if="${b.progress} == 'rejected'"></i>
<i class="fas fa-question framed framed-orange" th:if="${b.progress} == 'requested'"></i>
<i class="fas fa-trash framed framed-pink" th:if="${b.progress} == 'revoked'"></i>
<span th:text="${b.forename + ' ' + b.surname + ' @ ' + b.campName + ' ' + #numbers.formatInteger(b.year, 4)}" class="headlinefont"></span>
</button>
</h2>
<div th:id="'acc-body-' + ${b.pk}" class="accordion-collapse collapse" th:aria-labelledby="'acc-head-' + ${b.pk}">
@@ -62,7 +64,7 @@
</div>
</div>
</div>
<div class="card" th:if="${b.accept}">
<div class="card" th:if="${b.progress} == 'approved'">
<div class="card-header">Dokumente</div>
<div class="card-body">
<div class="container">
@@ -139,8 +141,11 @@
<div class="col-sm-8">
<input type="submit" class="btn btn-primary" value="Änderungen übernehmen" />
</div>
<div class="col-sm-2">
<a th:href="@{/registration/cancel/{id}(id=${b.pk})}" class="btn btn-outline-danger" th:if="${!b.isOver}">stornieren</a>
<div class="col-sm-2" th:if="${b.progress != 'revoked'}">
<a th:href="@{/registration/revoke/{id}(id=${b.pk})}" class="btn btn-outline-danger" th:if="${!b.isOver}">stornieren</a>
</div>
<div class="col-sm-2" th:if="${b.progress == 'revoked'}">
<a th:href="@{/registration/unrevoke/{id}(id=${b.pk})}" class="btn btn-outline-success" th:if="${!b.isOver}">Stornierung aufheben</a>
</div>
</div>
</div>
@@ -150,8 +155,12 @@
<div class="alert alert-primary" th:if="${b.created != null}">
angemeldet am <span th:text="${#temporals.format(b.created, 'dd.MM.yyyy')}"></span> von <span th:text="${b.subscriber}"></span>
</div>
<div th:class="'alert ' + ${b.accept ? 'alert-success' : 'alert-danger'}" th:if="${b.accept != null}">
<span th:text="${b.accept ? 'bestätigt' : 'abgelehnt'}"></span> von <span th:text="${b.registrator}"></span>
<div class="alert alert-danger" th:if="${b.progress == 'revoked'}">
storniert von <span th:text="${b.subscriber}"></span>
</div>
<div th:class="'alert alert-' + (${b.progress} == 'approved' ? 'success' : 'danger')" th:if="${#lists.contains({'approved', 'rejected'}, b.progress)}">
<span th:if="${b.progress} == 'approved'">bestätigt</span>
<span th:if="${b.progress} == 'rejected'">abgelehnt</span> von <span th:text="${b.registrator}"></span>
</div>
<div class="alert alert-warning" th:if="${b.isOver}">Die Freizeit ist bereits vorbei.</div>
</div>
@@ -47,7 +47,7 @@
</div>
<div class="row g-5" th:unless="${c.bookingHasStarted}">
<div class="col-12 alert alert-info" role="alert">
Die Anmeldung wird erst am <span th:text="${#temporals.format(c.startBooking, 'dd.MM.yyyy')}"></span> freigeschaltet.
Die Anmeldung wird erst am <span th:text="${#temporals.format(c.startBooking, 'dd.MM.yyyy, HH:mm')}"></span> Uhr, freigeschaltet.
</div>
</div>
</div>
-17
View File
@@ -17,23 +17,6 @@
<p>Einen Tag vor seinem Tod sagte er dazu: „Ich will mir dafür nicht auf die Schulter klopfen. Es ist Gottes Werk.“</p>
<p>Wir dürfen dankbar sein, für das, was er durch Gottes Gnade aufgebaut hat und bitten euch herzlich um Gebet, um diese große Arbeit der Freizeiten so weiterzuführen, wie es dem Herrn gefällt und wie es sich auch Onkel Werner gewünscht hat. Er sagte, dass er sich wünscht und dafür betet, dass wir noch doppelt so viel Segen erleben dürften, ähnlich wie Elisa, der sich das doppelte des Geistes Elias erbat.</p>
<p>Onkel Werner wird uns fehlen, aber wir freuen uns, dass er nach einem erfüllten Leben sein Ziel erreichen durfte!</p>
<!--
<img class="img-fluid rounded" th:src="@{/images/start-banner-2019.jpg}" alt=""><br />
<br />
<div class="blocktext">
<h1 style="text-align: left">Ferien, die in Erinnerung bleiben persönlich, echt, erlebnisreich</h1>
<p style="text-align: left">Du willst nicht einfach deine Zeit totschlagen, sondern Ferien erleben, die dein Leben prägen,
sinnvoll sind und dazu Spaß machen? Dann bist du hier genau richtig! Bei den Freizeiten geht es um Freundschaften, Spaß und
vor allem Gottes Wort. Genau dafür stehen Onkel Werner Freizeiten!</p>
<p></p>
<h2 style="text-align: left">Unsere Freizeiten</h2>
<h4 style="text-align: left">Erfahre hier, welche Freizeiten wir anbieten und was da so abgeht.</h4>
<p style="text-align: left">Preise, Termine und weitere Infos findest du in unserem Buchungsportal.</p>
<p style="text-align: left">
<a th:href="@{/camplist}">Jetzt Freizeiten entdecken</a>
</p>
</div>
-->
</div>
</div>
</div>
@@ -21,7 +21,7 @@
th:if="${bean.isDirector}">Leiter</span><span th:if="${bean.isFeeder}">Küchenhilfe</span>
</div>
<div class="card-footer">
<a th:href="@{/registration/remove/{id}(id=${bean.pk})}" class="btn btn-danger">Ja, stornieren</a> &nbsp;<a th:href="@{/dashboard}" class="btn btn-outline-success">Stornierung abbrechen</a>
<a th:href="@{/registration/revoke/{id}(id=${bean.pk})}" class="btn btn-danger">Ja, stornieren</a> &nbsp;<a th:href="@{/dashboard}" class="btn btn-outline-success">Stornierung abbrechen</a>
</div>
</div>
</div>
@@ -0,0 +1,43 @@
<!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 - Freizeitbericht Grow-Up 2026</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="container">
<div class="row">
<div class="col">
<h2 class="titlefont">Grow Up 2026 - eine Zusammenfassung von Lorena</h2>
<p>
Ich war dieses Jahr zum ersten Mal bei der Grow up dabei und war wirklich überrascht, wie viel Neues ich lernen durfte. Besonders auffällig ist, dass sich die Grow up deutlich von anderen Freizeiten unterscheidet. Ein zentrales Element ist ein Arbeitsheft, in dem wir täglich eine Lektion bearbeiten, die wir anschließend in Austauschgruppen besprechen. Diese Gruppen bestehen aus etwa zehn Teilnehmern und werden ausgelost.
Im Folgenden möchte ich einen typischen Tagesablauf beschreiben.
Zunächst wurden wir um 7:30 Uhr mit unterschiedlicher Musik geweckt. Danach hatten wir eine halbe Stunde Zeit, um uns fertig zu machen. Zwischen 8:00 und 9:00 Uhr bearbeiteten wir die Lektion in unserem Heft. Anschließend gab es Frühstück.
Gegen 10:30 Uhr ging es mit gemeinsamem Singen weiter. Danach trafen wir uns in unseren Austauschgruppen, um die Lektion zusammen mit einem Mitarbeiter zu besprechen. Im Anschluss daran gab es Mittagessen.
Ab 15:30 Uhr begann das Nachmittagsprogramm. Dieses bestand aus verschiedenen Aktivitäten wie Seminaren, AGs, einem stillen Nachmittag oder auch evangelistischen Aktionen. Gegen 18:30 Uhr folgte das Abendessen.
Daraufhin begann um etwa 20:00 Uhr das Abendprogramm. Dazu gehörten beispielsweise freie Abende, Andachten, Nachtspiele, ein Frageabend zu Themen wie Ehe, Sexualität und Beziehungen, Zeugnisabende oder auch ein Lagerfeuer. Um 23:00 Uhr war offiziell Nachtruhe zumindest theoretisch. In der Praxis führten viele Teilnehmer noch Gespräche oder machten gemeinsam Quatsch.
Während der Freizeit kam es auch zu einigen lustigen Streichen. So wurden beispielsweise Mitarbeiter in ihren Zimmern „eingesperrt“, indem die Tür mit einem Besen und Kreppband blockiert wurde. Außerdem spielte jemand am 1. April um 4 Uhr morgens Musik ab und raubte uns damit den Schlaf. Darüber hinaus wurden Lebensmittel in die Tiefkühltruhe gelegt und am nächsten Tag trotzdem wieder serviert. Auch Salz und Zucker wurden vertauscht.
Insgesamt lässt sich sagen, dass die Grow up einen klaren Fokus auf die Nachfolge im Glauben legt und uns Jugendlichen konkrete Hilfestellungen bietet. Gleichzeitig schafft sie einen Raum, in dem wir neue Erfahrungen machen können. Für mich persönlich war es zum Beispiel das erste Mal, dass ich an einer evangelistischen Aktion teilgenommen habe.
Dabei erhielten wir von den Mitarbeitern Verteilmaterial wie Flyer und Bücher zum Thema Ostern. Anschließend teilten wir den Ort Olfen unter uns auf, während einige Gruppen auch in umliegende Ortschaften fuhren. Dort hatten wir die Möglichkeit, mit Bewohnern ins Gespräch zu kommen und zu lernen, wie man auf den Glauben aufmerksam machen kann.
Ein weiteres Highlight war für mich der stille Nachmittag. In dieser Zeit, die etwa zweieinhalb Stunden dauerte, konnte man seine Beziehung zu Gott reflektieren, über das Gelernte nachdenken oder sich neue Ziele setzen.
Das Thema der diesjährigen Freizeit war Daniel 17. Besonders gelungen fand ich, dass die Mitarbeiter die Inhalte so vermittelt haben, dass wir sie gut auf unser eigenes Leben übertragen konnten. Der Leitspruch der Woche lautete: „Ein Mann mit Gott ist immer in der Mehrzahl“ John Knox.
Mich hat diese Freizeit auf jeden Fall ermutigt mehr Menschen mit der frohen Botschaft zu erreichen.
</p>
</div>
</div>
</div>
</th:block>
</body>
</html>
@@ -0,0 +1,70 @@
<!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 - Osterfreizeit 2026 in Olfen</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="container">
<div class="row">
<div class="col">
<div class="blocktext">
<i>
Dieses Jahr gibt es wieder verschiedene Beiträge, die die Teilnehmer selber geschrieben haben. Damit bekommt ihr hoffentlich einen Eindruck direkt aus erster Hand. Die Reihenfolge der Beiträge
ist nach Eingang geordnet.
</i>
<hr />
<p class="titlefont">Matthias hat seine Erfahrungen in Stichpunkten gesammelt:</p>
<ul>
<li>Die Freizeit ging vom 05.04. bis zum 11.04.26</li>
<li>Es waren sehr viele Kinder und Erwachsene da.</li>
<li>Das Essen war sehr lecker.</li>
<li>Von den Kindern waren die Mädchen auf der besseren Seite, weil sie die besseren (schöneren) Klos (Bad) hatten.</li>
<li>Wir haben viele Lieder gesungen.</li>
<li>Es gab einen Missionsabend, wo 8 Leute von einem Boot da waren, die von ihrer Arbeit erzählt haben. Sie waren sehr nett. Viele von ihnen konnten nur englisch und kein deutsch. Wir haben mit ihnen Spiele gespielt, wie es auf ihrem Boot ist.</li>
<li>Es gab am Missionsabend zum Abendessen Lasagne selber gemacht von Johannes und der Küche.</li>
<li>Wir waren Rodeln und sind zur Rodelbahn von einem kleinen Parkplatz losgelaufen. Es gab immer kleine Pausen. Eine davon war auf einem Spielplatz, wo so kleine Nutrieaz leben.</li>
<li>An der Rodelbahn hatte jeder 3 Fahrten frei, und eine Menukarte kostenlos.</li>
<li>Alle hatten sehr viel Spaß. Es gab auch manchmal Verletzungen, aber keinen ernsten.</li>
<li>Jeder hat einen Ordner am Anfang der Woche bekommen, wo man seine Blätter hinein tun konnte, die man jeden Vormittag bei der allgemeinen Bibelfreude bekam.</li>
<li>Nach dem Mittagessen gab es auch einen kleinen Nachtisch und später dann noch Kiosk, wo man sich etwas Süßes kaufen konnte.</li>
<li>Jeden Tag gab es nach dem Mittagessen ungefähr 1 Stunde Zeit, wo man auf seinem Zimmer sein musste. Man konnte spielen oder seinen Ordner bearbeiten.</li>
<li>Nach der Mittagspause gab es ein Spiel für alle, meistens draußen. Zwischen dem Spiel und dem Abendessen hatte man noch freie Zeit.</li>
<li>Das Abendessen war meist etwas Leichtes wie z.B. Brot mit Wurst.</li>
<li>Am Abend gab es dann wieder ein Spiel, wo es meistens sehr spät wurde.</li>
<li>Viele Zimmer waren oft in der Nacht wach und haben verschiedene Sachen gemacht, anstatt zu schlafen.</li>
<li>Am Donnerstag gab es AGs wie z.B. Klettern, Basteln und Fussball.</li>
<li>Viele Kinder haben sehr gerne Tischtennis gespielt. Es wurden auch fleißig Lego-Autos oder Boote gebaut.</li>
<li>Jeden Tag wurden nach der Morgenandacht neue Bibelverse gelernt und auch aufgesagt. Fürs Auswendigsagen hat man eine kleine Süßigkeit bekommen.</li>
<li>Am Abend nach dem Spiel gab es noch eine kleine Andacht, die so ein bisschen auf den morgigen Tag vorbereiten sollte, die im Zimmer stattfindet vor dem Frühstück.</li>
<li>Jeder hat für die Woche noch ein kleines Heft bekommen für die Bibelfreude am Morgen (ein kleines Andachtsheft).</li>
<li>Meine Lieblingssüßigkeit im Kiosk sind so "Knusperreis", auch genannt Puffreis, oder so Anacondas (süße Schlangen)</li>
</ul>
<p class="titlefont">... und dann auch noch eine Übersicht über einen erlebten Tag geschrieben:</p>
<p>Heute ist Freitag, und ich wirde mal früher aufgeweckt, und zwar von irgendwelchen Jungs mit Wasser im Bett, denn sie haben mir mitten im Schlaf Wasser ins Gesicht gekippt. Ja, ich weiß, nervig. Egal, jetzt zum richtigen. Wir wurden um 8:00 Uhr geweckt. Dann hatten wir eine halbe Stunde Zeit, um uns fertig zu machen, bis unser Mitarbeiter zu uns ins Zimmer kommt und mit uns Bibelfreude macht. Um 9:00 Uhr gibt es das Frühstück, das sehr lecker schmeckt. Nach dem Essen haben wir eine halbe Stunde Zeit gehabt, bis es weiter ging. In dieser Zeit konnte man sein Zimmer aufräumen oder Pluspunkte sammeln. Dann hatten wir frohe Stunde, wo wir viele Lieder gesungen haben und eine spannende Geschichte hören durften. Während der Frohen Stunde sind zwei Mitarbeiter durch die Zimmer gelaufen und haben Punkte vergeben, wie sauber unser Zimmer ist. Deswegen auch die Pluspunkte, um die Gesamtpunktzahl hochzupushen. Danach gibt es nochmal etwa 10 Minuten freie Zeit, bis es Mittagessen gibt, das auch wieder sehr lecker schmeckt, wie der Nachtisch. Anschließend gibt es noch 10 Minuten ungefähr, wo es auch kurz Kiosk gibt. Dann müssen wir aufs Zimmer. Dort können wir unsere Ordner machen und sie verschönern. Heute war es besonders wichtig, weil die, die wollten, konnten heute abgeben, weil heute der letzte richtige Tag war. Dann wurden wir wieder geholt zum Spiel (heute war es ein Dorfspiel). Nach dem Spiel hatten wir freie Zeit, wo ein paar Mödchen und ich Ketten gebastelt haben. Ich habe dann auch noch den Bericht ein wenig weiter geschrieben. Beim Abendessen hatten alle sehr viel Spaß; es wurde gelacht, gegessen und Spaß gehabt. Nach dem Abendessen hatten wir wieder freie Zeit, wo z.B. Tischtennis, Klavier oder Kartenspiele gespielt wurde. Gegen 20:00 Uhr fängt die Andacht an, wo wir wieder viel lernen können. Heute gibt es ein Nachtspiel, worauf ich mich richtig freue. Wenn ihr euch wundert: ja, ich weiß, ich habe die letzten Sätze im Voraus geschrieben, das ist so, weil ich den Bericht abgeben kann, dass er es noch rechtzeitig hier auf die Homepage schafft. Es hat mir sehr viel Spaß gemacht, euch über diesen Tag von der Freizeit zu erzählen.</p>
<hr />
<p class="titlefont">Melissa (14 Jahre) schreibt:</p>
<p>Ich kann echt nicht glauben, dass die Zeit soooo schnell vergeht. Ich sitze hier, in meinem Zimmer und schreibe einen Bericht über meine offiziell <u>letzte</u> Olfenfreizeit. Vor fast vier Jahren saß ich hier und hab noch Bibelverse gelernt und jetzt... Ich bin echt dankbar, dass ich sooo viele Jahre auf diese Freizeit gehen durfte. Nicht mal wegen der Menschen hier, sondern einfach Gott näher zu kommen. Vor vier Jahren saß ich in der Andacht und habe das Lied &quot;Bin ich dabei?&quot; gesungen. Es war erst ein ganz normales Lied, bis ich an den Satz kam: &quot;Ich habe Angst, ich bin nicht dabei.&quot; Dieser Satz hat mich so zum Nachdenken gebracht, und ich kann echt nicht glauben, dass ich mich nach meiner Bekehrung auf den Namen Herrn Jesus Christus taufen durfte und jezt dabei sein kann, weil ich ein Kin Gottes bin. Diese Freizeiten bringen mich immer mehr näher zu Gott und ich staune jedes Mal neu, wie groß Gott doch wirklich ist, wie unbeschreiblich er uns liebt, uns vergibt und eine Beziehung mit uns haben möchte. Ich liebe auch die Gemeinschaft, di eman hier 24/7 hat. Das gemeinsame Bibellesen, Singen, und die Spiele machen so viel Spaß. Morgens aufzustehen und direkt mit Gottes Wort zu starten, bereichert den ganzen Tag und bringt gute Laune am Frühstückstisch. Außerdem hab ich gelernt, was wahre Freunde sind und hab diese auch hier afu den Freizeiten durch das stundenlange Kichern von früh morgens, bis spät in die Nacht kennengelernt. Manchmal, ich würde sogar sagen: <u>fast immer</u>, sind die Freunde in Christus größer, als Christus in uns selbst. Christliche Freunde sind eins der größten Schätze, die man haben kann. Selbst, wenn es am Ende nur eine Person ist. Als ich noch neu auf der Freizeit war, habe ich nie das Angebot angenommen, mit den Mitarbeitern über den Glauben offen zu reden, oder präzise Fragen zu stellen und um ehrlich zu sein: bereue ich es <u>sehr</u>. Denn mit Mitabeitern zu reden, selbst wenn es über die unnormalsten Themen geht, ist so eine Bereicherung und lässt einen noch mehr im Glauben wachsen. Sie sind immer, wirklich immer für einen da, haben ein offenes Ohr und einen Bibelvers per Rat. Sie nehmen sich so viel Zeit, egal ob es jetzt nur 5 Minuten oder 1 Stunde ist.<br />
Ich habe von diesen Freizeiten so unendlich viel Erfahrung (auch für mein späteres Leben) gesammelt, darunter wie barmherzig, gnädig und liebevoll Gott doch ist. Er hat seinen Sohn für uns geopfert, obwohl wir es nie im Leben verdient haben, nur das der Weg wieder frei ist und wir mit Gott leben können. Und auch, wenn dann mal die eigene Zimmernummer für den Küchendienst aufgerufen wird und man die Augen verdreht und versucht, noch schnell zu verhandeln, ob man nicht doch am Abend machen kann, macht Küchendienst doch irgendwie Spaß und lässt die ganze Küche von dem lauten Gesang mit einem Lächeln herumlaufen. Selbst, wenn man am Morgen von der Zimmerbewertung zwei Minuspunkte bekommen hat und die Bestechungsschokolade, welche auf dem Tisch zwischen den selbstgeschriebenen Bibelversen lag, die ungemachten Betten, den dreckigen Boden und die Socken, die im Zimmer verstreut lagen nicht entschuldigen konnten. Ich bin Gott sooo unendlich dankbar, dass ich meine Zeit so oft in Olfen mit all diesen einzigartigen Menschen verbringen konnte und kann es kaum erwarten, nächstes Jahr (auch auf Wunsch der jüngeren Teilnehmer) als Mitarbeiter auf dieser Freizeit zu sein. Gottes reichen Segen an den Leser.</p>
<hr />
<p class="titlefont">Simeon wollte uns an seinem Freitag teilnehmen lassen:</p>
<p><img th:src="@{/images/olfenosternaufdie1.png}" align="left" style="margin-right: 8px" />Heute ist Freitag... ich hatte viel Spaß, aber ich hatte auch Tage, wo es mir nicht so sehr gefallen hat. Am Montag hat es um 17:00 Uhr angefangen. Uns wurden die Regeln erklärt, und natürlich wurden auch unsere Handys eingesammelt. Danach hatten wir freie Zeit, da konnten wir spielen und frei sein. Dann hatten wir die Andacht. Wir haben natürlich auch gegessen. Auf dieser Freizeit kann man jedes Mal etwas Neues über Gott und Jesus lernen. Ich war dabei, wie sich viele Kinder bekehrt haben. Die Leute sind nett (naja... meistens zumindest). Wir spielen auch sehr viel gemeinsam, Spieleabende, Nachtwanderungen, Nachtspiele und vieles mehr. Ich bin immer so glücklich, wenn wir so wunderschöne Lieder singen. Das schönste fand ich, als wir alle zusammen am Lagerfeuer saßen und singen konnten. Ein Mitarbeiter hat uns eine Geschichte erzählt. Es war einfach wunderschön...</p>
<hr />
<p class="titlefont">Emma möchte uns auch von ihrem Tag berichten:</p>
<p><img th:src="@{/images/emmagiraffe.png}" align="right" style="margin-left: 8px" />Ich finde Olfen eine großartige Freizeit, denn hier wird einem nie langweilig, und ein Tag hier würde so ausssehen:
Während man entweder eine kurze oder lange Nacht hinte rsich hatte wegen beispielsweise Bibel lesen bis um 3 Uhr nachts oder so müde vom Vortag ist, dass man schon um 0 Uhr einschläft. Während man dann also noch im 7. Himmel ist, wird man vom Punkt 8 Uhr geweckt. Das ist für viele von uns der größte Horror. Es werden von den Mitarbeitern dann laute, lustige und fröhliche Lieder mit Begleitung der Gitarre gesungen. Anschließend haben wir dann 15 Minuten, um uns anzuziehen, Zähne zu putzen und unsere Betten zu machen. Die Meisten bleiben aber wie ich nochmal 10 Minuten im Bett, kriegen dann aber nochmal 5 Minuten von unseren üebraus lieben und geduldigen Mitarbeitern, um dann widerum ein kurzes Gebet zu sprechen und dann die Bibelfreude zu machen. In der Bibelfreude wollen die Mitarbeiter uns die frohe Botschaft mit Bibelversen, die wir nachlesen, und einem kleinen Arbeitsheft verkünden. Der Austausch ist immer sehr interessant, udn es macht auch glücklich, sich über Gott auszutauschen. Die Bibelfreude dauert in der Regel 30 Minuten. Dann endlich gibt es das lang ersehnte Frühstück.
Hierzu muss ich sagen, dass es einfach nur krass ist, wie lecker unsere Küche kochen kann, und das, egal ob du lactoseintolerant bist oder sogar glutenfrei bist. Ich danke Gott dafür täglich, dass die Küche sowas kann. Es ist alles sauber und hygienisch, was vielen Leuten auch wichtig ist, und bei uns muss der Teller aufgegessen werden, deswegen: am Anfang bisschen weniger nehmen, hilft. Nach dem Frühstück haben wir dann eine halbe Stunde, in der wir unser Zimmer putzen müssen, also Waschbecken, Boden und Tisch. Außerdem muss man den Müll bis 10 Uhr leeren. Aber die Restzeit spielen viele von uns Volleyball oder Tischtennis, was bei uns sehr beliebt ist. Nach dieser Pause haben wir frohe Stunde. Da setzen wir uns in den Saal und hören von einem Mitarbeiter eine Andacht. Nach der Andacht beten wir und lernen den Bibelvers zusammen, den dei Mitarbeiter vereinbart haben. Den schreiben wir dann in unseren Ordner ab und kriegen Arbeitsblätter. Nach dem Mittagessen ist dann Mittagspause, in der wir leise im Zimmer sitzen sollten und die gegebenen Arbeitsblätter ausfüllen müssen. Diese geht 1 Stunde, aber es fühlt sich an wie 20 Minuten.
Die Mitarbeiter helfen uns bei jeder Frage, und wenn sie einmal nicht die Antwort wissen, studieren wir zusammen in der Bibel bis wir endlich die Antwort haben. Ich persönlich finde sowas richtig cool, sich so für Gott in dieser Freizeit Zeit zu nehmen. Danach spielen wir immer wieder im Saal ein Spiel wie z.B. Kennenlernspiele oder Dorfspiele. Diese gehen meistens mehr als 2 Stunden, so dass es schon Zeit für das Abendessen ist. Nach jeder Mahlzeit wird übrigens ein Zimmer ausgewählt, das dann mmit ihren jeweiligen Mitarbeitern Küchendienst hat. Wir haben danach immer eine kleine Pause und anschließend eine Abendandacht mit Gebet und im Anschluss ein Abendspiel, Nachtspiel draußen oder Lagerfeuer. Und als Abschluss werden wir in unsere Zimmer geschickt, um uns bettfertig zu machen. Nun kommt der Mitarbeiter des Zimmers und betet mit uns. Die Meisten reden dann noch relativ laut im Zimmer, deswegen gibt es auch noch Nachtwache von bestimmten Mitarbeitern, die auch manchmal ermahnen müssen. Irgendwann ist es dann aber auch leise...<br />
<br />
Das war ein Tag in Olfen, und ich hoffe, Ihnen hat meine Beschreibung Spaß beim Lesen gemacht!
</p>
<p></p>
</div>
</div>
</div>
</div>
</th:block>
</body>
</html>
@@ -0,0 +1,41 @@
<!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 - Schweizfreizeit 2026 Freizeitbericht</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="container">
<div class="row">
<div class="col">
<div class="blocktext">
<div class="right">
<i class="far fa-clock"></i>&nbsp;Schweiz 2026
</div>
<p>Schweiz 2026 ist leider vorbei. Obwohl es mein erstes Mal war, hat es mir und anderen hoffentlich viel Spaß gemacht. Es waren zwei Wochen Spiel, Spaß und Anstrengung.</p>
<p>Am Samstag, den 01.08.2026, war der Anreisetag. Viele kamen mit dem Reisebus aus Heilbronn, andere mit dem Auto. Die eine Woche war sehr spannend: neue Gesichter kennenlernen, aber auch bekannte Gesichter aus anderen Freizeiten oder Gemeinden wiedersehen. Es gab viele Spiele, z. B. Wasserschlacht, Cup-Spiel, Nerf-Schlacht und andere spannende Spiele.</p>
<p>Am Mittwoch gab es dann für manche schon den ersten Albtraum, aber für andere auch großen Spaß: die Probewanderung. Wir sind auf die Wätterlatte gewandert, einen Berg in der Nähe vom Haus. Dort oben war eine sehr schöne Aussicht, aber auch sehr windig.</p>
<p>Am Samstag kam dann der richtige Spaß: die große Wanderung, wo es auf eine Höhe von 2840 m ging. Auf dem Weg gab es immer wieder spannende Stellen für den einen oder anderen: enge Wege, Kühe neben einem oder auch steile/enge Anstiege. An der ersten großen Pause von zwei gab es dann noch leckeren ein- und zweijährig gereiften Käse für alle, die wollten, zum Probieren.</p>
<p>Nach der Mittagspause gab es eigentlich noch den Trend, dass alle, die neu auf der Freizeit und auf der Wanderung waren, ein Schwein küssen mussten, aber zu mancher Freude gab es das nicht. Weiter oben gab es dann noch Murmeltiere, die aber sehr scheu waren. Danach hieß es für alle: bis zur nächsten großen Pause steil nach oben wandern.</p>
<p>An der nächsten Pause gab es für den einen oder anderen verwunderlicherweise auch etwas Schnee, wo fleißig damit gespielt wurde. Danach ging es leicht gerade weiter, was leider nicht so lange anhielt, denn es gab zum Ende noch mal schön viele Treppen. Oben, wo das erste von zwei Zielen erreicht war, haben die Mitarbeiter uns noch etwas Leckeres zum Trinken spendiert.</p>
<p>Dann, als alle fertig waren, war der Abstieg zum zweiten Ziel, dem Oschinensee, gefragt. Es gab wieder sehr rutschige und steile Stellen, aber alle haben das sehr gut gemeistert. Am See haben wir dann die kleine Wanderung getroffen, die dort auf uns gewartet hat. Jeder, der wollte, durfte dann auch noch ein bisschen im eiskalten Wasser baden. Kurz bevor wir den restlichen Abstieg gewagt haben, haben wir noch gesehen, wie es aussieht, wenn sich die Steine von einer Klippe lösen und zu uns herunterkommen. Ich hoffe, es hat allen dort sehr Spaß gemacht.</p>
<p>Dann ging es noch ein kleines Stück den Berg hinunter, um dann mit dem Zug und Auto den langen, anstrengenden Tag fürs Erste zu beenden.</p>
<p>Am Sonntag sind wir dann alle zusammen in die Kirche gegangen, wo wir auch ein eingeübtes Lied vorgesungen haben. Am Nachmittag haben wir noch einen kleinen Spaziergang ins nahegelegene Dorf gemacht, um dort mit dem Sessellift auf den Berg zu fahren und die Aussicht zu genießen. Den Abstieg haben manche über die Gondel genommen, andere sind runtergewandert.</p>
<p>Am Mittwoch waren wir dann am Thunersee, wo wir sehr viel Spaß hatten mit Volleyball, Gesellschaftsspielen oder Spielen im Wasser. Manche haben sich mit dem Schlamm auf dem Seeboden eingeschmiert, in der Meinung, dass das wohl sehr gesund sei. Nach dem See durften wir dann auch noch unsere Vorräte mit Snacks und anderen leckeren Sachen füllen, die wir uns kaufen konnten. Am Ende sind dann alle mit vollen Händen und hoffentlich glücklich nach Hause gekommen.</p>
<p>Am Dienstag haben wir ein Waldspiel gespielt („capture the flag“), aber mit Poolnudeln, wo jedes Team vom anderen Team vier Poolnudeln holen musste. Manchen hat das Spiel sehr viel Spaß gemacht, was man auch zwischendrin gemerkt hat, aber manche hatten auch nicht so Lust, was man so ein bisschen auch gemerkt hat. Nach zwei Runden ging es dann mit Kratzern, Wunden oder auch gar nichts zurück zum Haus, wo wir bis zum Abendessen freie Zeit hatten.</p>
<p>Am Mittwoch war am Abend noch eine Sonnenfinsternis, die aber in der Schweiz leider nicht so gut (viel) zu sehen war. Am Abend, als es schon gut dunkel war, haben wir uns auf die Terrasse gelegt und in den Sternenhimmel geschaut, in der Hoffnung, viele Sternschnuppen zu sehen.</p>
<p>Der Donnerstag war auch wieder sehr spannend, denn am Nachmittag gab es dann AGs. Nachdem der Vormittag eigentlich wie immer verlaufen ist mit Bibelfreude, Spiel und Froher Stunde , gab es dann solche AGs wie z. B. Lederarmbänder/-hänger basteln, Volleyballspielen, Bootcamp oder Leinwände und Karten bemalen zur Auswahl. Am Abend war dann der Zeugnisabend angesetzt, wo ein paar von ihrem Leben erzählt haben. Dann gab es noch die Auswahl, einen kleinen Spaziergang zu machen oder nochmals Sterne zu schauen.</p>
<p>Dann sollte es eigentlich ins Bett gehen, was aber nicht so gut funktioniert hat, denn viele Kinder waren noch sehr laut.</p>
<p>Freitag war der letzte ganze Tag (leider); der Vormittag wurde wie immer durchgeführt. Am Nachmittag wurde das Haus von allen geputzt. Samstag ging es dann wieder nach Hause.</p>
<p>Ich hoffe, es hat allen sehr viel Spaß gemacht und jeder hatte eine schöne Zeit. Auch wenn wir am Mittwoch leckeres Essen am Thunersee verloren (vergessen) haben, war die Freizeit sehr schön. Ich hoffe, ich und andere hatten sehr viel Spaß, und ich würde mich freuen, viele wieder auf dem Nachtreffen zu treffen wenn es eins gibt, was ich hoffe.</p>
<p>Liebe Grüße</p>
<p>&nbsp;</p>
<p>Matthias Holzrichter</p>
</div>
</div>
</div>
</div>
</th:block>
</body>
</html>
+27 -19
View File
@@ -1,17 +1,19 @@
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" data-bs-theme="light">
<head>
<title>Camp Organizer 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.3.2/css/bootstrap.min.css} " />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/6.5.1/css/all.min.css} " />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.3.8/css/bootstrap.min.css} " />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/7.0.1/css/all.min.css} " />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/datatables/1.13.5/css/jquery.dataTables.min.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/select2/4.0.13/css/select2.min.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/style.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/select2-bootstrap-5-theme.min.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/style.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/fancyapps__fancybox/3.5.7/dist/jquery.fancybox.min.css}">
<script th:src="@{/webjars/jquery/3.7.1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/5.3.2/js/bootstrap.bundle.min.js}"></script>
<script th:src="@{/webjars/fancyapps__fancybox/3.5.7/dist/jquery.fancybox.min.js}"></script>
<script th:src="@{/webjars/bootstrap/5.3.8/js/bootstrap.bundle.min.js}"></script>
<script th:src="@{/webjars/datatables/1.13.5/js/jquery.dataTables.min.js}"></script>
<script th:src="@{/webjars/select2/4.0.13/js/select2.full.min.js}"></script>
<script th:src="@{/js/dataTables.de.js}"></script>
@@ -21,7 +23,7 @@
</head>
<body>
<nav class="navbar sticky-top navbar-expand-lg navbar-light bg-light headerlayout navbar-background">
<span class="navbar-brand"><img th:src="@{/images/logo.png}" width="128px" style="margin-left: 8px" /></span>
<span class="navbar-brand"><a th:href="@{/allgemeines}"><span class="logo"></span></a></span>
<button class="navbar-toggler" style="margin-right: 40px" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false">
<span class="navbar-toggler-icon"></span>
@@ -29,14 +31,13 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent" style="margin-right: 20px">
<ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/ical}" class="btn btn-secondary btn-icon-silent" target="_blank" title="Freizeitdaten als ical herunterladen"><i class="far fa-calendar-alt"></i></a></li>
<li class="nav-item"><a class="btn btn-icon-silent menufont" th:href="@{/}">Startseite</a></li>
<li class="nav-item"><a class="btn btn-icon-silent menufont" th:href="@{/nachruf}">Nachruf</a></li>
<li class="nav-item"><a class="btn btn-icon-silent menufont" th:href="@{/allgemeines}">Allgemeines</a></li>
<li class="nav-item"><a class="btn btn-icon-silent menufont" th:href="@{/camplist}">Freizeiten</a></li>
<li class="nav-item"><a class="btn btn-icon-silent menufont" th:href="@{/verein}">Verein</a></li>
<li class="nav-item dropdown"><a class="nav-link dropdown-toggle btn-icon-silent menufont" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Tagebuch </a>
<li class="nav-item dropdown"><a class="nav-link dropdown-toggle btn-icon-silent menufont" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">Berichte</a>
<ul class="dropdown-menu visibledropdown" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item menufont" th:href="@{/reports/olfenostern2026}">Osterfreizeit 2026 in Olfen</a></li>
<li><a class="dropdown-item menufont" th:href="@{/reports/growup2026}">GrowUp 2026</a></li>
<li><a class="dropdown-item menufont" th:href="@{/reports/schweiz2026}">Schweiz 2026</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item menufont" th:href="@{/reports/olfenostern2023}">Osterfreizeit 2023 in Olfen</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item menufont" th:href="@{/reports/olfen2020}">Herbstfreizeit 2020 in Olfen</a></li>
@@ -65,7 +66,15 @@
<li><a class="dropdown-item menufont" th:href="@{/reports/schweiz2014}">Schweiz 2014</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item menufont" th:href="@{/reports/history}">Über 30 Jahre Onkel Werner Freizeiten Ein Rückblick ...</a></li>
</ul></li>
</ul>
</li>
<li class="nav-item dropdown"><a class="nav-link dropdown-toggle btn-icon-silent menufont" href="#" id="navbarDropdownClub" role="button" data-bs-toggle="dropdown" aria-expannded="false">Verein</a>
<ul class="dropdown-menu visibledropdown" aria-labelledby="navbarDropdownClub">
<li><a class="dropdown-item menufont" th:href="@{/nachruf}">Onkel Werner</a></li>
<li><a class="dropdown-item menufont" th:href="@{/verein}">Vereinsbeschreibung</a></li>
<li><a class="dropdown-item menufont" th:href="@{/vereinsmitglieder}">Vereinsmitglieder</a></li>
</ul>
</li>
<li class="nav-item"><a class="btn btn-icon-silent menufont" th:href="@{/kontakt}">Kontakt</a></li>
</ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('business')">
@@ -74,7 +83,7 @@
<a class="nav-link dropdown-toggle btn-icon-silent menufont" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">Abrechnung</a>
<ul class="dropdown-menu">
<li><a th:href="@{/business}" class="dropdown-item menufont">Freizeitübersicht</a></li>
<li><a th:href="@{/business/bookings}" class="dropdown-item menufont" sec:authorize="hasRole('business_booking')">Buchungsübersicht</a></li>
<li><a th:href="@{/business/bookings/{year}(year=${#dates.format(new java.util.Date(), 'yyyy')})}" class="dropdown-item menufont" sec:authorize="hasRole('business_booking')">Buchungsübersicht</a></li>
<li><a th:href="@{/business/outlay}" class="dropdown-item menufont" sec:authorize="hasRole('business_outlay')">Auslagen / Rechnungen</a>
<li><a th:href="@{/business/privileges}" class="dropdown-item menufont" sec:authorize="hasRole('admin')">Nutzerverwaltung</a></li>
</ul>
@@ -102,7 +111,8 @@
<li><a th:href="@{/admin/mail}" class="dropdown-item menufont">Testmail</a></li>
<li><a th:href="@{/admin/document}" class="dropdown-item menufont">Dokumente</a></li>
<li><a th:href="@{/admin/location}" class="dropdown-item menufont">Freizeitheime</a></li>
<li><a th:href="@{/admin/camp}" class="dropdown-item menufont">Freizeiten</a>
<li><a th:href="@{/admin/camp}" class="dropdown-item menufont">Freizeiten</a></li>
<li><a th:href="@{/admin/campdocument}" class="dropdown-item menufont">Freizeit-Dokumente</a></li>
<li class="dropdown dropend">
<a class="dropdown-item dropdown-toggle menufont" href="#" role="button" data-bs-auto-close='outside' data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Rechteverwaltung</a>
<ul class="dropdown-menu">
@@ -127,12 +137,10 @@
<li class="nav-item">
<div class="dropdown">
<button class="btn btn-hoverborder navbar-collapse" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<img th:src="@{/images/Icon_Profil.svg}" width="24px" height="24px" />
<span class="icon_profile"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end" th:if="${#strings.isEmpty(currentUser)}">
<li><a class="dropdown-item" th:href="@{/dashboard}">einloggen</a></li>
<li><hr /></li>
<li><a class="dropdown-item" th:href="@{/migration/login}">Login umziehen</a></li>
<li><a class="dropdown-item" th:href="@{/dashboard}">einloggen / registrieren</a></li>
</ul>
<ul class="dropdown-menu dropdown-menu-end" th:unless="${#strings.isEmpty(currentUser)}">
<li><a class="dropdown-item" th:href="@{${keycloakProfileUrl}}" target="_blank">Benutzername ändern</a></li>
+1 -44
View File
@@ -31,50 +31,7 @@
<h3 class="headlinefont">Der Verein</h3>
<p>Und unser Verein? Der ist eben aus genau diesen Kindern und Jugendlichen entstanden, die selbst Onkel Werners Freizeiten besucht haben und später dort Mitarbeiter geworden sind.
Wir möchten, dass seine Arbeit weitergeführt wird. Aber vor allem wollen wir, dass noch viele Kinder und Jugendliche den Herrn Jesus kennen lernen können und mit ihm leben wollen.</p>
<p>
<h3 class="headlinefont">Die Vereinsmitglieder</h3>
<p>In unserem Verein arbeiten verschiedene Freizeitbegeisterte mit, von denen sich hier ein paar vorstellen:</p>
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/simeon.velleuer.jpg}" align="left" class="rounded" style="margin-right: 8px" /> Simeon Velleuer, 41 Jahre, verheiratet mit Anja und Vater von zwei wunderbaren
Kindern, Sophie und Kaleb. Für alle „Süddeutschen“ meine Heimat liegt „im Norden“ in Mettmann.<br /> <br /> Ich darf mittlerweile auf eine über zwanzigjährige Zeit in der Mitarbeit
mit Kids und Teens zurückblicken und bin unserem gnädigen Gott und Vater dafür sehr dankbar. Er hat mich errettet und aus der Finsternis in sein wunderbares Licht berufen (1.Petr. 2,9).
Diese Botschaft möchte ich gerne weitergeben - auf Freizeiten durch die froh machende Botschaft der Bibel und durch das ganz praktische und tägliche Leben.<br /> <br /> Ich erhoffe mir
von den Freizeiten, dass wir auf ein Leben mit Gott aufmerksam machen können. Das ist spannend und herausfordernd zugleich. Durch zahlreiche Erfahrungen in meinem Leben habe ich
feststellen können, dass Gott immer den richtigen Plan für jeden ganz persönlich längst fertig hat. Wir dürfen uns ihm ganz anvertrauen.<br /> <br /> (Es ist schon ein paar Jahre her,
als ich selber noch Teilnehmer auf der wunderschönen Griesalp war. Heutzutage fahren wir ins Kiental. Großartig gelegen inmitten der faszinierenden Bergwelt des Schweizer Berner
Oberlandes. Die Kombination aus Wanderungen und Andachten ist genau das Richtige für mich...und mit Sicherheit auch für dich.)<br />
<hr />
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/joerg.henke.jpg}" align="left" class="rounded" style="margin-right: 8px" /> Jörg Henke, Vater von zwei atemberaubenden Töchtern. Aus Dresden unterstütze ich die
Freizeitarbeit v.a. im Bereich der Homepage und den Abrechnungen. Wenn ich es mal wieder schaffe, auch an einer Freizeit teilzunehmen, spiele ich gerne Gitarre und singe viele Lieder mit
den Teilnehmern.<br /> <br /> Als einer von Onkel Werners "Erben" darf ich mich an meine Bekehrung erinnern, die ich am 31.10.1993 erlebt habe. Details dazu gibt es auch hier: <a
href="https://www.jottyfan.de/bekehrung/" target="_blank">https://www.jottyfan.de/bekehrung/</a>.<br /> <br /> Gerade deswegen ist es mir wichtig, dass unsere Freizeiten stattfinden,
offen für finanziell schwächere Familien sind und wir durch die uns anvertrauten Spenden unterstützen können. Jedes Kind / jeder Teenie soll die rettende Botschaft vom Kreuz hören: dass
Jesus Christus gerade für dich gestorben und auferstanden ist, um dich von deinen Sünden zu befreien und dir damit ewiges Leben in der Gemeinschaft mit Gott schenkt. Sehr wertvoll finde
ich daher auch die Abendandachten, die es ermöglichen, genau diese Wahrheit zu verdeutlichen.<br />
<hr />
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/anna.weiser.png}" align="left" class="rounded" style="margin-right: 8px" /> Mein Name ist Anna Weiser, ich bin Ehefrau und Mutter und arbeite seit ich 18 Jahre alt
bin auf den Freizeiten mit. Schnell habe ich gemerkt, dass der Herr mir das Anliegen für die Freizeitarbeit stärker aufs Herz legt und so habe ich mit der Zeit mehr Verantwortung übernehmen
und in den Verein eintreten dürfen.<br /><br />Mir liegt es stark am Herzen, Kindern, Jugendlichen und jungen Erwachsenen vom Evangelium zu erzählen, ihnen das Gottes Wort groß zu machen,
sie in der Nachfolge von Jesus Christus zu fördern und ihnen auch über die Freizeiten hinaus in ihrem Glaubensleben und ihrer Charakterentwicklung zu helfen.<br /><br />
Seit 2020 darf ich mit einem Minijob im Verein angestellt sein und übernehme neben der Freizeitarbeit (Schwerpunkt Teenie- und Jugendfreizeiten) vor allem Aufgaben im organisatorischen
Bereich und in der Nacharbeit mit Mädels und jungen Frauen.
<br />
<hr />
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/linda.lang.png}" align="left" class="rounded" style="margin-right: 8px" /> Ich bin Linda und 22 Jahre alt. In meiner Freizeit fahre ich Inliner, reise und habe gerne
eine Kamera in der Hand. Schon als Kind durfte ich die Onkel Werner Freizeiten (vor allem die Heimfreizeiten) besuchen und auch da immer mehr über Gottes Wort lernen und nach meiner Bekehrung
weiter erkennen. Nachdem die Teilnehmer-Zeit vorbei war, durfte ich auch als Mitarbeiter auf Freizeiten unterstützen, bin aber im Verein hauptsächlich im Bereich der Website und Design tätig.<br /><br />
Das Besondere an den Onkel Werner Freizeiten ist, dass Kinder und Jugendliche nicht nur gemeinsam Spiel und Spaß haben, sondern auch hören und sehen, wie das Wort Gottes im Alltag angewandt
und erlebt werden kann. Der Mittelpunkt der Freizeiten ist Jesus Christus, der jedes einzelne Kind liebt und zu sich ruft, egal aus welchen Umständen es kommt. Wir möchten diese Gelegenheit
deshalb nutzen und sein Evangelium in den Freizeiten weitergeben.
<br />
<hr />
<p></p>
</div>
</div>
</div>
@@ -0,0 +1,82 @@
<!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 - Vereinsmitglieder</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="container">
<div class="row">
<div class="col-12">
<h3 class="headlinefont">Die Vereinsmitglieder</h3>
<p>In unserem Verein arbeiten verschiedene Freizeitbegeisterte mit, von denen sich hier ein paar vorstellen:</p>
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/simeon.velleuer.jpg}" align="left" class="rounded" style="margin-right: 8px" /> Simeon Velleuer, 41 Jahre, verheiratet mit Anja und Vater von zwei wunderbaren
Kindern, Sophie und Kaleb. Für alle „Süddeutschen“ meine Heimat liegt „im Norden“ in Mettmann.<br /> <br /> Ich darf mittlerweile auf eine über zwanzigjährige Zeit in der Mitarbeit
mit Kids und Teens zurückblicken und bin unserem gnädigen Gott und Vater dafür sehr dankbar. Er hat mich errettet und aus der Finsternis in sein wunderbares Licht berufen (1.Petr. 2,9).
Diese Botschaft möchte ich gerne weitergeben - auf Freizeiten durch die froh machende Botschaft der Bibel und durch das ganz praktische und tägliche Leben.<br /> <br /> Ich erhoffe mir
von den Freizeiten, dass wir auf ein Leben mit Gott aufmerksam machen können. Das ist spannend und herausfordernd zugleich. Durch zahlreiche Erfahrungen in meinem Leben habe ich
feststellen können, dass Gott immer den richtigen Plan für jeden ganz persönlich längst fertig hat. Wir dürfen uns ihm ganz anvertrauen.<br /> <br /> (Es ist schon ein paar Jahre her,
als ich selber noch Teilnehmer auf der wunderschönen Griesalp war. Heutzutage fahren wir ins Kiental. Großartig gelegen inmitten der faszinierenden Bergwelt des Schweizer Berner
Oberlandes. Die Kombination aus Wanderungen und Andachten ist genau das Richtige für mich...und mit Sicherheit auch für dich.)<br />
<hr />
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/joerg.henke.jpg}" align="left" class="rounded" style="margin-right: 8px" /> Jörg Henke, Vater von zwei atemberaubenden Töchtern. Aus Dresden unterstütze ich die
Freizeitarbeit v.a. im Bereich der Homepage und den Abrechnungen. Wenn ich es mal wieder schaffe, auch an einer Freizeit teilzunehmen, spiele ich gerne Gitarre und singe viele Lieder mit
den Teilnehmern.<br /> <br /> Als einer von Onkel Werners "Erben" darf ich mich an meine Bekehrung erinnern, die ich am 31.10.1993 erlebt habe. Details dazu gibt es auch hier: <a
href="https://www.jottyfan.de/bekehrung/" target="_blank">https://www.jottyfan.de/bekehrung/</a>.<br /> <br /> Gerade deswegen ist es mir wichtig, dass unsere Freizeiten stattfinden,
offen für finanziell schwächere Familien sind und wir durch die uns anvertrauten Spenden unterstützen können. Jedes Kind / jeder Teenie soll die rettende Botschaft vom Kreuz hören: dass
Jesus Christus gerade für dich gestorben und auferstanden ist, um dich von deinen Sünden zu befreien und dir damit ewiges Leben in der Gemeinschaft mit Gott schenkt. Sehr wertvoll finde
ich daher auch die Abendandachten, die es ermöglichen, genau diese Wahrheit zu verdeutlichen.<br />
<hr />
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/anna.weiser.png}" align="left" class="rounded" style="margin-right: 8px" /> Mein Name ist Anna Weiser, ich bin Ehefrau und Mutter und arbeite seit ich 18 Jahre alt
bin auf den Freizeiten mit. Schnell habe ich gemerkt, dass der Herr mir das Anliegen für die Freizeitarbeit stärker aufs Herz legt und so habe ich mit der Zeit mehr Verantwortung übernehmen
und in den Verein eintreten dürfen.<br /><br />Mir liegt es stark am Herzen, Kindern, Jugendlichen und jungen Erwachsenen vom Evangelium zu erzählen, ihnen das Gottes Wort groß zu machen,
sie in der Nachfolge von Jesus Christus zu fördern und ihnen auch über die Freizeiten hinaus in ihrem Glaubensleben und ihrer Charakterentwicklung zu helfen.<br /><br />
Seit 2020 darf ich mit einem Minijob im Verein angestellt sein und übernehme neben der Freizeitarbeit (Schwerpunkt Teenie- und Jugendfreizeiten) vor allem Aufgaben im organisatorischen
Bereich und in der Nacharbeit mit Mädels und jungen Frauen.
<br />
<hr />
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/lars.dell.png}" align="left" class="rounded" style="margin-right: 8px" /> Würdest du lieber die Victoriafälle besuchen oder die Sahara Wüste?<br />
Hallöchen, ich bin Lars; auch wenn du mit der Frage am Anfang nichts verbinden kannst, lies ruhig weiter. Man hat mich kürzlich gefragt, warum ich denn eigentlich Mitarbeiter sein möchte,
„was motiviert dich denn?“ fragt man da. Nun, um ehrlich zu sein: Oft scheinbar sehr wenig. Sehr oft einfach der Drang, das tun zu müssen, was man schon immer getan hat.
Manche vermuten ja, dass der Strohhut auf Freizeitfotos einem immer wiederkehrenden Landstreicher zuzuschreiben ist, tatsächlich ist es aber meiner. Warum aber komme ich
immer wieder? Nun, manchmal weiß ich es nicht anders zu beschreiben als ein Drängen, ein Pflichtgefühl, das mich zieht, eine Last, die man einfach nicht loskriegt, wenn man
stehenbleibt. Ich kann mich daran erinnern, dass es früher nicht so war: Als Mitarbeiter mit Ende 15 sah die Welt anders aus: Man hat sich auf die Gemeinschaft, den Spaß, die
Spiele, allgemein die Mega Gaudi auf den Freizeiten gefreut. Da waren die Freunde, die Leute, die man gern hat, vielleicht gerade deshalb, weil man sie nur ein paar Mal im
Jahr sieht. Aber wenn die ersten Weggefährten und Freunde nicht mehr kommen, stellt sich die Frage: Warum kommst du?<br />
Ich habe vorhin den Drang beschreiben, kommen zu müssen. Es ist eine Last, die in der Vorbereitung auf die Freizeiten immer leichter wird und auf den Freizeiten irgendwann
ganz verschwindet. Ich glaube, dass es daran liegt, dass man merkt, dass Gott wirken möchte. Man sieht, dass Er durch die gute Botschaft von Jesus Christus Leben verändert
und durch Sein Wort die dicksten, stursten und selbstmitleidigsten Kinder, Teens, Jugendlichen und Erwachsenen knackt. Das ist wie der freie, ungetrübte Blick auf einen
gewaltigen Wasserfall. Ich erlebe Freizeiten als solche Wasserfälle der Gnade Gottes, wo Er wirkt und wir staunend zusehen, was Er Gewaltiges tut. Und da kann ich ein
Werkzeug sein. Die Frage, die sich mir dann stellt, ist nicht mehr: Warum komme ich eigentlich? Sondern: Wo wirkt die gewaltige Gnade Gottes?<br />
Und so drängt es mich immer wieder in die Ferne: Hohenhaslach, Schweiz, Olfen egal wo, egal wer, egal wie viele - Hauptsache, es gibt dort Wasserfälle zu besichtigen.
<br />
<hr />
</div>
</div>
<div>
<h3 class="headlinefont">Ehemalige Vereinsmitglieder</h3>
<p>Leider verlassen unseren Verein auch ab und zu Mitarbeiter, bei denen sich die Lebensumstände verändert haben. Wie sie sich einst vorgestellt haben, kann hier nachgelesen werden:</p>
</div>
<div class="col-sm-12 col-md-6">
<img th:src="@{/images/linda.lang.png}" align="left" class="rounded" style="margin-right: 8px" /> Ich bin Linda und 22 Jahre alt. In meiner Freizeit fahre ich Inliner, reise und habe gerne
eine Kamera in der Hand. Schon als Kind durfte ich die Onkel Werner Freizeiten (vor allem die Heimfreizeiten) besuchen und auch da immer mehr über Gottes Wort lernen und nach meiner Bekehrung
weiter erkennen. Nachdem die Teilnehmer-Zeit vorbei war, durfte ich auch als Mitarbeiter auf Freizeiten unterstützen, bin aber im Verein hauptsächlich im Bereich der Website und Design tätig.<br /><br />
Das Besondere an den Onkel Werner Freizeiten ist, dass Kinder und Jugendliche nicht nur gemeinsam Spiel und Spaß haben, sondern auch hören und sehen, wie das Wort Gottes im Alltag angewandt
und erlebt werden kann. Der Mittelpunkt der Freizeiten ist Jesus Christus, der jedes einzelne Kind liebt und zu sich ruft, egal aus welchen Umständen es kommt. Wir möchten diese Gelegenheit
deshalb nutzen und sein Evangelium in den Freizeiten weitergeben.
<br />
<hr />
</div>
</div>
</th:block>
</body>
</html>