Skip to content

Commit bcb4812

Browse files
committed
fix/qg-270: исправлено сохранение черновиков в случае ошибки отправки формы карточки клиента
1 parent 102d61f commit bcb4812

7 files changed

Lines changed: 62 additions & 41 deletions

File tree

app/src/main/kotlin/pro/qyoga/app/therapist/clients/cards/CreateClientCardPageController.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import org.springframework.ui.Model
66
import org.springframework.web.bind.annotation.GetMapping
77
import org.springframework.web.bind.annotation.PostMapping
88
import org.springframework.web.bind.annotation.RequestMapping
9-
import org.springframework.web.servlet.ModelAndView
109
import pro.azhidkov.platform.kotlin.value
10+
import pro.azhidkov.platform.spring.http.hxRedirect
1111
import pro.qyoga.app.platform.notFound
1212
import pro.qyoga.core.clients.cards.Client
1313
import pro.qyoga.core.clients.cards.ClientsRepo
@@ -25,26 +25,30 @@ class CreateClientCardPageController(
2525

2626
@GetMapping("/create")
2727
fun getCreateClientPage(model: Model): String {
28-
model.addAttribute("formAction", "/therapist/clients/create")
28+
model.addAttribute("formAction", FORM_ACTION)
2929
return "therapist/clients/client-create"
3030
}
3131

3232
@PostMapping("/create")
3333
fun createClient(
3434
clientCardDto: ClientCardDto,
3535
@AuthenticationPrincipal principal: QyogaUserDetails,
36-
): ModelAndView {
36+
): Any {
3737
val res = runCatching {
3838
clientsRepo.save(Client(principal.id, clientCardDto))
3939
}
4040

4141
return when (res.value()) {
42-
is Client -> ModelAndView("redirect:/therapist/clients")
42+
is Client -> hxRedirect("/therapist/clients", "HX-Trigger" to "formSaved")
4343

4444
null -> notFound
45-
is DuplicatedPhoneException -> editClientFormWithValidationError(clientCardDto)
45+
is DuplicatedPhoneException -> editClientFormWithValidationError(FORM_ACTION, clientCardDto)
4646
else -> throw res.exceptionOrNull()!!
4747
}
4848
}
4949

50+
companion object {
51+
private const val FORM_ACTION = "/therapist/clients/create"
52+
}
53+
5054
}

app/src/main/kotlin/pro/qyoga/app/therapist/clients/cards/EditClientCardPageController.kt

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ package pro.qyoga.app.therapist.clients.cards
22

33
import org.springframework.data.repository.findByIdOrNull
44
import org.springframework.stereotype.Controller
5-
import org.springframework.ui.ModelMap
65
import org.springframework.web.bind.annotation.GetMapping
76
import org.springframework.web.bind.annotation.PathVariable
87
import org.springframework.web.bind.annotation.PostMapping
98
import org.springframework.web.servlet.ModelAndView
109
import pro.azhidkov.platform.kotlin.value
11-
import pro.azhidkov.platform.spring.mvc.viewId
10+
import pro.azhidkov.platform.spring.http.hxRedirect
1211
import pro.qyoga.app.platform.notFound
13-
import pro.qyoga.app.therapist.clients.ClientPageFragmentModel
1412
import pro.qyoga.app.therapist.clients.ClientPageModel
1513
import pro.qyoga.app.therapist.clients.ClientPageTab
1614
import pro.qyoga.core.clients.cards.ClientsRepo
@@ -20,19 +18,6 @@ import pro.qyoga.core.clients.cards.model.Client
2018
import pro.qyoga.core.clients.cards.patchedBy
2119
import java.util.*
2220

23-
data class EditClientCardPageModel(
24-
private val formAction: String
25-
) : ClientPageFragmentModel,
26-
ModelAndView(
27-
viewId("/therapist/clients/client-edit"),
28-
mapOf(
29-
"formAction" to formAction
30-
)
31-
) {
32-
33-
override val model: ModelMap = super<ModelAndView>.modelMap
34-
35-
}
3621

