Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ services:
postgresql:
image: postgres:17-alpine
container_name: projekt-arendehantering-postgres
profiles: ["postgres"]
environment:
POSTGRES_DB: arende
POSTGRES_USER: arende
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.example.projektarendehantering.infrastructure.persistence;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.*;

import java.time.Instant;
import java.util.UUID;

Expand All @@ -11,6 +10,7 @@
public class CaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String status;
private UUID ownerId;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example.projektarendehantering.presentation.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class CreateCaseForm {

@NotBlank(message = "Title is required")
@Size(max = 200, message = "Title must be under 200 characters")
private String title;

@NotBlank(message = "Description is required")
@Size(max = 4000, message = "Description must be under 4000 characters")
private String description;

public CreateCaseForm() {}

public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }

public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,62 @@
package org.example.projektarendehantering.presentation.web;

import org.example.projektarendehantering.application.service.CaseService;
import org.example.projektarendehantering.presentation.dto.CaseDTO;
import org.example.projektarendehantering.presentation.dto.CreateCaseForm;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
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 jakarta.validation.Valid;
import java.util.UUID;

@Controller
public class UiController {

private final CaseService caseService;

public UiController(CaseService caseService) {
this.caseService = caseService;
}

@GetMapping("/")
public String index() {
return "index";
}

@GetMapping("/ui/cases")
public String listCases() {
public String listCases(Model model) {
model.addAttribute("cases", caseService.getAllCases());
return "cases/list";
}

@GetMapping("/ui/cases/new")
public String newCase() {
public String newCase(Model model) {
model.addAttribute("createCaseForm", new CreateCaseForm());
return "cases/new";
}

@PostMapping("/ui/cases/new")
public String createCase(@Valid @ModelAttribute("createCaseForm") CreateCaseForm form, BindingResult result) {
if (result.hasErrors()) {
return "cases/new";
}

CaseDTO caseDTO = new CaseDTO();
caseDTO.setTitle(form.getTitle());
caseDTO.setDescription(form.getDescription());

caseService.createCase(caseDTO);
return "redirect:/ui/cases";
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

@GetMapping("/ui/cases/{caseId}")
public String caseDetail(@PathVariable String caseId, Model model) {
model.addAttribute("caseId", caseId);
public String caseDetail(@PathVariable UUID caseId, Model model) {
caseService.getCase(caseId).ifPresent(c -> model.addAttribute("case", c));
return "cases/detail";
}
}
Expand Down
123 changes: 0 additions & 123 deletions src/main/resources/static/app.js

This file was deleted.

30 changes: 18 additions & 12 deletions src/main/resources/templates/cases/detail.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Case</title>
<link rel="stylesheet" href="/app.css"/>
<script defer src="/app.js"></script>
<head th:replace="~{fragments/head :: head(title='Case')}">
</head>
<body>
<div th:replace="~{fragments/header :: header}"></div>
Expand All @@ -16,12 +11,23 @@ <h1>Case</h1>
<a class="button button-secondary" href="/ui/cases">Back to list</a>
</div>

<section class="panel">
<h2>Case id</h2>
<code th:text="${caseId}">case-id</code>
<p class="muted">
This is a placeholder until <code>GET /cases/{id}</code> exists. Once it does, we can populate this view with real data.
</p>
<section class="panel" th:if="${case != null}">
<div class="kv">
<div class="k">Title</div>
<div class="v" th:text="${case.title}">Title</div>
</div>
<div class="kv">
<div class="k">Description</div>
<div class="v" th:text="${case.description}">Description</div>
</div>
<div class="kv">
<div class="k">Created At</div>
<div class="v" th:text="${case.createdAt}">Date</div>
</div>
</section>
<section class="panel panel-error" th:if="${case == null}">
<h2>Error</h2>
<p>Case not found.</p>
</section>
</main>

Expand Down
34 changes: 20 additions & 14 deletions src/main/resources/templates/cases/list.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Cases</title>
<link rel="stylesheet" href="/app.css"/>
<script defer src="/app.js"></script>
<head th:replace="~{fragments/head :: head(title='Cases')}">
</head>
<body>
<div th:replace="~{fragments/header :: header}"></div>
Expand All @@ -17,14 +12,25 @@ <h1>Cases</h1>
</div>

<section class="panel">
<h2>List (placeholder)</h2>
<p class="muted">
This page is ready to be wired up once you add read endpoints (e.g. <code>GET /cases</code>, <code>GET /cases/{id}</code>).
</p>
<div class="hint">
Tip: you can still open a specific case page by URL:
<code>/ui/cases/&lt;caseId&gt;</code>
</div>
<table class="table" th:if="${not #lists.isEmpty(cases)}">
<thead>
<tr>
<th>Title</th>
<th>Created</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="c : ${cases}">
<td th:text="${c.title}">Title</td>
<td th:text="${c.createdAt}">Date</td>
<td>
<a th:href="@{/ui/cases/{id}(id=${c.id})}" class="button button-secondary">View</a>
</td>
</tr>
</tbody>
</table>
<p th:if="${#lists.isEmpty(cases)}" class="muted">No cases found.</p>
</section>
</main>

Expand Down
33 changes: 7 additions & 26 deletions src/main/resources/templates/cases/new.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Create case</title>
<link rel="stylesheet" href="/app.css"/>
<script defer src="/app.js"></script>
<head th:replace="~{fragments/head :: head(title='Create case')}">
</head>
<body>
<div th:replace="~{fragments/header :: header}"></div>
Expand All @@ -16,35 +11,21 @@ <h1>Create case</h1>
<a class="button button-secondary" href="/ui/cases">Back to list</a>
</div>

<form class="form" id="createCaseForm">
<form class="form" th:action="@{/ui/cases/new}" th:object="${createCaseForm}" method="post">
<label class="field">
<span class="label">Title</span>
<input class="input" name="title" maxlength="200" required/>
<input class="input" th:field="*{title}" maxlength="200"/>
<span class="muted" th:if="${#fields.hasErrors('title')}" th:errors="*{title}" style="color: var(--danger)">Error</span>
</label>
<label class="field">
<span class="label">Description</span>
<textarea class="input" name="description" rows="6" maxlength="4000" required></textarea>
<textarea class="input" th:field="*{description}" rows="6"></textarea>
<span class="muted" th:if="${#fields.hasErrors('description')}" th:errors="*{description}" style="color: var(--danger)">Error</span>
</label>
<div class="actions">
<button class="button" type="submit">Create</button>
<button class="button" type="submit">Create Case</button>
</div>
</form>

<section class="panel" id="createCaseResult" hidden>
<h2>Created</h2>
<div class="kv">
<div class="k">caseId</div>
<div class="v"><code id="createdCaseId"></code></div>
</div>
<div class="actions">
<a class="button" id="openCaseLink" href="#">Open case page</a>
</div>
</section>

<section class="panel panel-error" id="createCaseError" hidden>
<h2>Error</h2>
<pre class="pre" id="createCaseErrorText"></pre>
</section>
</main>

<div th:replace="~{fragments/footer :: footer}"></div>
Expand Down
Loading