diff --git a/website/src/components/infinteLoading.tsx b/website/src/components/infinteLoading.tsx index 1a21afb..5f13e79 100644 --- a/website/src/components/infinteLoading.tsx +++ b/website/src/components/infinteLoading.tsx @@ -7,6 +7,7 @@ import React, {useEffect, useRef, useState} from "react"; import {useIntersection} from "@mantine/hooks"; import {makeRequestWithToken} from "@/lib/api_tools"; import {PostCard} from "@/pages/media/components/cards"; +import {PostFeedCard} from "@/components/postfeed"; interface InfiniteLoadingProps { searchQuery: string @@ -71,6 +72,8 @@ export function InfiniteLoading({searchQuery, display} : InfiniteLoadingProps) { {display === "PlantCardApi" && } {display === "PostCard" && } + {display === "PostFeedCard" && } + ))} diff --git a/website/src/components/postfeed.tsx b/website/src/components/postfeed.tsx new file mode 100644 index 0000000..3540e77 --- /dev/null +++ b/website/src/components/postfeed.tsx @@ -0,0 +1,82 @@ +import styles from "@/styles/components/postfeed.module.css"; +import Link from "next/link"; +import {getFilePath} from "@/lib/data"; +import {useEffect, useState} from "react"; +import {makeCachedRequest} from "@/lib/api_tools"; +import {ADMIN_USER_TYPE, EDITOR_USER_TYPE, MEMBER_USER_TYPE} from "@/lib/users"; +import {getNamesInPreference, macronCodeToChar, numberDictionary} from "@/lib/plant_data"; +import {InfiniteLoading} from "@/components/infinteLoading"; +import {QueryClient, QueryClientProvider} from "@tanstack/react-query"; +import Image from "next/image"; + +interface PostCardProps { + post_title: string, + post_image: string, + post_user_id: number, + post_plant_id: number, + post_date: string, + id: number, +} + +export function PostFeedCard(props: PostCardProps) { + + const [plantName, setPlantName] = useState(""); + + useEffect(() => { + fetchData(); + }, []); + + const fetchData = async () => { + + // Get the plant information + const plants = await makeCachedRequest('plants_names_all', '/api/plants/search?getNames=true&getUnpublished=true'); + const plant = plants.find((plant: any) => plant.id === props.post_plant_id); + if(plant != null) { + let name = macronCodeToChar(getNamesInPreference(plant)[0], numberDictionary); + + // Check if the name is too long + if(name.length > 15) { + name = name.substring(0, 15) + ".."; + } + + setPlantName(name); + } + + } + + return ( +
{ + window.location.href = `/plants/${props.post_plant_id}`; + } + } className={styles.postCard}> + {props.post_title}/ +
+

{props.post_title}

+

{plantName}

+
+
+ ) +} + +export default function PostFeed() { + const queryClient = new QueryClient() + + return ( + <> +
+
+

Post Feed

+
+
+ + + +
+
+ + ) +} \ No newline at end of file diff --git a/website/src/pages/api/posts/fetch.ts b/website/src/pages/api/posts/fetch.ts index 5ec4bbc..0bd720a 100644 --- a/website/src/pages/api/posts/fetch.ts +++ b/website/src/pages/api/posts/fetch.ts @@ -18,7 +18,14 @@ export default async function handler( const tables = getTables(); // Get the id - const { id, operation, min, following, page } = request.query; + const { + id, + operation, + min, + following, + page , + plant_id + } = request.query; // Check if the user is permitted to access the API const session = await getServerSession(request, response, authOptions) @@ -38,10 +45,26 @@ export default async function handler( let query = ''; - const amountPerPage = 3 + let amountPerPage = 3 switch (operation) { + case "siteFeed": + + + amountPerPage = 8 + + // Get the latest posts + query = `SELECT * FROM posts ORDER BY ${tables.post_date} DESC`; + + // Check if a specific plant id was provided + if(plant_id) { + query = `SELECT * FROM posts WHERE ${tables.post_plant_id} = ${plant_id} ORDER BY ${tables.post_date} DESC`; + } + + break; + + case "list": // Check if there is no user id if(!id) { diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index 3146c28..c5499b6 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -19,6 +19,7 @@ import {useSession} from "next-auth/react"; import {UserDatabaseDetails} from "@/lib/users"; import {Layout} from "@/components/layout"; import axios from "axios"; +import PostFeed from "@/components/postfeed"; // TODO: // - Plant App @@ -333,6 +334,18 @@ export default function Home() { + {/* Section for the images feed */} +
+ + {/* Container for the images */} +
+ + {/* Display the images */} + +
+
+ + ); diff --git a/website/src/pages/media/components/cards.tsx b/website/src/pages/media/components/cards.tsx index 7e60479..10ef752 100644 --- a/website/src/pages/media/components/cards.tsx +++ b/website/src/pages/media/components/cards.tsx @@ -118,7 +118,7 @@ export function PostCard(props: PostCardProps) { // Get the plant information - const plants = await makeCachedRequest('plants_names_all', '/api/plants/search?getNames=true'); + const plants = await makeCachedRequest('plants_names_all', '/api/plants/search?getNames=true&getUnpublished=true'); const plant = plants.find((plant: any) => plant.id === props.post_plant_id); if(plant != null) { let name = macronCodeToChar(getNamesInPreference(plant)[0], numberDictionary); diff --git a/website/src/pages/media/new.tsx b/website/src/pages/media/new.tsx index de6398b..4a7e202 100644 --- a/website/src/pages/media/new.tsx +++ b/website/src/pages/media/new.tsx @@ -76,7 +76,7 @@ export default function Post(){ const fetchData = async () => { // Get the plants - const plants = await makeCachedRequest('plants_names_all', '/api/plants/search?getNames=true'); + const plants = await makeCachedRequest('plants_names_all', '/api/plants/search?getNames=true&getUnpublished=true'); // Get the plant names const plantNames = plants.map((plant : any) => { diff --git a/website/src/styles/components/postfeed.module.css b/website/src/styles/components/postfeed.module.css new file mode 100644 index 0000000..ca9294c --- /dev/null +++ b/website/src/styles/components/postfeed.module.css @@ -0,0 +1,75 @@ +.postFeed{ + width: 100%; +} + +.postFeedHeader{ + text-align: center; + margin-bottom: 20px; + font-weight: 500; + font-size: 64px; + color: var(--text-dark); + padding: 5%; +} + +/* Instagram like grid that displays the posts */ +.postGrid{ + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; + justify-content: center; + padding: 5%; + align-items: center; +} + +/*make the button span the entire width of the container */ +.postGrid button{ + grid-column: 1 / -1; + margin-top: 20px; + padding: 10px; + width: 80%; + margin-left: 10%; +} + +.postCard{ + background-color: var(--secondary-gray); + border-radius: 10px; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); + overflow: hidden; + position: relative; + cursor: pointer; + width: 300px; + height: 300px; +} + +.postCard img{ + width: 100%; + height: 100%; + object-fit: cover; +} + +/* post card text at the bottom of the card with a gradient background fading from black to transparent */ +.postCardText{ + position: absolute; + bottom: 0; + width: 100%; + padding: 10px; + background: linear-gradient(0deg, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); + color: white; + font-size: 16px; +} + +.postCard:hover p { + opacity: 1; + color: #94c47d; +} + +.postCardText p{ + opacity: 0.8; +} + +/* On hover, the post card text will slide up to reveal the post, make sure the gradient background is still visible */ +.postCard:hover .postCardText{ + background: linear-gradient(0deg, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.5)); + font-size: 18px; + transition: 0.01s; +}