show current camps on start page
This commit is contained in:
		| @@ -55,8 +55,12 @@ public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { | |||||||
| 	@Override | 	@Override | ||||||
| 	protected void configure(HttpSecurity http) throws Exception { | 	protected void configure(HttpSecurity http) throws Exception { | ||||||
| 		super.configure(http); | 		super.configure(http); | ||||||
| 		http.authorizeRequests().antMatchers("/**").permitAll(); | 		http.authorizeRequests() | ||||||
| 		http.anonymous().disable(); | 		// @formatter:off | ||||||
|  | 		  .antMatchers("/user/**", "/business/**").authenticated() | ||||||
|  |   		.anyRequest().permitAll(); | ||||||
|  |   	// @formatter:on | ||||||
|  | //		http.anonymous().disable(); | ||||||
| 		http.csrf().disable(); | 		http.csrf().disable(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | package de.jottyfan.camporganizer.module.common; | ||||||
|  |  | ||||||
|  | import javax.servlet.http.HttpServletRequest; | ||||||
|  |  | ||||||
|  | import org.jooq.exception.DataAccessException; | ||||||
|  | import org.keycloak.KeycloakSecurityContext; | ||||||
|  | import org.springframework.ui.Model; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author jotty | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public abstract class CommonController { | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * try to get current keycloak user | ||||||
|  | 	 * | ||||||
|  | 	 * @param request the request | ||||||
|  | 	 * @return the preferred username or null | ||||||
|  | 	 */ | ||||||
|  | 	private String getCurrentUser(HttpServletRequest request) { | ||||||
|  | 		KeycloakSecurityContext ksc = (KeycloakSecurityContext) request | ||||||
|  | 				.getAttribute(KeycloakSecurityContext.class.getName()); | ||||||
|  | 		return ksc == null ? null : ksc.getIdToken().getPreferredUsername(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * setup the session for the template | ||||||
|  | 	 * @param model the model | ||||||
|  | 	 * @param request the request | ||||||
|  | 	 */ | ||||||
|  | 	public void setupSession(Model model, HttpServletRequest request) { | ||||||
|  | 		String username = getCurrentUser(request); | ||||||
|  | 		model.addAttribute("currentUser", username); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -7,6 +7,7 @@ import org.apache.logging.log4j.LogManager; | |||||||
| import org.apache.logging.log4j.Logger; | import org.apache.logging.log4j.Logger; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
| import org.springframework.stereotype.Controller; | import org.springframework.stereotype.Controller; | ||||||
|  | import org.springframework.ui.Model; | ||||||
| import org.springframework.web.bind.annotation.GetMapping; | import org.springframework.web.bind.annotation.GetMapping; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -15,18 +16,27 @@ import org.springframework.web.bind.annotation.GetMapping; | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| @Controller | @Controller | ||||||
| public class IndexController { | public class IndexController extends CommonController { | ||||||
| 	private static final Logger LOGGER = LogManager.getLogger(IndexController.class); | 	private static final Logger LOGGER = LogManager.getLogger(IndexController.class); | ||||||
|  |  | ||||||
| 	@Autowired | 	@Autowired | ||||||
| 	private HttpServletRequest request; | 	private HttpServletRequest request; | ||||||
|  |  | ||||||
|  | 	@Autowired | ||||||
|  | 	private IndexService service; | ||||||
|  |  | ||||||
| 	@GetMapping("/") | 	@GetMapping("/") | ||||||
| 	public String index() { | 	public String index(Model model) { | ||||||
| 		if (request == null) { | 		super.setupSession(model, request); | ||||||
| 			LOGGER.error("request is null"); | 		model.addAttribute("camps", service.getAllCamps()); | ||||||
|  | 		return "/index"; | ||||||
| 	} | 	} | ||||||
| 		return "/"; |  | ||||||
|  | 	@GetMapping("/user") | ||||||
|  | 	public String user(Model model) { | ||||||
|  | 		super.setupSession(model, request); | ||||||
|  | 		model.addAttribute("camps", service.getAllCamps()); | ||||||
|  | 		return "/user/index"; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@GetMapping("/logout") | 	@GetMapping("/logout") | ||||||
|   | |||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | package de.jottyfan.camporganizer.module.common; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.Stream; | ||||||
|  |  | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  |  | ||||||
|  | import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author jotty | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | @Service | ||||||
|  | public class IndexService { | ||||||
|  | 	@Autowired | ||||||
|  | 	private IndexGateway gateway; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * get all camps from the database and prepare them for the view | ||||||
|  | 	 * | ||||||
|  | 	 * @return the list of found camps | ||||||
|  | 	 */ | ||||||
|  | 	public List<VCampRecord> getAllCamps() { | ||||||
|  | 		Stream<VCampRecord> stream = gateway.getAllCamps(); | ||||||
|  | 		List<VCampRecord> list = new ArrayList<>(); | ||||||
|  | 		stream.forEach(o -> list.add(o)); | ||||||
|  | 		return list; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | package de.jottyfan.camporganizer.module.common; | ||||||
|  |  | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.boot.info.BuildProperties; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author jotty | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | @Component | ||||||
|  | public class ManifestBean { | ||||||
|  |  | ||||||
|  | 	@Autowired(required = false) | ||||||
|  | 	private BuildProperties buildProperties; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return the version of this project | ||||||
|  | 	 */ | ||||||
|  | 	public String getVersion() { | ||||||
|  | 		return buildProperties != null ? buildProperties.getVersion() | ||||||
|  | 				: this.getClass().getPackage().getImplementationVersion(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -7,7 +7,7 @@ spring.datasource.password=jooq | |||||||
| # application | # application | ||||||
| server.port = 8081 | server.port = 8081 | ||||||
|  |  | ||||||
| server.servlet.context-path=/COBusiness | server.servlet.context-path=/CampOrganizer2 | ||||||
|  |  | ||||||
| # keycloak | # keycloak | ||||||
| keycloak.auth-server-url = http://localhost:8080/ | keycloak.auth-server-url = http://localhost:8080/ | ||||||
| @@ -15,7 +15,7 @@ keycloak.realm = ow | |||||||
| keycloak.resource = biblecamp | keycloak.resource = biblecamp | ||||||
| keycloak.public-client = true | keycloak.public-client = true | ||||||
| keycloak.security-constraints[0].authRoles[0] = business | keycloak.security-constraints[0].authRoles[0] = business | ||||||
| keycloak.security-constraints[0].securityCollections[0].patterns[0] = /* | keycloak.security-constraints[0].securityCollections[0].patterns[0] = /business/* | ||||||
| #keycloak.credentia | #keycloak.credentia | ||||||
| keycloak.use-resource-role-mappings=true | keycloak.use-resource-role-mappings=true | ||||||
| #keycloak.bearer-only=true | #keycloak.bearer-only=true | ||||||
|   | |||||||
							
								
								
									
										100
									
								
								src/main/resources/static/css/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/main/resources/static/css/style.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | html { | ||||||
|  | 	width: 100vw; | ||||||
|  | 	height: 100vh; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | body { | ||||||
|  | 	background-image: linear-gradient(to bottom right, rgb(255, 190, 111), rgb(198, 70, 0)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .mainpage { | ||||||
|  | 	background-color: rgba(255, 255, 255, 0.4); | ||||||
|  | 	padding: 8px; | ||||||
|  | 	overflow: auto; | ||||||
|  | 	width: 100%; | ||||||
|  | 	height: calc(100vh - 60px); /* 60 px is the current height of the headline; TODO: calculate it */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .middlecenter { | ||||||
|  | 	margin: auto; | ||||||
|  | 	top: 40vh; | ||||||
|  | 	bottom: 40vh; | ||||||
|  | 	position: fixed; | ||||||
|  | 	left: 40vw; | ||||||
|  | 	right: 40vw; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .myheadline { | ||||||
|  | 	margin: 8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .bottomdist16 { | ||||||
|  | 	margin-bottom: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .topright { | ||||||
|  | 	position: fixed; | ||||||
|  | 	right: 8px; | ||||||
|  | 	top: 8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tablelink { | ||||||
|  | 	cursor: pointer; | ||||||
|  | 	text-decoration: none; | ||||||
|  | 	color: black; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tablelink:hover { | ||||||
|  | 	text-decoration: underline; | ||||||
|  | 	color: darkcyan; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .deletelink { | ||||||
|  | 	padding: 4px; | ||||||
|  | 	border: 1px solid rgba(0, 0, 0, 0.0); | ||||||
|  | 	border-radius: 4px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .deletelink:hover { | ||||||
|  | 	color: red; | ||||||
|  | 	border: 1px solid red; | ||||||
|  | 	text-decoration-line: line-through; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .linkbtn { | ||||||
|  | 	background: transparent; | ||||||
|  | 	border: 2px solid transparent; | ||||||
|  | 	padding: 8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .linkbtn:hover { | ||||||
|  | 	background-color: white; | ||||||
|  | 	border: 2px solid silver; | ||||||
|  | 	border-radius: 4px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .btn-icon-silent { | ||||||
|  | 	background: transparent; | ||||||
|  | 	border: 2px solid transparent; | ||||||
|  | 	padding: 8px; | ||||||
|  | 	color: silver; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .btn-icon-silent:hover { | ||||||
|  | 	background-color: white; | ||||||
|  | 	border: 2px solid silver; | ||||||
|  | 	border-radius: 4px; | ||||||
|  | 	color: darkcyan; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .mytoggle_collapsed { | ||||||
|  | 	display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .mytoggle_btn { | ||||||
|  | 	cursor: pointer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .mytoggle_btn:hover { | ||||||
|  | 	background-color: #abcdef; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								src/main/resources/static/js/mytoggle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/main/resources/static/js/mytoggle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | class MyToggle { | ||||||
|  | 	constructor() { | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	toggle(divid) { | ||||||
|  | 		$("[id='" + divid + "']").toggle(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								src/main/resources/templates/error.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/main/resources/templates/error.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html xmlns:th="http://www.thymeleaf.org"> | ||||||
|  |  | ||||||
|  | <head> | ||||||
|  | <title>Fehler</title> | ||||||
|  | <link th:rel="stylesheet" th:href="@{/css/style.css}" /> | ||||||
|  | <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.1.3/css/bootstrap.min.css} " /> | ||||||
|  | <script th:src="@{/webjars/bootstrap/5.1.3/js/bootstrap.min.js}"></script> | ||||||
|  | </head> | ||||||
|  |  | ||||||
|  | <body> | ||||||
|  |   <div class="middlecenter"> | ||||||
|  |     <div class="alert alert-danger" role="alert"> | ||||||
|  |       <p>Es ist ein Fehler aufgetreten.</p> | ||||||
|  |       <a th:href="@{/}" class="linkbtn">Ach, Mist...</a> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </body> | ||||||
|  |  | ||||||
|  | </html> | ||||||
							
								
								
									
										61
									
								
								src/main/resources/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/resources/templates/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html th:replace="~{template :: layout(~{::title}, ~{::libs}, ~{::header}, ~{::content})}" xmlns:th="http://www.thymeleaf.org" 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" /> | ||||||
|  | <libs></libs> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | 	<header> | ||||||
|  | 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="aktualisieren"><i class="fas fa-sync"></i></a> | ||||||
|  | 		<span>Das Buchungsportal dess Onkel Werner Freizeiten e.V.</span> | ||||||
|  | 	</header> | ||||||
|  | 	<content> | ||||||
|  | 	<div class="mainpage"> | ||||||
|  | 		<script type="text/javascript"> | ||||||
|  | 			var mytoggle = new MyToggle(); | ||||||
|  | 		</script> | ||||||
|  | 		<h1>Unsere Freizeiten</h1> | ||||||
|  | 		<div class="card bottomdist16" th:each="c : ${camps}"> | ||||||
|  | 			<div class="card-header mytoggle_btn" th:onclick="mytoggle.toggle('campdiv_[[${c.pk}]]')"> | ||||||
|  | 				<span th:text="${c.name}"></span> <span th:text="${#numbers.formatInteger(c.year, 0)}" th:if="${c.year != null}"></span> | ||||||
|  | 			</div> | ||||||
|  | 			<div th:id="'campdiv_' + ${c.pk}" class="card-body mytoggle_collapsed"> | ||||||
|  | 				<div class="container"> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Ort</div> | ||||||
|  | 						<div class="col-sm-9"> | ||||||
|  | 							<a th:href="${c.url}" th:text="${c.locationName}" target="_blank"></a> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Jungen und Mädchen</div> | ||||||
|  | 						<div class="col-sm-9" th:text="${c.minAge} + ' - ' + ${c.maxAge}"></div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Zeit</div> | ||||||
|  | 						<div class="col-sm-9"> | ||||||
|  | 							<span th:text="${#temporals.format(c.arrive, 'dd.MM.')} + ' - ' + ${#temporals.format(c.depart, 'dd.MM.yyyy')}" th:if="${c.arrive != null &&  c.depart != null}"></span> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Preis</div> | ||||||
|  | 						<div class="col-sm-9" th:text="${c.price}"></div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Ferien</div> | ||||||
|  | 						<div class="col-sm-9" th:text="${c.countries}"></div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3"></div> | ||||||
|  | 						<div class="col-sm-9"> | ||||||
|  | 							<button class="btn btn-primary">jetzt anmelden</button> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 	</content> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										33
									
								
								src/main/resources/templates/template.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/main/resources/templates/template.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html th:fragment="layout (title, libs, header, content)" xmlns:th="http://www.thymeleaf.org"> | ||||||
|  | <head> | ||||||
|  | <title th:replace="${title}">Camp Organizer Business</title> | ||||||
|  | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||||
|  | <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.2.0/css/bootstrap.min.css} " /> | ||||||
|  | <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/5.15.4/css/all.css} " /> | ||||||
|  | <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/datatables/1.11.4/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}" /> | ||||||
|  | <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> | ||||||
|  | <script th:src="@{/webjars/bootstrap/5.2.0/js/bootstrap.bundle.min.js}"></script> | ||||||
|  | <script th:src="@{/webjars/datatables/1.11.4/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> | ||||||
|  | <script th:src="@{/js/mytoggle.js}"></script> | ||||||
|  | <meta th:replace="${libs}"></meta> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | 	<div class="myheadline"> | ||||||
|  | 		<div class="topright linkbtn"> | ||||||
|  | 			<span th:if="${not #strings.isEmpty(currentUser)}"> | ||||||
|  | 				<b th:inline="text">[[${currentUser}]]</b> aus Version <span th:text="${@manifestBean.getVersion()}"></span> <a th:href="@{/logout}">ausloggen</a> | ||||||
|  | 			</span> | ||||||
|  | 			<span th:if="${#strings.isEmpty(currentUser)}"> | ||||||
|  | 				<a th:href="@{/user}">einloggen</a> | ||||||
|  | 			</span> | ||||||
|  | 		</div> | ||||||
|  | 		<span th:replace="${header}">Layout header</span> | ||||||
|  | 	</div> | ||||||
|  | 	<div th:replace="${content}">Layout content</div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										61
									
								
								src/main/resources/templates/user/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/resources/templates/user/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html th:replace="~{template :: layout(~{::title}, ~{::libs}, ~{::header}, ~{::content})}" xmlns:th="http://www.thymeleaf.org" 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" /> | ||||||
|  | <libs></libs> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | 	<header> | ||||||
|  | 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="aktualisieren"><i class="fas fa-sync"></i></a> | ||||||
|  | 		<span>Das Buchungsportal dess Onkel Werner Freizeiten e.V.</span> | ||||||
|  | 	</header> | ||||||
|  | 	<content> | ||||||
|  | 	<div class="mainpage"> | ||||||
|  | 		<script type="text/javascript"> | ||||||
|  | 			var mytoggle = new MyToggle(); | ||||||
|  | 		</script> | ||||||
|  | 		<h1>Unsere Freizeiten</h1> | ||||||
|  | 		<div class="card bottomdist16" th:each="c : ${camps}"> | ||||||
|  | 			<div class="card-header mytoggle_btn" th:onclick="mytoggle.toggle('campdiv_[[${c.pk}]]')"> | ||||||
|  | 				<span th:text="${c.name}"></span> <span th:text="${#numbers.formatInteger(c.year, 0)}" th:if="${c.year != null}"></span> | ||||||
|  | 			</div> | ||||||
|  | 			<div th:id="'campdiv_' + ${c.pk}" class="card-body mytoggle_collapsed"> | ||||||
|  | 				<div class="container"> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Ort</div> | ||||||
|  | 						<div class="col-sm-9"> | ||||||
|  | 							<a th:href="${c.url}" th:text="${c.locationName}" target="_blank"></a> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Jungen und Mädchen</div> | ||||||
|  | 						<div class="col-sm-9" th:text="${c.minAge} + ' - ' + ${c.maxAge}"></div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Zeit</div> | ||||||
|  | 						<div class="col-sm-9"> | ||||||
|  | 							<span th:text="${#temporals.format(c.arrive, 'dd.MM.')} + ' - ' + ${#temporals.format(c.depart, 'dd.MM.yyyy')}" th:if="${c.arrive != null &&  c.depart != null}"></span> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Preis</div> | ||||||
|  | 						<div class="col-sm-9" th:text="${c.price}"></div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3">Ferien</div> | ||||||
|  | 						<div class="col-sm-9" th:text="${c.countries}"></div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="row"> | ||||||
|  | 						<div class="col-sm-3"></div> | ||||||
|  | 						<div class="col-sm-9"> | ||||||
|  | 							<button class="btn btn-primary">jetzt anmelden</button> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 	</content> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
		Reference in New Issue
	
	Block a user