Skip to content

Commit 33d4b96

Browse files
committed
feat: glossary and glossary term editing + other ui improvements
1 parent e9abfcb commit 33d4b96

27 files changed

+840
-346
lines changed

backend/data/src/main/kotlin/io/tolgee/constants/Message.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ enum class Message {
276276
SORTING_AND_PAGING_IS_NOT_SUPPORTED_WHEN_USING_CURSOR,
277277
GLOSSARY_NOT_FOUND,
278278
GLOSSARY_TERM_NOT_FOUND,
279+
GLOSSARY_TERM_TRANSLATION_NOT_FOUND,
279280
;
280281

281282
val code: String

backend/data/src/main/kotlin/io/tolgee/model/glossary/GlossaryTermTranslation.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import jakarta.persistence.UniqueConstraint
1818
)
1919
class GlossaryTermTranslation(
2020
var languageCode: String,
21-
@Column(columnDefinition = "text")
21+
@Column(columnDefinition = "text", nullable = false)
2222
@ActivityLoggedProp
2323
var text: String? = null,
2424
) : StandardAuditModel() {

ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/glossary/GlossaryTermController.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import io.tolgee.ee.api.v2.hateoas.assemblers.glossary.GlossaryTermTranslationMo
77
import io.tolgee.ee.api.v2.hateoas.assemblers.glossary.GlossaryTermWithTranslationsModelAssembler
88
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermModel
99
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermWithTranslationsModel
10-
import io.tolgee.ee.data.glossary.CreateGlossaryTermRequest
11-
import io.tolgee.ee.data.glossary.CreateGlossaryTermResponse
12-
import io.tolgee.ee.data.glossary.UpdateGlossaryTermRequest
10+
import io.tolgee.ee.data.glossary.CreateGlossaryTermWithTranslationRequest
11+
import io.tolgee.ee.data.glossary.CreateUpdateGlossaryTermResponse
12+
import io.tolgee.ee.data.glossary.UpdateGlossaryTermWithTranslationRequest
1313
import io.tolgee.ee.service.glossary.GlossaryTermService
1414
import io.tolgee.model.enums.OrganizationRoleType
1515
import io.tolgee.model.glossary.GlossaryTerm
@@ -44,10 +44,10 @@ class GlossaryTermController(
4444
@PathVariable
4545
glossaryId: Long,
4646
@RequestBody
47-
dto: CreateGlossaryTermRequest,
48-
): CreateGlossaryTermResponse {
49-
val (term, translation) = glossaryTermService.create(organizationId, glossaryId, dto)
50-
return CreateGlossaryTermResponse(
47+
dto: CreateGlossaryTermWithTranslationRequest,
48+
): CreateUpdateGlossaryTermResponse {
49+
val (term, translation) = glossaryTermService.createWithTranslation(organizationId, glossaryId, dto)
50+
return CreateUpdateGlossaryTermResponse(
5151
term = glossaryTermModelAssembler.toModel(term),
5252
translation = translation?.let { glossaryTermTranslationModelAssembler.toModel(translation) },
5353
)
@@ -61,10 +61,13 @@ class GlossaryTermController(
6161
@PathVariable organizationId: Long,
6262
@PathVariable glossaryId: Long,
6363
@PathVariable termId: Long,
64-
@RequestBody @Valid dto: UpdateGlossaryTermRequest,
65-
): GlossaryTermModel {
66-
val updatedTerm = glossaryTermService.update(organizationId, glossaryId, termId, dto)
67-
return glossaryTermModelAssembler.toModel(updatedTerm)
64+
@RequestBody @Valid dto: UpdateGlossaryTermWithTranslationRequest,
65+
): CreateUpdateGlossaryTermResponse {
66+
val (term, translation) = glossaryTermService.updateWithTranslation(organizationId, glossaryId, termId, dto)
67+
return CreateUpdateGlossaryTermResponse(
68+
term = glossaryTermModelAssembler.toModel(term),
69+
translation = translation?.let { glossaryTermTranslationModelAssembler.toModel(translation) },
70+
)
6871
}
6972

7073
@DeleteMapping("/terms/{termId:[0-9]+}")

ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/glossary/GlossaryTermTranslationController.kt

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import io.swagger.v3.oas.annotations.Operation
44
import io.swagger.v3.oas.annotations.tags.Tag
55
import io.tolgee.ee.api.v2.hateoas.assemblers.glossary.GlossaryTermTranslationModelAssembler
66
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermTranslationModel
7-
import io.tolgee.ee.data.glossary.CreateGlossaryTermTranslationRequest
7+
import io.tolgee.ee.data.glossary.UpdateGlossaryTermTranslationRequest
88
import io.tolgee.ee.service.glossary.GlossaryTermService
99
import io.tolgee.ee.service.glossary.GlossaryTermTranslationService
1010
import io.tolgee.model.enums.OrganizationRoleType
1111
import io.tolgee.security.authentication.AllowApiAccess
1212
import io.tolgee.security.authentication.AuthTokenType
1313
import io.tolgee.security.authorization.RequiresOrganizationRole
14+
import io.tolgee.security.authorization.UseDefaultPermissions
1415
import org.springframework.web.bind.annotation.*
1516

1617
@RestController
@@ -35,10 +36,33 @@ class GlossaryTermTranslationController(
3536
@PathVariable
3637
termId: Long,
3738
@RequestBody
38-
dto: CreateGlossaryTermTranslationRequest,
39-
): GlossaryTermTranslationModel? {
39+
dto: UpdateGlossaryTermTranslationRequest,
40+
): GlossaryTermTranslationModel {
4041
val glossaryTerm = glossaryTermService.get(organizationId, glossaryId, termId)
4142
val translation = glossaryTermTranslationService.updateOrCreate(glossaryTerm, dto)
42-
return translation?.let { modelAssembler.toModel(translation) }
43+
return translation?.let { modelAssembler.toModel(translation) } ?: GlossaryTermTranslationModel.defaultValue(
44+
dto.languageCode,
45+
)
46+
}
47+
48+
@GetMapping("/{languageCode}")
49+
@Operation(summary = "Get glossary term translation for language")
50+
@AllowApiAccess(AuthTokenType.ONLY_PAT)
51+
@UseDefaultPermissions
52+
fun get(
53+
@PathVariable
54+
organizationId: Long,
55+
@PathVariable
56+
glossaryId: Long,
57+
@PathVariable
58+
termId: Long,
59+
@PathVariable
60+
languageCode: String,
61+
): GlossaryTermTranslationModel {
62+
val glossaryTerm = glossaryTermService.get(organizationId, glossaryId, termId)
63+
val translation = glossaryTermTranslationService.find(glossaryTerm, languageCode)
64+
return translation?.let { modelAssembler.toModel(translation) } ?: GlossaryTermTranslationModel.defaultValue(
65+
languageCode,
66+
)
4367
}
4468
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/hateoas/assemblers/glossary/GlossaryTermTranslationModelAssembler.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ class GlossaryTermTranslationModelAssembler :
1414
) {
1515
override fun toModel(entity: GlossaryTermTranslation): GlossaryTermTranslationModel {
1616
return GlossaryTermTranslationModel(
17-
id = entity.id,
1817
languageCode = entity.languageCode,
19-
text = entity.text,
18+
text = entity.text ?: "",
2019
)
2120
}
2221
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/hateoas/model/glossary/GlossaryTermTranslationModel.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import org.springframework.hateoas.server.core.Relation
55

66
@Relation(collectionRelation = "glossaryTermTranslations", itemRelation = "glossaryTermTranslation")
77
class GlossaryTermTranslationModel(
8-
val id: Long,
98
val languageCode: String,
10-
val text: String?,
11-
) : RepresentationModel<GlossaryTermTranslationModel>()
9+
val text: String,
10+
) : RepresentationModel<GlossaryTermTranslationModel>() {
11+
companion object {
12+
fun defaultValue(languageCode: String) = GlossaryTermTranslationModel(languageCode, "")
13+
}
14+
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/CreateGlossaryTermRequest.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ package io.tolgee.ee.data.glossary
22

33
import jakarta.validation.constraints.Size
44

5-
class CreateGlossaryTermRequest {
6-
@field:Size(min = 0, max = 50)
7-
var text: String = ""
8-
5+
open class CreateGlossaryTermRequest {
96
@field:Size(min = 0, max = 150)
107
var description: String? = null
118

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.tolgee.ee.data.glossary
2+
3+
import jakarta.validation.constraints.Size
4+
5+
class CreateGlossaryTermWithTranslationRequest : CreateGlossaryTermRequest() {
6+
@field:Size(min = 0, max = 50)
7+
var text: String = ""
8+
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/CreateGlossaryTermResponse.kt renamed to ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/CreateUpdateGlossaryTermResponse.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.tolgee.ee.data.glossary
33
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermModel
44
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermTranslationModel
55

6-
data class CreateGlossaryTermResponse(
6+
data class CreateUpdateGlossaryTermResponse(
77
val term: GlossaryTermModel,
88
val translation: GlossaryTermTranslationModel?,
99
)

ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/UpdateGlossaryTermRequest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package io.tolgee.ee.data.glossary
22

33
import jakarta.validation.constraints.Size
44

5-
class UpdateGlossaryTermRequest {
5+
open class UpdateGlossaryTermRequest {
66
@field:Size(min = 0, max = 150)
77
var description: String? = null
88

ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/CreateGlossaryTermTranslationRequest.kt renamed to ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/UpdateGlossaryTermTranslationRequest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.tolgee.ee.data.glossary
33
import jakarta.validation.constraints.NotBlank
44
import jakarta.validation.constraints.Size
55

6-
class CreateGlossaryTermTranslationRequest {
6+
class UpdateGlossaryTermTranslationRequest {
77
@field:Size(min = 0, max = 50)
88
var text: String = ""
99

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.tolgee.ee.data.glossary
2+
3+
import jakarta.validation.constraints.Size
4+
5+
class UpdateGlossaryTermWithTranslationRequest : UpdateGlossaryTermRequest() {
6+
@field:Size(min = 0, max = 50)
7+
var text: String? = null
8+
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/service/glossary/GlossaryTermService.kt

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package io.tolgee.ee.service.glossary
22

33
import io.tolgee.constants.Message
4-
import io.tolgee.ee.data.glossary.CreateGlossaryTermRequest
5-
import io.tolgee.ee.data.glossary.CreateGlossaryTermTranslationRequest
6-
import io.tolgee.ee.data.glossary.UpdateGlossaryTermRequest
4+
import io.tolgee.ee.data.glossary.*
75
import io.tolgee.ee.repository.glossary.GlossaryTermRepository
86
import io.tolgee.exceptions.NotFoundException
97
import io.tolgee.model.glossary.GlossaryTerm
@@ -69,7 +67,7 @@ class GlossaryTermService(
6967
organizationId: Long,
7068
glossaryId: Long,
7169
request: CreateGlossaryTermRequest,
72-
): Pair<GlossaryTerm, GlossaryTermTranslation?> {
70+
): GlossaryTerm {
7371
val glossary = glossaryService.get(organizationId, glossaryId)
7472
val glossaryTerm =
7573
GlossaryTerm(
@@ -83,12 +81,22 @@ class GlossaryTermService(
8381
flagAbbreviation = request.flagAbbreviation
8482
flagForbiddenTerm = request.flagForbiddenTerm
8583
}
84+
return glossaryTermRepository.save(glossaryTerm)
85+
}
86+
87+
fun createWithTranslation(
88+
organizationId: Long,
89+
glossaryId: Long,
90+
request: CreateGlossaryTermWithTranslationRequest,
91+
): Pair<GlossaryTerm, GlossaryTermTranslation?> {
92+
val glossary = glossaryService.get(organizationId, glossaryId)
93+
val glossaryTerm = create(organizationId, glossaryId, request)
8694
val translation =
87-
CreateGlossaryTermTranslationRequest().apply {
95+
UpdateGlossaryTermTranslationRequest().apply {
8896
languageCode = glossary.baseLanguageCode!!
8997
text = request.text
9098
}
91-
return glossaryTermRepository.save(glossaryTerm) to
99+
return glossaryTerm to
92100
glossaryTermTranslationService.create(
93101
glossaryTerm,
94102
translation,
@@ -112,6 +120,35 @@ class GlossaryTermService(
112120
return glossaryTermRepository.save(glossaryTerm)
113121
}
114122

123+
fun updateWithTranslation(
124+
organizationId: Long,
125+
glossaryId: Long,
126+
termId: Long,
127+
request: UpdateGlossaryTermWithTranslationRequest,
128+
): Pair<GlossaryTerm, GlossaryTermTranslation?> {
129+
val glossary = glossaryService.get(organizationId, glossaryId)
130+
val glossaryTerm = update(organizationId, glossaryId, termId, request)
131+
val translationText = request.text
132+
if (translationText == null) {
133+
return glossaryTerm to
134+
glossaryTermTranslationService.find(
135+
glossaryTerm,
136+
glossary.baseLanguageCode!!,
137+
)
138+
}
139+
140+
val translation =
141+
UpdateGlossaryTermTranslationRequest().apply {
142+
languageCode = glossary.baseLanguageCode!!
143+
text = translationText
144+
}
145+
return glossaryTerm to
146+
glossaryTermTranslationService.updateOrCreate(
147+
glossaryTerm,
148+
translation,
149+
)
150+
}
151+
115152
fun delete(
116153
organizationId: Long,
117154
glossaryId: Long,

ee/backend/app/src/main/kotlin/io/tolgee/ee/service/glossary/GlossaryTermTranslationService.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.tolgee.ee.service.glossary
22

3-
import io.tolgee.ee.data.glossary.CreateGlossaryTermTranslationRequest
3+
import io.tolgee.constants.Message
4+
import io.tolgee.ee.data.glossary.UpdateGlossaryTermTranslationRequest
45
import io.tolgee.ee.repository.glossary.GlossaryTermTranslationRepository
6+
import io.tolgee.exceptions.NotFoundException
57
import io.tolgee.model.glossary.GlossaryTerm
68
import io.tolgee.model.glossary.GlossaryTermTranslation
79
import org.springframework.stereotype.Service
@@ -19,7 +21,7 @@ class GlossaryTermTranslationService(
1921

2022
fun create(
2123
term: GlossaryTerm,
22-
dto: CreateGlossaryTermTranslationRequest,
24+
dto: UpdateGlossaryTermTranslationRequest,
2325
): GlossaryTermTranslation? {
2426
if (dto.text.isEmpty()) {
2527
return null
@@ -37,7 +39,7 @@ class GlossaryTermTranslationService(
3739

3840
fun updateOrCreate(
3941
term: GlossaryTerm,
40-
dto: CreateGlossaryTermTranslationRequest,
42+
dto: UpdateGlossaryTermTranslationRequest,
4143
): GlossaryTermTranslation? {
4244
if (dto.text.isEmpty()) {
4345
glossaryTermTranslationRepository.deleteByTermAndLanguageCode(term, dto.languageCode)
@@ -52,4 +54,18 @@ class GlossaryTermTranslationService(
5254
translation.text = dto.text
5355
return glossaryTermTranslationRepository.save(translation)
5456
}
57+
58+
fun find(
59+
term: GlossaryTerm,
60+
languageCode: String,
61+
): GlossaryTermTranslation? {
62+
return glossaryTermTranslationRepository.findByTermAndLanguageCode(term, languageCode)
63+
}
64+
65+
fun get(
66+
term: GlossaryTerm,
67+
languageCode: String,
68+
): GlossaryTermTranslation {
69+
return find(term, languageCode) ?: throw NotFoundException(Message.GLOSSARY_TERM_TRANSLATION_NOT_FOUND)
70+
}
5571
}

webapp/src/ee/glossary/components/GlossaryListItem.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { CircledLanguageIconList } from 'tg.component/languages/CircledLanguageI
88
import { languageInfo } from '@tginternal/language-util/lib/generated/languageInfo';
99
import { GlossaryListItemMenu } from 'tg.ee.module/glossary/components/GlossaryListItemMenu';
1010

11+
type SimpleOrganizationModel = components['schemas']['SimpleOrganizationModel'];
12+
type GlossaryModel = components['schemas']['GlossaryModel'];
13+
1114
const StyledContainer = styled('div')`
1215
display: grid;
1316
grid-template-columns: 1fr 1fr 1fr 70px;
@@ -65,16 +68,14 @@ const StyledNameText = styled(Typography)`
6568
word-break: break-word;
6669
`;
6770

68-
type GlossaryModel = components['schemas']['GlossaryModel'];
69-
7071
type Props = {
7172
glossary: GlossaryModel;
72-
organizationSlug: string;
73+
organization: SimpleOrganizationModel;
7374
};
7475

7576
export const GlossaryListItem: React.VFC<Props> = ({
7677
glossary,
77-
organizationSlug,
78+
organization,
7879
}) => {
7980
const history = useHistory();
8081
const assignedProjects = glossary.assignedProjects._embedded?.projects;
@@ -100,7 +101,7 @@ export const GlossaryListItem: React.VFC<Props> = ({
100101
history.push(
101102
LINKS.ORGANIZATION_GLOSSARY.build({
102103
[PARAMS.GLOSSARY_ID]: glossary.id,
103-
[PARAMS.ORGANIZATION_SLUG]: organizationSlug,
104+
[PARAMS.ORGANIZATION_SLUG]: organization.slug,
104105
})
105106
)
106107
}
@@ -133,7 +134,7 @@ export const GlossaryListItem: React.VFC<Props> = ({
133134
<Box width="100%" display="flex" justifyContent="flex-end">
134135
<GlossaryListItemMenu
135136
glossary={glossary}
136-
organizationSlug={organizationSlug}
137+
organization={organization}
137138
/>
138139
</Box>
139140
</StyledControls>

0 commit comments

Comments
 (0)