diff --git a/.idea/BrandonGrahamDay.iml b/.idea/BrandonGrahamDay.iml index 5d40fe1..3ea5c30 100644 --- a/.idea/BrandonGrahamDay.iml +++ b/.idea/BrandonGrahamDay.iml @@ -71,5 +71,8 @@ + + + \ No newline at end of file diff --git a/CKC/pom.xml b/CKC/pom.xml index 4501ccf..7f1e721 100644 --- a/CKC/pom.xml +++ b/CKC/pom.xml @@ -36,6 +36,7 @@ spring-boot-starter-web + com.mysql @@ -47,13 +48,17 @@ com.fasterxml.jackson.core jackson-databind - 2.15.3 + 2.17.0 com.fasterxml.jackson.core jackson-core 2.15.3 + + com.fasterxml.jackson.core + jackson-annotations + @@ -123,4 +128,4 @@ - + \ No newline at end of file diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Articles/Articles.java b/CKC/src/main/java/rocks/zipcode/CKC/Articles/Articles.java index 960ead5..0d675db 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Articles/Articles.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Articles/Articles.java @@ -1,7 +1,10 @@ + package rocks.zipcode.CKC.Articles; import jakarta.persistence.*; +import java.util.Date; + @Entity @Table(name="articles") public class Articles { @@ -13,64 +16,86 @@ public class Articles { String title; @Column(name="Articles_Author") String author; - @Column(name="Articles_Thumbnail") + @Column(name = "Articles_Thumbnail", length = 1000) String thumbnail; - @Column(name="Articles_Description") + @Column(name="Articles_Description",length = 1000) String articleDescription; - @Column(name="Articles_Body") + @Column(name="Articles_Body",length = 100000) String articleBody; + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "Articles_PublishedAt") + private Date publishedAt; + @Embedded ArticlesSource source; - public Long getId() { - return id; - } +public Long getId() { + return id; +} - public void setId(Long id) { - this.id = id; - } - public String getTitle() { - return title; - } +public void setId(Long id) { + this.id = id; +} - public void setTitle(String title) { - this.title = title; +public ArticlesSource getSource() { + return source; } +public String getTitle() { + return title; +} - public String getAuthor() { - return author; - } +public void setTitle(String title) { + this.title = title; +} - public void setAuthor(String author) { - this.author = author; - } +public String getAuthor() { + return author; +} - public String getThumbnail() { - return thumbnail; - } +public void setAuthor(String author) { + this.author = author; +} - public void setThumbnail(String thumbnail) { - this.thumbnail = thumbnail; - } +public String getThumbnail() { + return thumbnail; +} - public String getArticleBody() { - return articleBody; - } +public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; +} - public void setArticleBody(String articleBody) { - this.articleBody = articleBody; - } +public String getArticleBody() { + return articleBody; +} + +public void setArticleBody(String articleBody) { + this.articleBody = articleBody; +} - public Articles(){} +public Articles(){} - public Articles(Long id, String title, String author, String thumbnail, String articleBody) { - this.id = id; - this.title = title; - this.author = author; - this.thumbnail = thumbnail; - this.articleBody = articleBody; +public Articles(Long id, String title, String author, String thumbnail, String articleBody, Date publishedAt) { + this.id = id; + this.title = title; + this.author = author; + this.thumbnail = thumbnail; + this.articleBody = articleBody; + this.publishedAt=publishedAt; +} + +public void setArticleDescription(String description) { + this.articleDescription=description; +} + +public void setSource(ArticlesSource source) { + this.source = source; + +} + public Date getPublishedAt() { + return publishedAt; } - public void setArticleDescription(String description) { + public void setPublishedAt(Date publishedAt) { + this.publishedAt = publishedAt; } -} +} \ No newline at end of file diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesController.java b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesController.java index 8ff902c..c597d59 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesController.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesController.java @@ -2,10 +2,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/articles") +@CrossOrigin(origins = "http://localhost:3000") public class ArticlesController { private final ArticlesRepository articlesRepository; @@ -15,12 +17,12 @@ public class ArticlesController { public ArticlesController(ArticlesRepository articlesRepository, ArticlesService articlesService){ this.articlesRepository = articlesRepository; this.articlesService = articlesService; } - @GetMapping - public Iterable getAllArticles(){ return articlesRepository.findAll(); } - - @CrossOrigin(origins = "http://localhost:3000") // Allows frontend access @GetMapping("/fetch") - public List fetchArticles() { + public List getNews() { return articlesService.fetchNews(); } -} + + @GetMapping + public Iterable getAllArticles(){ return articlesRepository.findAll(); } + +} \ No newline at end of file diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesDTO.java b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesDTO.java index d6cca6a..ade569f 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesDTO.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesDTO.java @@ -85,4 +85,4 @@ public String getContent() { public void setContent(String content) { this.content = content; } -} +} \ No newline at end of file diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesService.java b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesService.java index 4a8202d..b2378e6 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesService.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesService.java @@ -1,16 +1,13 @@ package rocks.zipcode.CKC.Articles; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import rocks.zipcode.CKC.Articles.ArticlesDTO; -import java.util.List; import java.util.ArrayList; -import java.util.Arrays; +import java.util.List; @Service public class ArticlesService { @@ -21,8 +18,9 @@ public ArticlesService(ArticlesRepository articlesRepository) { this.articlesRepository = articlesRepository; } - public List fetchNews() { - List dtoList = new ArrayList<>(); + public List fetchNews() { + List savedArticles = new ArrayList<>(); + try { String url = "https://newsapi.org/v2/top-headlines?country=us&apiKey=e31ee66a0d864e2e9a447942e8af0b2c"; RestTemplate restTemplate = new RestTemplate(); @@ -33,31 +31,36 @@ public List fetchNews() { JsonNode articlesNode = root.path("articles"); for (JsonNode node : articlesNode) { - ArticlesDTO dto = new ArticlesDTO(); - dto.setTitle(node.path("title").asText(null)); - dto.setAuthor(node.path("author").asText(null)); - dto.setDescription(node.path("description").asText(null)); - dto.setContent(node.path("content").asText(null)); - dto.setUrlToImage(node.path("urlToImage").asText(null)); - dto.setPublishedAt(node.path("publishedAt").asText(null)); + Articles article = new Articles(); + article.setTitle(node.path("title").asText(null)); + article.setAuthor(node.path("author").asText(null)); + article.setThumbnail(node.path("urlToImage").asText(null)); + article.setArticleDescription(node.path("description").asText(null)); + article.setArticleBody(node.path("content").asText(null)); + // Handle embedded source + ArticlesSource source = new ArticlesSource(); JsonNode sourceNode = node.path("source"); - if (!sourceNode.isMissingNode()) { - dto.setId(sourceNode.path("id").asText(null)); - dto.setName(sourceNode.path("name").asText(null)); - } + source.setId(sourceNode.path("id").asText(null)); + source.setName(sourceNode.path("name").asText(null)); + article.setSource(source); - dtoList.add(dto); + // Save to DB + Articles saved = articlesRepository.save(article); + savedArticles.add(saved); } + } catch (Exception e) { e.printStackTrace(); } - return dtoList; + + return savedArticles; } } + //@Service //public class ArticlesService { // diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesSource.java b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesSource.java index ec3824e..ca026c9 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesSource.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Articles/ArticlesSource.java @@ -5,11 +5,17 @@ @Embeddable public class ArticlesSource { + @Column(name = "source_id") private String id; + + @Column(name = "source_name") private String name; public ArticlesSource(){} - public ArticlesSource(String name, String id){} + public ArticlesSource(String name, String id){ + this.name = name; + this.id = id; + } public void setName(String name) { this.name = name; diff --git a/CKC/src/main/java/rocks/zipcode/CKC/CkcApplication.java b/CKC/src/main/java/rocks/zipcode/CKC/CkcApplication.java index 644c7d5..c8f799b 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/CkcApplication.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/CkcApplication.java @@ -3,7 +3,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.web.client.RestTemplate; @SpringBootApplication @@ -15,5 +18,9 @@ public static void main(String[] args) { SpringApplication.run(CkcApplication.class, args); } + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } } diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Comments/Comments.java b/CKC/src/main/java/rocks/zipcode/CKC/Comments/Comments.java index e50e55a..9043e66 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Comments/Comments.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Comments/Comments.java @@ -1,7 +1,10 @@ package rocks.zipcode.CKC.Comments; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.*; +import rocks.zipcode.CKC.Articles.Articles; import rocks.zipcode.CKC.User.Users; @@ -17,12 +20,6 @@ public class Comments { private Long id; - // Optional: enable this when Article entity is ready - // @ManyToOne - // @JoinColumn(name = "article_id") - // private Articles article; - - @Column(name = "text") private String text; @@ -31,18 +28,23 @@ public class Comments { @Column(name = "date_posted") private Date datePosted; - @ManyToOne - @JoinColumn(name = "user_id") // this name should match the column in your DB + @JoinColumn(name = "article_id") + private Articles articles; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "user_id") + @JsonIgnoreProperties({"comments"}) private Users user; public Comments() {} - public Comments(Long id, Users users, String text, Date datePosted) { + public Comments(Long id, Users user, Articles articles, String text, Date datePosted) { this.id = id; this.user = user; + this.articles = articles; this.text = text; this.datePosted = datePosted; } @@ -70,4 +72,6 @@ public Comments(Long id, Users users, String text, Date datePosted) { public void setDatePosted(Date datePosted) { this.datePosted = datePosted; } + public Articles getArticle() { return articles; } + public void setArticle(Articles articles) { this.articles = articles; } } diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsController.java b/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsController.java index 6c0f77a..a20c52a 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsController.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsController.java @@ -3,87 +3,97 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import rocks.zipcode.CKC.User.Users; +import rocks.zipcode.CKC.Articles.Articles; +import rocks.zipcode.CKC.Articles.ArticlesRepository; +import rocks.zipcode.CKC.Comments.Comments; +import rocks.zipcode.CKC.Comments.CommentsDTO; +import rocks.zipcode.CKC.Comments.CommentsRepository; import rocks.zipcode.CKC.User.UserRepository; +import rocks.zipcode.CKC.User.Users; import java.util.Date; - +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; @RestController @RequestMapping("/comments") +@CrossOrigin(origins = "http://localhost:3000") public class CommentsController { - private final CommentsRepository commentsRepository; private final UserRepository userRepository; + private final ArticlesRepository articlesRepository; @Autowired - public CommentsController(CommentsRepository commentsRepository, UserRepository userRepository) { + public CommentsController(CommentsRepository commentsRepository, UserRepository userRepository, ArticlesRepository articlesRepository) { this.commentsRepository = commentsRepository; this.userRepository = userRepository; + this.articlesRepository = articlesRepository; } + @PostMapping("/add/comment") + public Comments addComment(@RequestBody CommentsDTO dto) { + Users user = userRepository.findByUserName(dto.getUserName()) + .orElseThrow(() -> new RuntimeException("User not found")); - @PostMapping("/submit") - public String submitComment(@RequestParam Long userId, /*@RequestParam Long articleId,*/ @RequestParam String text) { - // the use of optional allows for null value to exist - Users users = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("You must have an account to comment.")); - // think about redirecting to sign in page after it's made - - - // Articles article = articleRepository.findById(articleId).orElse("Article no longer exists"); - - - Comments newComment = new Comments(); - newComment.setUser(users); - // newComment.setArticle(article); - newComment.setText(text); - newComment.setDatePosted(new Date()); + Articles article = articlesRepository.findById(dto.getArticleId()) + .orElseThrow(() -> new RuntimeException("Article not found")); + Comments comment = new Comments(); + comment.setUser(user); + comment.setArticle(article); + comment.setText(dto.getText()); + comment.setDatePosted(new Date()); - commentsRepository.save(newComment); - return "Comment Submitted"; - // return "redirect:/articles/" + articleId; + return commentsRepository.save(comment); } - + @GetMapping("/fetch/all") + public List getAllComments() { + Iterable iterable = commentsRepository.findAll(); + return StreamSupport.stream(iterable.spliterator(), false) + .collect(Collectors.toList()); + } @GetMapping("/user/{userId}") public ResponseEntity> getCommentsByUser(@PathVariable Long userId) { - Users users = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("User not found.")); - return ResponseEntity.ok(users.getComments()); + Users user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("User not found")); + return ResponseEntity.ok(user.getComments()); + } + @GetMapping("/user/{userId}/with-articles") + public ResponseEntity> getCommentsByUserWithArticles(@PathVariable Long userId) { + Users user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("User not found")); + + // Filter the user's comments that have an article associated + List commentsWithArticles = user.getComments().stream() + .filter(comment -> comment.getArticle() != null) + .toList(); + + return ResponseEntity.ok(commentsWithArticles); } - // DELETE comment by ID @DeleteMapping("/{id}") public ResponseEntity deleteComment(@PathVariable Long id) { if (commentsRepository.existsById(id)) { commentsRepository.deleteById(id); return ResponseEntity.ok("Comment deleted successfully."); - } else { - return ResponseEntity.notFound().build(); } + return ResponseEntity.notFound().build(); } - - // PUT (update) comment by ID @PutMapping("/{id}") public ResponseEntity updateComment(@PathVariable Long id, @RequestBody Comments updatedComment) { return commentsRepository.findById(id) - .map(existingComment -> { - existingComment.setText(updatedComment.getText()); - existingComment.setDatePosted(new Date()); // update timestamp - commentsRepository.save(existingComment); - return ResponseEntity.ok(existingComment); + .map(existing -> { + existing.setText(updatedComment.getText()); + existing.setDatePosted(new Date()); + commentsRepository.save(existing); + return ResponseEntity.ok(existing); }) .orElse(ResponseEntity.notFound().build()); } - -// @GetMapping("/articles/{id}") -// public String showArticle(@PathVariable Long id, Model model) { -// // Load article, comments, etc. -// return "article-detail"; // name of Thymeleaf view -// } } + diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsDTO.java b/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsDTO.java new file mode 100644 index 0000000..db3f672 --- /dev/null +++ b/CKC/src/main/java/rocks/zipcode/CKC/Comments/CommentsDTO.java @@ -0,0 +1,27 @@ +package rocks.zipcode.CKC.Comments; + +public class CommentsDTO { + + private String userName; + private Long articleId; + private String text; + + // Getters and setters + public String getUser() { return userName; } + public void setUserId(String userId) { this.userName = userId; } + + public Long getArticleId() { return articleId; } + public void setArticleId(Long articleId) { this.articleId = articleId; } + + public String getText() { return text; } + public void setText(String text) { this.text = text; } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} + diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Stock/StockController.java b/CKC/src/main/java/rocks/zipcode/CKC/Stock/StockController.java new file mode 100644 index 0000000..0ceca8a --- /dev/null +++ b/CKC/src/main/java/rocks/zipcode/CKC/Stock/StockController.java @@ -0,0 +1,29 @@ +package rocks.zipcode.CKC.Stock; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + import java.util.*; + +@RestController +@RequestMapping("/api/stocks") +public class StockController { + + @Autowired + private StockService stockService; + + @GetMapping("/{symbol}/live") + public ResponseEntity getLiveChartData(@PathVariable String symbol) { + List prices = stockService.getLivePrices(symbol); + + if (prices.isEmpty()) { + return ResponseEntity.status(404).body("No live data for symbol: " + symbol); + } + + Map result = new HashMap<>(); + result.put("symbol", symbol); + result.put("prices", prices); + return ResponseEntity.ok(result); + } +} diff --git a/CKC/src/main/java/rocks/zipcode/CKC/Stock/StockService.java b/CKC/src/main/java/rocks/zipcode/CKC/Stock/StockService.java new file mode 100644 index 0000000..f47f440 --- /dev/null +++ b/CKC/src/main/java/rocks/zipcode/CKC/Stock/StockService.java @@ -0,0 +1,48 @@ +package rocks.zipcode.CKC.Stock; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class StockService { + + private final RestTemplate restTemplate; + private final String apiKey = "6AQN5983ASH3JJ0A"; // Your actual API key + + public StockService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public List getLivePrices(String symbol) { + String url = "https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY" + + "&symbol=" + symbol + + "&interval=1min" + + "&outputsize=compact" + + "&apikey=" + apiKey; + + ResponseEntity response = restTemplate.getForEntity(url, Map.class); + Map body = response.getBody(); + + String key = "Time Series (1min)"; + if (body == null || !body.containsKey(key)) { + System.out.println("No 1-minute time series found for: " + symbol); + return Collections.emptyList(); + } + + Map timeSeries = (Map) body.get(key); + + return timeSeries.entrySet().stream() + .sorted(Collections.reverseOrder(Map.Entry.comparingByKey())) // newest first + .limit(10) + .map(entry -> { + Map minuteData = (Map) entry.getValue(); + String close = minuteData.get("4. close"); + return Double.parseDouble(close); + }) + .collect(Collectors.toList()); + } +} diff --git a/CKC/src/main/java/rocks/zipcode/CKC/User/UserController.java b/CKC/src/main/java/rocks/zipcode/CKC/User/UserController.java index 16dae9f..9be2762 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/User/UserController.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/User/UserController.java @@ -4,6 +4,12 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import rocks.zipcode.CKC.Comments.Comments; +import rocks.zipcode.CKC.Comments.CommentsRepository; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; @RestController @@ -12,15 +18,16 @@ public class UserController { private final UserRepository userRepository; + private final CommentsRepository commentsRepository; @Autowired - public UserController(UserRepository userRepository) { + public UserController(UserRepository userRepository,CommentsRepository commentsRepository) { + this.commentsRepository=commentsRepository; this.userRepository = userRepository; } - -// @GetMapping + // @GetMapping // // this is what was returning the json to the webpage // public Iterable getAllUsers() { // return userRepository.findAll(); @@ -42,6 +49,13 @@ public ResponseEntity createUser(@RequestBody Users users) { return new ResponseEntity<>(savedUsers, HttpStatus.CREATED); } + @GetMapping("/fetch/all") + public List getAllComments() { + Iterable iterable = commentsRepository.findAll(); + return StreamSupport.stream(iterable.spliterator(), false) + .collect(Collectors.toList()); + } + @PostMapping("/save") // saves user information from form-style parameters diff --git a/CKC/src/main/java/rocks/zipcode/CKC/User/UserRepository.java b/CKC/src/main/java/rocks/zipcode/CKC/User/UserRepository.java index 8f306bd..b7797f7 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/User/UserRepository.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/User/UserRepository.java @@ -7,4 +7,5 @@ public interface UserRepository extends CrudRepository { Optional findById(Long id); + Optional findByUserName(String userName); } diff --git a/CKC/src/main/java/rocks/zipcode/CKC/User/Users.java b/CKC/src/main/java/rocks/zipcode/CKC/User/Users.java index a876e4b..9ec81a5 100644 --- a/CKC/src/main/java/rocks/zipcode/CKC/User/Users.java +++ b/CKC/src/main/java/rocks/zipcode/CKC/User/Users.java @@ -1,6 +1,7 @@ package rocks.zipcode.CKC.User; +import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import rocks.zipcode.CKC.Comments.Comments; @@ -28,13 +29,10 @@ public class Users { @OneToMany(mappedBy = "user") - @JsonIgnore private List comments; public Users() { - - } diff --git a/CKC/src/main/java/rocks/zipcode/CKC/WebConfig.java b/CKC/src/main/java/rocks/zipcode/CKC/WebConfig.java new file mode 100644 index 0000000..9b99e2b --- /dev/null +++ b/CKC/src/main/java/rocks/zipcode/CKC/WebConfig.java @@ -0,0 +1,17 @@ +package rocks.zipcode.CKC; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry){ + registry.addMapping("/**") + .allowedOrigins("http://localhost:3000") + .allowedMethods("GET", "POST", "PUT", "DELETE") + .allowedHeaders("*"); + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 18eb0d0..ce8fa36 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,7 +14,10 @@ "@testing-library/user-event": "^13.5.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-router-dom": "^7.6.0", "react-scripts": "5.0.1", + "react-slick": "^0.30.3", + "slick-carousel": "^1.8.1", "web-vitals": "^2.1.4" } }, @@ -5599,6 +5602,12 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "license": "MIT" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -6891,6 +6900,12 @@ "node": ">=10.13.0" } }, + "node_modules/enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==", + "license": "MIT" + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -10836,6 +10851,13 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT", + "peer": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10943,6 +10965,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -13916,6 +13947,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", + "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz", + "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -13989,6 +14067,23 @@ } } }, + "node_modules/react-slick": { + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.3.tgz", + "integrity": "sha512-B4x0L9GhkEWUMApeHxr/Ezp2NncpGc+5174R02j+zFiWuYboaq98vmxwlpafZfMjZic1bjdIqqmwLDcQY0QaFA==", + "license": "MIT", + "dependencies": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -14214,6 +14309,12 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -14822,6 +14923,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -15000,6 +15107,15 @@ "node": ">=8" } }, + "node_modules/slick-carousel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz", + "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==", + "license": "MIT", + "peerDependencies": { + "jquery": ">=1.8.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -15270,6 +15386,12 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index bfb0044..cb75a6c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,10 @@ "@testing-library/user-event": "^13.5.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-router-dom": "^7.6.0", "react-scripts": "5.0.1", + "react-slick": "^0.30.3", + "slick-carousel": "^1.8.1", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/frontend/public/3.png b/frontend/public/3.png new file mode 100644 index 0000000..ae2ca38 Binary files /dev/null and b/frontend/public/3.png differ diff --git a/frontend/public/ZipCode10years.jpeg b/frontend/public/ZipCode10years.jpeg new file mode 100644 index 0000000..7a7ea56 Binary files /dev/null and b/frontend/public/ZipCode10years.jpeg differ diff --git a/frontend/public/a.png b/frontend/public/a.png new file mode 100644 index 0000000..30f0719 Binary files /dev/null and b/frontend/public/a.png differ diff --git a/frontend/public/aapl.png b/frontend/public/aapl.png new file mode 100644 index 0000000..c173ae8 Binary files /dev/null and b/frontend/public/aapl.png differ diff --git a/frontend/public/addf.png b/frontend/public/addf.png new file mode 100644 index 0000000..db09f33 Binary files /dev/null and b/frontend/public/addf.png differ diff --git a/frontend/public/amzn.png b/frontend/public/amzn.png new file mode 100644 index 0000000..fb52cd4 Binary files /dev/null and b/frontend/public/amzn.png differ diff --git a/frontend/public/coke.png b/frontend/public/coke.png new file mode 100644 index 0000000..01fa3b9 Binary files /dev/null and b/frontend/public/coke.png differ diff --git a/frontend/public/dpnt.png b/frontend/public/dpnt.png new file mode 100644 index 0000000..b395ded Binary files /dev/null and b/frontend/public/dpnt.png differ diff --git a/frontend/public/goog.png b/frontend/public/goog.png new file mode 100644 index 0000000..61be7ac Binary files /dev/null and b/frontend/public/goog.png differ diff --git a/frontend/public/jnj.png b/frontend/public/jnj.png new file mode 100644 index 0000000..5e0eaad Binary files /dev/null and b/frontend/public/jnj.png differ diff --git a/frontend/public/manu.png b/frontend/public/manu.png new file mode 100644 index 0000000..39d0061 Binary files /dev/null and b/frontend/public/manu.png differ diff --git a/frontend/public/meta.png b/frontend/public/meta.png new file mode 100644 index 0000000..de3bf3f Binary files /dev/null and b/frontend/public/meta.png differ diff --git a/frontend/public/nike.png b/frontend/public/nike.png new file mode 100644 index 0000000..9a42388 Binary files /dev/null and b/frontend/public/nike.png differ diff --git a/frontend/src/Components/App.css b/frontend/src/Components/App.css index 74b5e05..784e839 100644 --- a/frontend/src/Components/App.css +++ b/frontend/src/Components/App.css @@ -1,7 +1,16 @@ +/* === Layout: Sticky Footer Setup === */ .App { + display: flex; + flex-direction: column; + min-height: 100vh; text-align: center; } +.content { + flex: 1; +} + +/* === App Logo Animation === */ .App-logo { height: 40vmin; pointer-events: none; @@ -13,6 +22,7 @@ } } +/* === Header Styling === */ .App-header { background-color: #282c34; min-height: 100vh; @@ -28,6 +38,7 @@ color: #61dafb; } +/* === Logo Spin Animation === */ @keyframes App-logo-spin { from { transform: rotate(0deg); @@ -36,3 +47,41 @@ transform: rotate(360deg); } } + +/* 🌟 Presentation Mode: Large Fonts for Distance Viewing 🌟 */ + +body { + font-size: 26px; + line-height: 1.8; + font-family: Arial, sans-serif; +} + +/* Headings */ +h1 { + font-size: 3rem; + font-weight: bold; +} + +h2 { + font-size: 2.5rem; +} + +h3 { + font-size: 2rem; +} + +/* Text elements */ +p, +span, +li { + font-size: 1.75rem; +} + +/* Inputs, Textareas, and Buttons */ +input, +textarea, +button { + font-size: 1.6rem; + padding: 16px 24px; +} + diff --git a/frontend/src/Components/App.js b/frontend/src/Components/App.js index e05f747..ac4b1c3 100644 --- a/frontend/src/Components/App.js +++ b/frontend/src/Components/App.js @@ -1,28 +1,28 @@ -import React, { useState } from 'react'; -import './App.css'; -import NewsGrid from './NewsGrid'; +import React from 'react'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import Header from './Header'; +import Footer from './Footer'; +import Home from './Home'; +import NewsArticle from './NewsArticle'; +import FavoritePage from './FavoritePage'; +import QrPage from './QrPage'; function App() { - const [selectedArticle, setSelectedArticle] = useState(null); - return ( -
-

CrossKey Communication

- {selectedArticle ? ( -
- -