3722
@Controller
3823
class EditClientCardPageController(
@@ -49,26 +34,24 @@ class EditClientCardPageController(
4934
return ClientPageModel(
5035
client,
5136
ClientPageTab.CARD,
52-
EditClientCardPageModel(
53-
"/therapist/clients/${client.id}/card"
54-
)
37+
EditClientCardPageModel(formAction(client.id))
5538
)
5639
}
5740

5841
@PostMapping(EDIT_CLIENT_CARD_PAGE_PATH)
5942
fun editClientCard(
6043
clientCardDto: ClientCardDto,
6144
@PathVariable clientId: UUID
62-
): ModelAndView {
45+
): Any {
6346
val res = runCatching {
6447
clientsRepo.updateById(clientId) { client -> client.patchedBy(clientCardDto) }
6548
}
6649

6750
return when (res.value()) {
68-
is Client -> ModelAndView("redirect:/therapist/clients")
51+
is Client -> hxRedirect("/therapist/clients", "HX-Trigger" to "formSaved")
6952

7053
null -> notFound
71-
is DuplicatedPhoneException -> editClientFormWithValidationError(clientCardDto)
54+
is DuplicatedPhoneException -> editClientFormWithValidationError(formAction(clientId), clientCardDto)
7255
else -> throw res.exceptionOrNull()!!
7356
}
7457
}
@@ -79,4 +62,6 @@ class EditClientCardPageController(
7962

8063
}
8164

82-
}
65+
}
66+
67+
private fun formAction(clientId: UUID): String = "/therapist/clients/${clientId}/card"

app/src/main/kotlin/pro/qyoga/app/therapist/clients/cards/EditClientPageModel.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
package pro.qyoga.app.therapist.clients.cards
22

3+
import org.springframework.ui.ModelMap
4+
import org.springframework.web.servlet.ModelAndView
35
import pro.azhidkov.platform.spring.mvc.modelAndView
6+
import pro.azhidkov.platform.spring.mvc.viewId
7+
import pro.qyoga.app.therapist.clients.ClientPageFragmentModel
48
import pro.qyoga.core.clients.cards.dtos.ClientCardDto
59

10+
data class EditClientCardPageModel(
11+
private val formAction: String,
12+
private val duplicatedPhone: Boolean = false
13+
) : ClientPageFragmentModel,
14+
ModelAndView(
15+
viewId("/therapist/clients/client-edit"),
16+
mapOf(
17+
"formAction" to formAction,
18+
"duplicatedPhone" to duplicatedPhone
19+
)
20+
) {
21+
22+
override val model: ModelMap = super<ModelAndView>.modelMap
23+
24+
}
625

7-
fun editClientFormWithValidationError(clientCardDto: ClientCardDto) =
26+
fun editClientFormWithValidationError(formAction: String, clientCardDto: ClientCardDto) =
827
modelAndView(
928
"therapist/clients/client-create", mapOf(
29+
"formAction" to formAction,
1030
"client" to clientCardDto,
1131
"duplicatedPhone" to true
1232
)

app/src/main/resources/templates/therapist/clients/client-card-fragment.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<div class="client__container" id="clientCardTab">
2-
<form class="form__client-card" id="createClientForm" method="post" th:action="${formAction}"
2+
<form class="form__client-card" hx-select="#createClientForm"
33
th:fragment="createClientForm"
4+
hx-swap="outerHTML"
5+
id="createClientForm"
6+
th:hx-post="${formAction}"
47
x-data="clientCardData()"
58
x-init="$watch('form', (form) => saveLocalState(form))">
69

@@ -51,6 +54,11 @@
5154
});
5255
});
5356

57+
document.body.addEventListener('formSaved', function () {
58+
console.debug('Form saved successfully, resetting local storage');
59+
resetLocalState();
60+
});
61+
5462
</script>
5563

5664
<input
@@ -238,7 +246,7 @@
238246
</a>
239247
</div>
240248
<div class="col-6 col-sm-auto text-center">
241-
<button @click="resetLocalState()" class="btn btn-outline-success" name="confirmButton"
249+
<button class="btn btn-outline-success" name="confirmButton"
242250
style="min-width: 110px;"
243251
>
244252
Сохранить

