diff --git a/lesson_22/ananatawa's folder/hero.jpg b/lesson_22/ananatawa's folder/hero.jpg new file mode 100644 index 000000000..3a3f4cb14 Binary files /dev/null and b/lesson_22/ananatawa's folder/hero.jpg differ diff --git a/lesson_22/ananatawa's folder/logo.png b/lesson_22/ananatawa's folder/logo.png new file mode 100644 index 000000000..847ff80d3 Binary files /dev/null and b/lesson_22/ananatawa's folder/logo.png differ diff --git a/lesson_22/ananatawa's folder/style.css b/lesson_22/ananatawa's folder/style.css new file mode 100644 index 000000000..06aa4cd2b --- /dev/null +++ b/lesson_22/ananatawa's folder/style.css @@ -0,0 +1,228 @@ +/* Basic Reset */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; + overflow-x: hidden; /* Prevents horizontal scroll */ + width: 100vw; /* Ensures full-viewport fit */ +} + +body { + font-family: 'Poppins', 'Montserrat', sans-serif; + line-height: 1.6; + background-color: #ffffff; + color: #333; +} + + +.header { + position: fixed; + top: 0; + left: 0; + width: 100vw; + background-color: #ffffff; + display: flex; + justify-content: space-between; + align-items: center; + padding: 40px 60px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + z-index: 1000; + transition: padding 0.3s ease, box-shadow 0.3s ease; + box-sizing: border-box; +} + +.header.shrunk { + padding: 15px 60px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.header-logo img { + height: 75px; +} + +.header-top-menu { + list-style: none; + display: flex; + gap: 20px; +} + +.header-top-menu li a { + text-decoration: none; + color: #333; + font-weight: 600; + transition: color 0.3s ease; +} + +.header-top-menu li a { + position: relative; + text-decoration: none; + color: #333; + font-weight: 600; + padding: 5px 0; + transition: color 0.3s ease; +} + +.header-top-menu li a::after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 0%; + height: 2px; + background-color: orange; + transition: width 0.3s ease; +} + +/* Normal hover effect */ +.header-top-menu li a:hover::after { + width: 100%; +} + +/* New: Always underline the FIRST link (Home) */ +.header-top-menu li:first-child a::after { + width: 100%; +} + +.header-cta .sign-up-button { + background-color: #f37f0b; + color: #fff; + padding: 10px 20px; + text-decoration: none; + border-radius: 5px; + .btn-contact { + font-weight: 600; + font-family: 'Inter', sans-serif; + /* Optional matching styles: */ + text-transform: uppercase; + letter-spacing: 0.05em; +} + transition: background-color 0.3s ease; +} + +.header-cta .sign-up-button:hover { + background-color: #0056b3; +} + +.main { + padding-top: 120px; /* So the hero and rest of page starts AFTER header */ + padding-left: 0px; + padding-right: 0px; +} + + + +.hero-section { + position: relative; + background-image: url("hero.jpg"); + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + height: 100vh; + width: 100vw; + overflow: hidden; + filter: brightness(1.5); + padding-top: 125px; /* <-- NEW, creates white space under the header */ + box-sizing: border-box; +} + + +.hero-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: rgba(0, 0, 0, 0.5); +} + +.hero-content { + position: relative; + z-index: 2; + max-width: 800px; +} + +.hero-title { + font-family: 'Poppins', sans-serif; + font-weight: 700; + font-size: 48px; + line-height: 1.2; + color: #ffffff; + text-align: left; + max-width: 600px; /* keeps the line breaks like the real site */ + margin: 0 auto 0 0; + +} + +.highlight { + color: #ddff00; /* Gold Highlight */ +} + +.hero-text { + font-size: 1.2rem; +} + +.programs-section { + margin-top: 60px; + text-align: center; +} + +.programs-section h2 { + font-size: 2rem; + margin-bottom: 30px; +} + +.programs { + list-style: none; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + padding: 0; +} + +.program { + background-color: #f4f4f4; + padding: 20px; + border-radius: 8px; + transition: box-shadow 0.3s ease; +} + +.program:hover { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.program h3 { + margin-bottom: 10px; + font-size: 1.5rem; +} + +.program p { + font-size: 1rem; +} + +.footer { + background-color: #222; + color: #fff; + text-align: center; + padding: 20px; + margin-top: 40px; +} + +.highlight { + color: inherit; + background-image: linear-gradient(to bottom, transparent 65%, #f37f0b 65%); + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.hero-content { + position: relative; + z-index: 2; + max-width: 800px; + margin-left: 60px; + margin-top: 100px; /* vertically aligns the hero-title */ +} \ No newline at end of file diff --git a/lesson_26.zip b/lesson_26.zip new file mode 100644 index 000000000..fab30d972 Binary files /dev/null and b/lesson_26.zip differ diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/cli/LibraryApp.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/cli/LibraryApp.java index f96a08191..363f64560 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/cli/LibraryApp.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/cli/LibraryApp.java @@ -42,7 +42,7 @@ public void run(String[] args) throws Exception { private void printLibraryInfo(Library library) { LibraryInfo info = library.getInfo(); - Map> checkedOutItemsByGuest = info.getCheckedOutItemsByGuest(); + Map> checkedOutItemsByGuest = info.getCheckedOutItemsByGuestMap(); int numCheckedOutItems = checkedOutItemsByGuest.values().stream().mapToInt(Set::size).sum(); System.out.println(); System.out.println("========================================"); diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Librarian.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Librarian.java index edb8aea92..6a396cec3 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Librarian.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Librarian.java @@ -1,5 +1,8 @@ package com.codedifferently.lesson26.library; +import java.util.List; +import java.util.UUID; + /** Represents a librarian of a library. */ public class Librarian extends LibraryGuestBase { public Librarian(String name, String email) { @@ -10,4 +13,33 @@ public Librarian(String name, String email) { public String toString() { return "Librarian{" + "id='" + getEmail() + '\'' + ", name='" + getName() + '\'' + '}'; } + + public MediaItem getItem(String id) { + try { + return getLibrary().findById(id); + } catch (Exception e) { + return null; + } + } + + public void removeItem(String id) { + getLibrary().deleteById(id); + } + + public MediaItem createItem(String title, String type, String genre, String author) { + UUID id = UUID.randomUUID(); + MediaItem item; + + switch (type.toLowerCase()) { + case "book" -> item = new Book(id, title, "1234567890", List.of(author), 100); + case "dvd" -> item = new Dvd(id, title); + case "magazine" -> item = new Magazine(id, title); + case "newspaper" -> item = new Newspaper(id, title); + default -> throw new IllegalArgumentException("Unknown media type: " + type); + } + + item.setLibrary(getLibrary()); + getLibrary().addItem(item); + return item; + } } diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Library.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Library.java index 2071a60f3..1ec110dbf 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Library.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/Library.java @@ -25,6 +25,10 @@ public class Library { * * @param id The id of the library. */ + public void addItem(MediaItem item) { + this.itemsById.put(item.getId(), item); + } + public Library(String id) { this.id = id; this.searcher = new CatalogSearcher(this.itemsById.values()); @@ -280,7 +284,7 @@ public LibraryInfo getInfo() { .id(this.id) .items(Collections.unmodifiableSet(new HashSet<>(this.itemsById.values()))) .guests(Collections.unmodifiableSet(new HashSet<>(this.guestsById.values()))) - .checkedOutItemsByGuest(Collections.unmodifiableMap(itemsByGuest)) + .checkedOutItemsByGuestMap(Collections.unmodifiableMap(itemsByGuest)) .build(); } @@ -297,4 +301,13 @@ public String toString() { + guestsById + '}'; } + + public MediaItem findById(String id) { + return itemsById.get(id); + } + + public void deleteById(String id) { + UUID uuid = UUID.fromString(id); + itemsById.remove(uuid); + } } diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryGuestBase.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryGuestBase.java index 14448ca12..1d7724efd 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryGuestBase.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryGuestBase.java @@ -9,11 +9,15 @@ /** Base implementation of a library guest. */ public class LibraryGuestBase implements LibraryGuest { - private Library library; + protected Library library; private final UUID id = UUID.randomUUID(); private final String name; private final String email; + public Library getLibrary() { + return library; + } + public LibraryGuestBase(String name, String email) { this.name = name; this.email = email; diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryInfo.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryInfo.java index a8c4deb76..b517668fd 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryInfo.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/library/LibraryInfo.java @@ -16,5 +16,5 @@ public class LibraryInfo { public String id; public Set items; public Set guests; - public Map> checkedOutItemsByGuest; + public Map> checkedOutItemsByGuestMap; } diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemRequest.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemRequest.java index 26f7e1a4a..d9d7aae66 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemRequest.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemRequest.java @@ -14,4 +14,8 @@ public class CreateMediaItemRequest { @NotNull(message = "item is required") @Valid private MediaItemRequest item; + + public MediaItemRequest getItem() { + return item; + } } diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemResponse.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemResponse.java index 01da025e9..40f506d66 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemResponse.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/CreateMediaItemResponse.java @@ -1,10 +1,20 @@ package com.codedifferently.lesson26.web; +import com.codedifferently.lesson26.library.MediaItem; +import java.util.UUID; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @Builder +@NoArgsConstructor +@AllArgsConstructor public class CreateMediaItemResponse { - private MediaItemResponse item; + private UUID id; + + public static CreateMediaItemResponse from(MediaItem item) { + return CreateMediaItemResponse.builder().id(item.getId()).build(); + } } diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/ErrorResponse.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/ErrorResponse.java new file mode 100644 index 000000000..264af30ed --- /dev/null +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/ErrorResponse.java @@ -0,0 +1,11 @@ +package com.codedifferently.lesson26.web; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ErrorResponse { + private List errors; +} diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/GetMediaItemsResponse.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/GetMediaItemsResponse.java index 0d167cdba..821bfc269 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/GetMediaItemsResponse.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/GetMediaItemsResponse.java @@ -8,5 +8,5 @@ @Data @Builder public class GetMediaItemsResponse { - @Singular private List items; + @Singular List items; } diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemRequest.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemRequest.java index 74552dbeb..045162511 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemRequest.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemRequest.java @@ -26,6 +26,9 @@ public class MediaItemRequest { @NotBlank(message = "Title is required") private String title; + private String genre; + private String author; + private String[] authors; private String edition; private int pages; diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemResponse.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemResponse.java index 69a29dc98..71e4af732 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemResponse.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemResponse.java @@ -15,10 +15,10 @@ public class MediaItemResponse { private UUID id; private String isbn; private String title; - public List authors; - public String edition; - public int pages; - public int runtime; + private List authors; + private String edition; + private int pages; + private int runtime; public static MediaItemResponse from(MediaItem item) { var result = diff --git a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemsController.java b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemsController.java index bbbc45e41..1c0996b70 100644 --- a/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemsController.java +++ b/lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemsController.java @@ -8,9 +8,7 @@ import java.util.List; import java.util.Set; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @CrossOrigin @@ -31,4 +29,41 @@ public ResponseEntity getItems() { var response = GetMediaItemsResponse.builder().items(responseItems).build(); return ResponseEntity.ok(response); } + + @GetMapping("/items/{id}") + public ResponseEntity getItem(@PathVariable String id) { + MediaItem item = librarian.getItem(id); + if (item == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(MediaItemResponse.from(item)); + } + + @PostMapping("/items") + public ResponseEntity createItem(@RequestBody CreateMediaItemRequest request) { + if (request == null || request.getItem() == null) { + return ResponseEntity.badRequest() + .body(new ErrorResponse(List.of("Item details are required."))); + } + + var item = request.getItem(); + MediaItem newItem = + librarian.createItem( + item.getTitle(), + item.getType(), + item.getGenre() != null ? item.getGenre() : "genre", + item.getAuthor() != null ? item.getAuthor() : "author"); + + return ResponseEntity.ok(CreateMediaItemResponse.from(newItem)); + } + + @DeleteMapping("/items/{id}") + public ResponseEntity deleteItem(@PathVariable String id) { + MediaItem item = librarian.getItem(id); + if (item == null) { + return ResponseEntity.notFound().build(); + } + librarian.removeItem(id); + return ResponseEntity.noContent().build(); + } } diff --git a/lesson_26/api/java/api_app/src/main/resources/csv/media_items.csv b/lesson_26/api/java/api_app/src/main/resources/csv/media_items.csv index c3f8161d5..1d3487dbc 100644 --- a/lesson_26/api/java/api_app/src/main/resources/csv/media_items.csv +++ b/lesson_26/api/java/api_app/src/main/resources/csv/media_items.csv @@ -21,6 +21,7 @@ dvd,2b92ef3d-b224-4589-9a59-cdaba758affd,The Matrix,,,,136, dvd,e5d75a1d-f3b4-430f-ba63-b6e4603228eb,Pulp Fiction,,,,154, magazine,75fb71ad-ea84-45b8-8396-b790c833573e,Wired,,,,,"May 2024" magazine,e30a6739-cdc9-4b2e-ac67-7278fcfb9a59,Forbes,,,,,"April 2024" +newspaper,11112222-aaaa-bbbb-cccc-333344445555,The Observer,,,,,"March 24, 2024" newspaper,3e228c29-6163-477a-8b70-e873a3788758,Los Angeles Times,,,,,"Morning Edition, March 23, 2024" newspaper,8e3946e2-d5a6-4cb4-ac92-17cc44935d2d,Chicago Tribune,,,,,"March 23, 2024" book,b08c9da7-5c01-494c-84ec-af3fef9dc480,The Lord of the Rings,978-0544003415,"J.R.R. Tolkien",1178,,