diff --git a/src/main/kotlin/wanijo/wanijo2/domain/Document.kt b/src/main/kotlin/wanijo/wanijo2/domain/Document.kt index 06624e4..690b603 100644 --- a/src/main/kotlin/wanijo/wanijo2/domain/Document.kt +++ b/src/main/kotlin/wanijo/wanijo2/domain/Document.kt @@ -7,9 +7,12 @@ import java.time.ZonedDateTime @Table("T_DOCUMENT") data class Document( @Id - val id: Int = 0, - val createdAt: ZonedDateTime = ZonedDateTime.now(), - val updatedAt: ZonedDateTime = ZonedDateTime.now(), + val id: Long = 0, val name: String, - val description: String + val description: String = "", + val updatedAt: ZonedDateTime = ZonedDateTime.now(), + val createdAt: ZonedDateTime = ZonedDateTime.now(), + val labelFields: Set = emptySet(), + val dateFields: Set = emptySet(), + val tags: Set = emptySet() ) diff --git a/src/main/kotlin/wanijo/wanijo2/domain/DocumentDao.kt b/src/main/kotlin/wanijo/wanijo2/domain/DocumentDao.kt index 66d6957..55fdd58 100644 --- a/src/main/kotlin/wanijo/wanijo2/domain/DocumentDao.kt +++ b/src/main/kotlin/wanijo/wanijo2/domain/DocumentDao.kt @@ -1,15 +1,27 @@ package wanijo.wanijo2.domain +import org.springframework.data.jdbc.repository.query.Query import org.springframework.data.repository.Repository interface DocumentDao: Repository { fun findAll(): List - fun findById(id: Int): Document? + fun findById(id: Long): Document? fun findByName(name: String): List fun save(doc: Document) + @Query( + """ + SELECT doc.* + FROM t_document doc + JOIN t_tag tag + ON doc.id = tag.t_document + WHERE LOWER(tag.name) LIKE '%' || LOWER(:tagName) || '%' + """ + ) + fun findByTagName(tagName: String): List + } diff --git a/src/main/kotlin/wanijo/wanijo2/domain/Tag.kt b/src/main/kotlin/wanijo/wanijo2/domain/Tag.kt new file mode 100644 index 0000000..dab34c3 --- /dev/null +++ b/src/main/kotlin/wanijo/wanijo2/domain/Tag.kt @@ -0,0 +1,13 @@ +package wanijo.wanijo2.domain + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Table +import java.time.ZonedDateTime + +@Table("T_TAG") +data class Tag( + @Id + val id: Long = 0, + val name: String, + val createdAt: ZonedDateTime = ZonedDateTime.now() +) diff --git a/src/main/kotlin/wanijo/wanijo2/domain/fields.kt b/src/main/kotlin/wanijo/wanijo2/domain/fields.kt new file mode 100644 index 0000000..8c286fe --- /dev/null +++ b/src/main/kotlin/wanijo/wanijo2/domain/fields.kt @@ -0,0 +1,27 @@ +package wanijo.wanijo2.domain + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Table +import java.time.ZonedDateTime + +@Table("T_LABEL_FIELD") +data class LabelField( + @Id + val id: Long, + val order: Int = 0, + val name: String, + val value: String = "", + val updatedAt: ZonedDateTime = ZonedDateTime.now(), + val createdAt: ZonedDateTime = ZonedDateTime.now(), +) + +@Table("T_DATE_FIELD") +data class DateField( + @Id + val id: Long, + val order: Int = 0, + val name: String, + val value: ZonedDateTime, + val updatedAt: ZonedDateTime = ZonedDateTime.now(), + val createdAt: ZonedDateTime = ZonedDateTime.now(), +) diff --git a/src/main/kotlin/wanijo/wanijo2/http/controller/ShowController.kt b/src/main/kotlin/wanijo/wanijo2/http/controller/ShowController.kt index 5ff7660..6df5c92 100644 --- a/src/main/kotlin/wanijo/wanijo2/http/controller/ShowController.kt +++ b/src/main/kotlin/wanijo/wanijo2/http/controller/ShowController.kt @@ -18,7 +18,7 @@ class ShowController( @GetMapping("/document/{id}") fun show( model: Model, - @PathVariable id: Int + @PathVariable id: Long ): String { val document = docDao.findById(id) ?: throw DocumentNotFound() model["document"] = document diff --git a/src/main/resources/db/migration/V202507132038__init.sql b/src/main/resources/db/migration/V202507132038__init.sql index e21a4c4..496a9b6 100644 --- a/src/main/resources/db/migration/V202507132038__init.sql +++ b/src/main/resources/db/migration/V202507132038__init.sql @@ -1,8 +1,9 @@ CREATE TABLE t_document ( - id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(), - name VARCHAR NOT NULL, + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + name VARCHAR NOT NULL, description VARCHAR ); + diff --git a/src/main/resources/db/migration/V202507181738__label_date_fields.sql b/src/main/resources/db/migration/V202507181738__label_date_fields.sql new file mode 100644 index 0000000..2be3d20 --- /dev/null +++ b/src/main/resources/db/migration/V202507181738__label_date_fields.sql @@ -0,0 +1,29 @@ +CREATE TABLE t_label_field +( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR NOT NULL, + "ORDER" INT NOT NULL DEFAULT 0, + "VALUE" VARCHAR, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + t_document INT REFERENCES t_document (id) NOT NULL +); + +CREATE TABLE t_date_field +( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR NOT NULL, + "ORDER" INT NOT NULL DEFAULT 0, + "VALUE" TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + t_document INT REFERENCES t_document (id) NOT NULL +); + +CREATE TABLE t_tag +( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + t_document INT REFERENCES t_document (id) NOT NULL +); diff --git a/src/main/resources/static/stylesheet.css b/src/main/resources/static/stylesheet.css index e72466e..bcd1ac5 100644 --- a/src/main/resources/static/stylesheet.css +++ b/src/main/resources/static/stylesheet.css @@ -1,14 +1,18 @@ +body { + margin: 0 5rem; +} + main { .show__meta { display: flex; justify-content: space-evenly; - font-size: 70%; + font-size: 90%; } a:link, a:visited { text-decoration: none; - color: dodgerblue; + color: LinkText; } a:hover, a:active { @@ -16,18 +20,24 @@ main { } fieldset { - border-color: highlight; + border: 1px solid AccentColor; } } table { + width: 100%; + + td { + padding: .3rem; + } + thead { - background-color: lightgray; + background-color: ButtonFace; } tbody { tr:hover { - background-color: beige; + background-color: ButtonBorder; } } } diff --git a/src/test/kotlin/wanijo/wanijo2/domain/DocumentDaoTest.kt b/src/test/kotlin/wanijo/wanijo2/domain/DocumentDaoTest.kt index 370cdb1..7221202 100644 --- a/src/test/kotlin/wanijo/wanijo2/domain/DocumentDaoTest.kt +++ b/src/test/kotlin/wanijo/wanijo2/domain/DocumentDaoTest.kt @@ -19,10 +19,7 @@ class DocumentDaoTest { assertEquals(0, adapter.findAll().count()) adapter.save( - Document( - name = "name", - description = "desc" - ) + Document(name = "name") ) assertEquals(1, adapter.findAll().count()) @@ -30,10 +27,7 @@ class DocumentDaoTest { @Test fun findByIdWorks() { - val doc = Document( - name = "name", - description = "desc" - ) + val doc = Document(name = "name") adapter.save(doc) @@ -43,32 +37,61 @@ class DocumentDaoTest { @Test fun idsAreGeneratedSequentially() { + adapter.save( + Document(name = "name1") + ) + + adapter.save( + Document(name = "name2") + ) + + assertNotNull(adapter.findById(1)) + assertNotNull(adapter.findById(2)) + } + + @Test + fun findByNameWorks() { + adapter.save( + Document(name = "findmich") + ) + + assertNotNull(adapter.findByName("findmich")) + } + + @Test + fun findsDocumentsWithTag() { adapter.save( Document( - name = "name1", - description = "desc" + name = "Dok1", + tags = setOf(Tag(name = "tAg1")) ) ) adapter.save( Document( - name = "name2", - description = "desc" + name = "Dok2", + tags = setOf(Tag(name = "taG2")) ) ) - assertNotNull(adapter.findById(1)) - assertNotNull(adapter.findById(2)) + val docs = adapter.findByTagName("tag1") + assertEquals(1, docs.size) + assertEquals("Dok1", docs[0].name) } @Test - fun findByNameWorks() - { + fun tagsAreSaved() { adapter.save( - Document(name = "findmich", description = "") + Document( + name = "dok1", + tags = setOf(Tag(name = "tag1"), Tag(name = "tag2")) + ) ) - assertNotNull(adapter.findByName("findmich")) + val doc = adapter.findAll()[0] + assertEquals(2, doc.tags.size) + assertEquals("tag1", doc.tags.first().name) + assertEquals("tag2", doc.tags.last().name) } }