diff --git a/src/main/java/codes/yam/contacts/ContactController.java b/src/main/java/codes/yam/contacts/ContactController.java index 5415d7b..2bc4550 100644 --- a/src/main/java/codes/yam/contacts/ContactController.java +++ b/src/main/java/codes/yam/contacts/ContactController.java @@ -4,6 +4,8 @@ import java.net.URI; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -19,12 +21,13 @@ public class ContactController { private final jakarta.validation.Validator validator; @GetMapping - public String contacts(@RequestParam(required = false) String q, Model model) { - var contacts = - (q != null && !q.isBlank()) ? contactService.search(q) : contactService.findAll(); - model.addAttribute("contacts", contacts); + public String contacts( + @RequestParam(required = false) String q, + @PageableDefault(sort = "last") Pageable pageable, + Model model) { + model.addAttribute("contactPage", contactService.list(q, pageable)); model.addAttribute("search", q); - return "contacts/list"; + return "contacts/index"; } @GetMapping("/{slug}") @@ -86,10 +89,10 @@ public ResponseEntity deleteManyContacts( public String validateEmail(@PathVariable String slug, @RequestParam String email) { var violations = validator.validateValue(Contact.class, "email", email); if (!violations.isEmpty()) { - return violations.iterator().next().getMessage(); + return "" + violations.iterator().next().getMessage() + ""; } if (contactService.isEmailTaken(email, slug)) { - return "Email already taken"; + return "Email already taken"; } return ""; } diff --git a/src/main/java/codes/yam/contacts/ContactRepository.java b/src/main/java/codes/yam/contacts/ContactRepository.java index a97cc73..601f1c6 100644 --- a/src/main/java/codes/yam/contacts/ContactRepository.java +++ b/src/main/java/codes/yam/contacts/ContactRepository.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -20,5 +22,5 @@ public interface ContactRepository extends JpaRepository { + "LOWER(c.first) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(c.last) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(c.email) LIKE LOWER(CONCAT('%', :q, '%'))") - List search(@Param("q") String q); + Page search(@Param("q") String q, Pageable pageable); } diff --git a/src/main/java/codes/yam/contacts/ContactService.java b/src/main/java/codes/yam/contacts/ContactService.java index 6809674..7b2dd18 100644 --- a/src/main/java/codes/yam/contacts/ContactService.java +++ b/src/main/java/codes/yam/contacts/ContactService.java @@ -2,6 +2,8 @@ import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service @@ -9,12 +11,11 @@ public class ContactService { private final ContactRepository contactRepository; - public List findAll() { - return contactRepository.findAll(); - } - - public List search(String q) { - return contactRepository.search(q); + public Page list(String q, Pageable pageable) { + if (q != null && !q.isBlank()) { + return contactRepository.search(q, pageable); + } + return contactRepository.findAll(pageable); } public Contact findBySlug(String slug) { @@ -46,9 +47,9 @@ public Contact update(String slug, Contact updated) { return contactRepository.save(contact); } - // TODO: This is fragile - throws NullPointException if first or last is null. private String generateSlug(Contact contact) { - return contact.getFirst().toLowerCase() + "-" + contact.getLast().toLowerCase(); + return (contact.getFirst().toLowerCase() + "-" + contact.getLast().toLowerCase()) + .replaceAll("[^a-z0-9-]", ""); } /** diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 9ba7332..cfb369a 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,21 +1,31 @@ -INSERT INTO contact (id, slug, first, last, email, phone) -VALUES ('550e8400-e29b-41d4-a716-446655440000', 'john-doe', 'John', 'Doe', 'john@example.com', '555-1234'), - ('550e8400-e29b-41d4-a716-446655440001', 'jane-doe', 'Jane', 'Doe', 'jane@example.com', '555-5678'), - ('550e8400-e29b-41d4-a716-446655440002', 'alice-smith', 'Alice', 'Smith', 'alice@example.com', '555-2001'), - ('550e8400-e29b-41d4-a716-446655440003', 'bob-johnson', 'Bob', 'Johnson', 'bob@example.com', '555-2002'), - ('550e8400-e29b-41d4-a716-446655440004', 'carol-williams', 'Carol', 'Williams', 'carol@example.com', '555-2003'), - ('550e8400-e29b-41d4-a716-446655440005', 'dave-brown', 'Dave', 'Brown', 'dave@example.com', '555-2004'), - ('550e8400-e29b-41d4-a716-446655440006', 'eve-jones', 'Eve', 'Jones', 'eve@example.com', '555-2005'), - ('550e8400-e29b-41d4-a716-446655440007', 'frank-garcia', 'Frank', 'Garcia', 'frank@example.com', '555-2006'), - ('550e8400-e29b-41d4-a716-446655440008', 'grace-martinez', 'Grace', 'Martinez', 'grace@example.com', '555-2007'), - ('550e8400-e29b-41d4-a716-446655440009', 'henry-davis', 'Henry', 'Davis', 'henry@example.com', '555-2008'), - ('550e8400-e29b-41d4-a716-44665544000a', 'iris-miller', 'Iris', 'Miller', 'iris@example.com', '555-2009'), - ('550e8400-e29b-41d4-a716-44665544000b', 'jack-wilson', 'Jack', 'Wilson', 'jack@example.com', '555-2010'), - ('550e8400-e29b-41d4-a716-44665544000c', 'karen-moore', 'Karen', 'Moore', 'karen@example.com', '555-2011'), - ('550e8400-e29b-41d4-a716-44665544000d', 'leo-taylor', 'Leo', 'Taylor', 'leo@example.com', '555-2012'), - ('550e8400-e29b-41d4-a716-44665544000e', 'mia-anderson', 'Mia', 'Anderson', 'mia@example.com', '555-2013'), - ('550e8400-e29b-41d4-a716-44665544000f', 'nick-thomas', 'Nick', 'Thomas', 'nick@example.com', '555-2014'), - ('550e8400-e29b-41d4-a716-446655440010', 'olivia-jackson', 'Olivia', 'Jackson', 'olivia@example.com', '555-2015'), - ('550e8400-e29b-41d4-a716-446655440011', 'paul-white', 'Paul', 'White', 'paul@example.com', '555-2016'), - ('550e8400-e29b-41d4-a716-446655440012', 'quinn-harris', 'Quinn', 'Harris', 'quinn@example.com', '555-2017'), - ('550e8400-e29b-41d4-a716-446655440013', 'rachel-clark', 'Rachel', 'Clark', 'rachel@example.com', '555-2018'); +INSERT INTO contact (id, slug, first, last, email, phone) VALUES +('550e8400-e29b-41d4-a716-446655440000', 'john-doe', 'John', 'Doe', 'john@example.com', '555-1234'), +('550e8400-e29b-41d4-a716-446655440001', 'jane-doe', 'Jane', 'Doe', 'jane@example.com', '555-5678'), +('550e8400-e29b-41d4-a716-446655440002', 'alice-smith', 'Alice', 'Smith', 'alice.smith@example.com', '555-1001'), +('550e8400-e29b-41d4-a716-446655440003', 'bob-johnson', 'Bob', 'Johnson', 'bob.johnson@example.com', '555-1002'), +('550e8400-e29b-41d4-a716-446655440004', 'carol-williams', 'Carol', 'Williams', 'carol.williams@example.com', '555-1003'), +('550e8400-e29b-41d4-a716-446655440005', 'david-brown', 'David', 'Brown', 'david.brown@example.com', '555-1004'), +('550e8400-e29b-41d4-a716-446655440006', 'eve-jones', 'Eve', 'Jones', 'eve.jones@example.com', '555-1005'), +('550e8400-e29b-41d4-a716-446655440007', 'frank-garcia', 'Frank', 'Garcia', 'frank.garcia@example.com', '555-1006'), +('550e8400-e29b-41d4-a716-446655440008', 'grace-miller', 'Grace', 'Miller', 'grace.miller@example.com', '555-1007'), +('550e8400-e29b-41d4-a716-446655440009', 'henry-davis', 'Henry', 'Davis', 'henry.davis@example.com', '555-1008'), +('550e8400-e29b-41d4-a716-44665544000a', 'iris-martinez', 'Iris', 'Martinez', 'iris.martinez@example.com', '555-1009'), +('550e8400-e29b-41d4-a716-44665544000b', 'jack-wilson', 'Jack', 'Wilson', 'jack.wilson@example.com', '555-1010'), +('550e8400-e29b-41d4-a716-44665544000c', 'karen-anderson', 'Karen', 'Anderson', 'karen.anderson@example.com', '555-1011'), +('550e8400-e29b-41d4-a716-44665544000d', 'leo-thomas', 'Leo', 'Thomas', 'leo.thomas@example.com', '555-1012'), +('550e8400-e29b-41d4-a716-44665544000e', 'mia-taylor', 'Mia', 'Taylor', 'mia.taylor@example.com', '555-1013'), +('550e8400-e29b-41d4-a716-44665544000f', 'noah-hernandez', 'Noah', 'Hernandez', 'noah.hernandez@example.com', '555-1014'), +('550e8400-e29b-41d4-a716-446655440010', 'olivia-moore', 'Olivia', 'Moore', 'olivia.moore@example.com', '555-1015'), +('550e8400-e29b-41d4-a716-446655440011', 'paul-jackson', 'Paul', 'Jackson', 'paul.jackson@example.com', '555-1016'), +('550e8400-e29b-41d4-a716-446655440012', 'quinn-martin', 'Quinn', 'Martin', 'quinn.martin@example.com', '555-1017'), +('550e8400-e29b-41d4-a716-446655440013', 'rachel-lee', 'Rachel', 'Lee', 'rachel.lee@example.com', '555-1018'), +('550e8400-e29b-41d4-a716-446655440014', 'sam-perez', 'Sam', 'Perez', 'sam.perez@example.com', '555-1019'), +('550e8400-e29b-41d4-a716-446655440015', 'tina-white', 'Tina', 'White', 'tina.white@example.com', '555-1020'), +('550e8400-e29b-41d4-a716-446655440016', 'ursula-harris', 'Ursula', 'Harris', 'ursula.harris@example.com', '555-1021'), +('550e8400-e29b-41d4-a716-446655440017', 'victor-clark', 'Victor', 'Clark', 'victor.clark@example.com', '555-1022'), +('550e8400-e29b-41d4-a716-446655440018', 'wendy-lewis', 'Wendy', 'Lewis', 'wendy.lewis@example.com', '555-1023'), +('550e8400-e29b-41d4-a716-446655440019', 'xavier-robinson', 'Xavier', 'Robinson', 'xavier.robinson@example.com', '555-1024'), +('550e8400-e29b-41d4-a716-44665544001a', 'yara-walker', 'Yara', 'Walker', 'yara.walker@example.com', '555-1025'), +('550e8400-e29b-41d4-a716-44665544001b', 'zane-hall', 'Zane', 'Hall', 'zane.hall@example.com', '555-1026'), +('550e8400-e29b-41d4-a716-44665544001c', 'amber-allen', 'Amber', 'Allen', 'amber.allen@example.com', '555-1027'), +('550e8400-e29b-41d4-a716-44665544001d', 'brian-young', 'Brian', 'Young', 'brian.young@example.com', '555-1028'); diff --git a/src/main/resources/templates/contacts/index.html b/src/main/resources/templates/contacts/index.html new file mode 100644 index 0000000..70cc616 --- /dev/null +++ b/src/main/resources/templates/contacts/index.html @@ -0,0 +1,55 @@ + + + + + +
+
+ + + +
+ +

Results for "":

+ +

No contacts found.

+
+ + + + + + + + + + + + + +
FirstLastPhoneEmail
+ +
+
+ Previous + + Next +
+

+ Add New Contact +

+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/contacts/list.html b/src/main/resources/templates/contacts/list.html deleted file mode 100644 index 9678d03..0000000 --- a/src/main/resources/templates/contacts/list.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - -
-
- - - -
- -

Results for "":

- -

No contacts found.

-
- - - - - - - - - - - - -
FirstLastPhoneEmail
- -
-

- Add New Contact - -

-
- - \ No newline at end of file diff --git a/src/main/resources/templates/fragments/contact-fields.html b/src/main/resources/templates/fragments/contact-fields.html index 5c5ae62..1e3226b 100644 --- a/src/main/resources/templates/fragments/contact-fields.html +++ b/src/main/resources/templates/fragments/contact-fields.html @@ -8,7 +8,8 @@
diff --git a/src/main/resources/templates/fragments/contact-list-rows.html b/src/main/resources/templates/fragments/contact-list-rows.html index bd7b8e9..6c92c5d 100644 --- a/src/main/resources/templates/fragments/contact-list-rows.html +++ b/src/main/resources/templates/fragments/contact-list-rows.html @@ -2,14 +2,15 @@ - + +