app/src/test/kotlin/pro/qyoga/tests/cases/app/therapist/clients/card/EditClientCardPageTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import io.kotest.inspectors.forNone
55
import org.junit.jupiter.api.DisplayName
66
import org.junit.jupiter.api.Test
77
import org.springframework.http.HttpStatus
8+
import pro.qyoga.core.clients.cards.model.PhoneNumber
89
import pro.qyoga.core.clients.cards.model.toUIFormat
910
import pro.qyoga.core.clients.cards.toDto
10-
import pro.qyoga.tests.assertions.shouldBe
11-
import pro.qyoga.tests.assertions.shouldBePage
12-
import pro.qyoga.tests.assertions.shouldHaveElement
13-
import pro.qyoga.tests.assertions.shouldMatch
11+
import pro.qyoga.tests.assertions.*
1412
import pro.qyoga.tests.clients.TherapistClient
1513
import pro.qyoga.tests.fixture.object_mothers.clients.ClientsObjectMother
1614
import pro.qyoga.tests.fixture.object_mothers.clients.ClientsObjectMother.createClientCardDto
@@ -19,6 +17,7 @@ import pro.qyoga.tests.fixture.object_mothers.therapists.THE_THERAPIST_ID
1917
import pro.qyoga.tests.infra.web.QYogaAppIntegrationBaseTest
2018
import pro.qyoga.tests.pages.publc.NotFoundErrorPage
2119
import pro.qyoga.tests.pages.therapist.clients.card.CreateClientForm
20+
import pro.qyoga.tests.pages.therapist.clients.card.EditClientForm
2221
import pro.qyoga.tests.pages.therapist.clients.card.EditClientPage
2322

2423

@@ -132,6 +131,7 @@ class EditClientCardPageTest : QYogaAppIntegrationBaseTest() {
132131
therapist.clients.editClientForError(existingClient.id, existingClientDto.copy(phoneNumber = thePhone))
133132

134133
// Проверка
134+
document shouldBeElement EditClientForm.clientForm(existingClient.copy(phoneNumber = PhoneNumber.of(thePhone)))
135135
document shouldHaveElement CreateClientForm.invalidPhoneInput
136136
}
137137

app/src/test/kotlin/pro/qyoga/tests/clients/api/TherapistClientsApi.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ class TherapistClientsApi(override val authCookie: Cookie) : AuthorizedApi {
8181
} When {
8282
post(CreateClientForm.action.url)
8383
} Then {
84-
statusCode(HttpStatus.FOUND.value())
85-
header("Location", endsWith(ClientsListPage.path))
84+
statusCode(HttpStatus.OK.value())
85+
header("HX-Redirect", endsWith(ClientsListPage.path))
86+
header("HX-Trigger", "formSaved")
8687
}
8788
}
8889

@@ -107,8 +108,9 @@ class TherapistClientsApi(override val authCookie: Cookie) : AuthorizedApi {
107108
} When {
108109
post(EditClientPage.PATH)
109110
} Then {
110-
statusCode(HttpStatus.FOUND.value())
111-
header("Location", endsWith(ClientsListPage.path))
111+
statusCode(HttpStatus.OK.value())
112+
header("HX-Redirect", endsWith(ClientsListPage.path))
113+
header("HX-Trigger", "formSaved")
112114
}
113115
}
114116

app/src/testFixtures/kotlin/pro/qyoga/tests/pages/therapist/clients/card/ClientForm.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import pro.qyoga.core.clients.cards.model.toUIFormat
88
import pro.qyoga.l10n.russianDateFormat
99
import pro.qyoga.tests.assertions.PageMatcher
1010
import pro.qyoga.tests.assertions.SelectorOnlyComponent
11+
import pro.qyoga.tests.assertions.shouldBeElement
1112
import pro.qyoga.tests.assertions.shouldMatch
1213
import pro.qyoga.tests.platform.html.*
1314
import pro.qyoga.tests.platform.html.Input.Companion.email
@@ -59,11 +60,12 @@ abstract class ClientForm(action: FormAction) : QYogaForm("createClientForm", ac
5960

6061
}
6162

62-
object CreateClientForm : ClientForm(FormAction.classicPost("/therapist/clients/create"))
63+
object CreateClientForm : ClientForm(FormAction.hxPost("/therapist/clients/create"))
6364

64-
object EditClientForm : ClientForm(FormAction.classicPost("/therapist/clients/{id}")) {
65+
object EditClientForm : ClientForm(FormAction.hxPost("/therapist/clients/{id}")) {
6566

6667
fun clientForm(client: Client): PageMatcher = PageMatcher { element ->
68+
element.select(this.selector()).single() shouldBeElement action
6769
element.select(firstName.selector()).`val`() shouldBe client.firstName
6870
element.select(lastName.selector()).`val`() shouldBe client.lastName
6971
element.select(middleName.selector()).`val`() shouldBe (client.middleName ?: "")

0 commit comments

Comments
 (0)