mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-18 21:44:08 +01:00
ui: add placeholder page for hub and inventory
This commit is contained in:
parent
cbcf2b870b
commit
fed4d14bb9
295
frontend/app/(sidebar)/hub/[id]/page.tsx
Normal file
295
frontend/app/(sidebar)/hub/[id]/page.tsx
Normal file
@ -0,0 +1,295 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { ArrowLeft, Calendar, Clock, Share2, Bookmark, ChevronUp } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
// Sample blog data - in a real app, you would fetch this based on the ID
|
||||
const blog = {
|
||||
id: 1,
|
||||
title: "Sustainable Farming Practices for Modern Agriculture",
|
||||
description: "Learn about eco-friendly farming techniques that can increase yield while preserving the environment.",
|
||||
date: "2023-05-15",
|
||||
author: "Emma Johnson",
|
||||
authorRole: "Agricultural Specialist",
|
||||
authorImage: "/placeholder.svg?height=100&width=100",
|
||||
topic: "Sustainability",
|
||||
image: "/placeholder.svg?height=600&width=1200",
|
||||
readTime: "5 min read",
|
||||
content: `
|
||||
<p>Sustainable farming is not just a trend; it's a necessary evolution in agricultural practices to ensure food security for future generations while minimizing environmental impact. This article explores practical, eco-friendly farming techniques that can increase yield while preserving our precious natural resources.</p>
|
||||
|
||||
<h2>The Importance of Sustainable Agriculture</h2>
|
||||
|
||||
<p>As the global population continues to grow, the demand for food increases, putting pressure on farmers to produce more. However, conventional farming methods often lead to soil degradation, water pollution, and biodiversity loss. Sustainable farming addresses these challenges by working with natural processes rather than against them.</p>
|
||||
|
||||
<p>Key benefits of sustainable farming include:</p>
|
||||
|
||||
<ul>
|
||||
<li>Reduced environmental impact</li>
|
||||
<li>Improved soil health and fertility</li>
|
||||
<li>Conservation of water resources</li>
|
||||
<li>Enhanced biodiversity</li>
|
||||
<li>Long-term economic viability</li>
|
||||
</ul>
|
||||
|
||||
<h2>Crop Rotation and Diversification</h2>
|
||||
|
||||
<p>One of the simplest yet most effective sustainable farming practices is crop rotation. By alternating different crops in the same area across growing seasons, farmers can break pest cycles, improve soil structure, and enhance nutrient availability.</p>
|
||||
|
||||
<p>Diversification goes hand in hand with rotation. Growing a variety of crops rather than practicing monoculture helps spread risk, improves ecological balance, and can provide multiple income streams for farmers.</p>
|
||||
|
||||
<h2>Integrated Pest Management (IPM)</h2>
|
||||
|
||||
<p>IPM is an ecosystem-based approach that focuses on long-term prevention of pests through a combination of techniques such as biological control, habitat manipulation, and resistant crop varieties. Chemical pesticides are used only when monitoring indicates they are needed according to established guidelines.</p>
|
||||
|
||||
<p>This approach reduces pesticide use, minimizes environmental impact, and helps prevent the development of pesticide-resistant pests.</p>
|
||||
|
||||
<h2>Water Conservation Techniques</h2>
|
||||
|
||||
<p>Water is a precious resource, and sustainable farming emphasizes its efficient use. Drip irrigation systems deliver water directly to plant roots, reducing evaporation and runoff. Rainwater harvesting systems capture and store rainfall for later use during dry periods.</p>
|
||||
|
||||
<p>Additionally, selecting drought-resistant crop varieties and implementing proper soil management practices can significantly reduce water requirements.</p>
|
||||
|
||||
<h2>Soil Health Management</h2>
|
||||
|
||||
<p>Healthy soil is the foundation of sustainable agriculture. Practices such as minimal tillage, cover cropping, and the application of organic matter help maintain soil structure, prevent erosion, and enhance fertility.</p>
|
||||
|
||||
<p>Composting farm waste and applying it back to the fields creates a closed-loop system that reduces the need for synthetic fertilizers while improving soil quality.</p>
|
||||
|
||||
<h2>Renewable Energy Integration</h2>
|
||||
|
||||
<p>Modern sustainable farms are increasingly incorporating renewable energy sources such as solar panels, wind turbines, and biogas digesters. These technologies reduce dependence on fossil fuels, lower operational costs, and decrease the carbon footprint of agricultural operations.</p>
|
||||
|
||||
<h2>Conclusion</h2>
|
||||
|
||||
<p>Transitioning to sustainable farming practices requires knowledge, planning, and sometimes initial investment. However, the long-term benefits for farmers, communities, and the environment make it a worthwhile endeavor.</p>
|
||||
|
||||
<p>By adopting these eco-friendly techniques, farmers can ensure the viability of their operations while contributing to a healthier planet for future generations.</p>
|
||||
`,
|
||||
tableOfContents: [
|
||||
{ id: "importance", title: "The Importance of Sustainable Agriculture", level: 1 },
|
||||
{ id: "crop-rotation", title: "Crop Rotation and Diversification", level: 1 },
|
||||
{ id: "ipm", title: "Integrated Pest Management (IPM)", level: 1 },
|
||||
{ id: "water-conservation", title: "Water Conservation Techniques", level: 1 },
|
||||
{ id: "soil-health", title: "Soil Health Management", level: 1 },
|
||||
{ id: "renewable-energy", title: "Renewable Energy Integration", level: 1 },
|
||||
{ id: "conclusion", title: "Conclusion", level: 1 },
|
||||
],
|
||||
relatedArticles: [
|
||||
{
|
||||
id: 2,
|
||||
title: "Optimizing Fertilizer Usage for Maximum Crop Yield",
|
||||
topic: "Fertilizers",
|
||||
image: "/placeholder.svg?height=200&width=300",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Water Conservation Techniques for Drought-Prone Areas",
|
||||
topic: "Sustainability",
|
||||
image: "/placeholder.svg?height=200&width=300",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "Organic Pest Control Methods That Actually Work",
|
||||
topic: "Organic",
|
||||
image: "/placeholder.svg?height=200&width=300",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default function BlogPage() {
|
||||
const [showScrollTop, setShowScrollTop] = useState(false);
|
||||
|
||||
// Handle scroll to show/hide scroll to top button
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setShowScrollTop(window.scrollY > 300);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
||||
// Scroll to top function
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
// Scroll to section function
|
||||
const scrollToSection = (id: string) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Header */}
|
||||
<header className="border-b sticky top-0 z-10 bg-background/95 backdrop-blur">
|
||||
<div className="container flex items-center justify-between h-16 px-4">
|
||||
<Link href="/knowledge-hub">
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
<ArrowLeft className="h-4 w-4" /> Back to Knowledge Hub
|
||||
</Button>
|
||||
</Link>
|
||||
<div className="flex items-center gap-2">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Share2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Share article</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Bookmark className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Save article</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main content */}
|
||||
<main className="container px-4 py-8 md:py-12">
|
||||
<div className="grid lg:grid-cols-[1fr_300px] gap-10 max-w-6xl mx-auto">
|
||||
{/* Article content */}
|
||||
<div>
|
||||
{/* Topic badge */}
|
||||
<div className="mb-4">
|
||||
<Badge className="rounded-full">{blog.topic}</Badge>
|
||||
</div>
|
||||
|
||||
{/* Article title */}
|
||||
<h1 className="text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight mb-4">{blog.title}</h1>
|
||||
|
||||
{/* Article meta */}
|
||||
<div className="flex flex-wrap items-center gap-3 text-sm text-muted-foreground mb-6">
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{new Date(blog.date).toLocaleDateString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>{blog.readTime}</span>
|
||||
</div>
|
||||
<span>By {blog.author}</span>
|
||||
</div>
|
||||
|
||||
{/* Featured image */}
|
||||
<div className="relative h-[300px] md:h-[400px] mb-8 rounded-lg overflow-hidden">
|
||||
<Image src={blog.image || "/placeholder.svg"} alt={blog.title} fill className="object-cover" />
|
||||
</div>
|
||||
|
||||
{/* Article description */}
|
||||
<p className="text-lg md:text-xl text-muted-foreground mb-8">{blog.description}</p>
|
||||
|
||||
{/* Article content */}
|
||||
<div className="prose prose-green max-w-none" dangerouslySetInnerHTML={{ __html: blog.content }} />
|
||||
|
||||
{/* Author bio */}
|
||||
<div className="mt-12 p-6 bg-muted rounded-lg">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative w-16 h-16 rounded-full overflow-hidden">
|
||||
<Image src={blog.authorImage || "/placeholder.svg"} alt={blog.author} fill className="object-cover" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">{blog.author}</h3>
|
||||
<p className="text-sm text-muted-foreground">{blog.authorRole}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="space-y-8">
|
||||
{/* Table of contents */}
|
||||
<div className="sticky top-24">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Table of Contents</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<nav className="space-y-2">
|
||||
{blog.tableOfContents.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => scrollToSection(item.id)}
|
||||
className={`text-left w-full px-2 py-1 text-sm rounded-md hover:bg-muted transition-colors
|
||||
${item.level > 1 ? "ml-4" : ""}`}>
|
||||
{item.title}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Related articles */}
|
||||
<Card className="mt-8">
|
||||
<CardHeader>
|
||||
<CardTitle>Related Articles</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{blog.relatedArticles.map((article) => (
|
||||
<Link href={`/blog/${article.id}`} key={article.id}>
|
||||
<div className="flex gap-3 group">
|
||||
<div className="relative w-16 h-16 rounded overflow-hidden flex-shrink-0">
|
||||
<Image
|
||||
src={article.image || "/placeholder.svg"}
|
||||
alt={article.title}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-medium group-hover:text-primary transition-colors">
|
||||
{article.title}
|
||||
</h4>
|
||||
<Badge variant="outline" className="mt-1 text-xs">
|
||||
{article.topic}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Scroll to top button */}
|
||||
{showScrollTop && (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className="fixed bottom-6 right-6 p-2 rounded-full bg-primary text-primary-foreground shadow-lg transition-opacity hover:opacity-90"
|
||||
aria-label="Scroll to top">
|
||||
<ChevronUp className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
259
frontend/app/(sidebar)/hub/page.tsx
Normal file
259
frontend/app/(sidebar)/hub/page.tsx
Normal file
@ -0,0 +1,259 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { CalendarIcon, ChevronRight, Leaf, Search } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
// Sample blog data
|
||||
const blogs = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Sustainable Farming Practices for Modern Agriculture",
|
||||
description:
|
||||
"Learn about eco-friendly farming techniques that can increase yield while preserving the environment.",
|
||||
date: "2023-05-15",
|
||||
author: "Emma Johnson",
|
||||
topic: "Sustainability",
|
||||
image: "/placeholder.svg?height=400&width=600",
|
||||
readTime: "5 min read",
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Optimizing Fertilizer Usage for Maximum Crop Yield",
|
||||
description: "Discover the perfect balance of fertilizers to maximize your harvest without wasting resources.",
|
||||
date: "2023-06-02",
|
||||
author: "Michael Chen",
|
||||
topic: "Fertilizers",
|
||||
image: "/placeholder.svg?height=400&width=600",
|
||||
readTime: "7 min read",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Seasonal Planting Guide: What to Grow and When",
|
||||
description:
|
||||
"A comprehensive guide to help you plan your planting schedule throughout the year for optimal results.",
|
||||
date: "2023-06-18",
|
||||
author: "Sarah Williams",
|
||||
topic: "Plantation",
|
||||
image: "/placeholder.svg?height=400&width=600",
|
||||
readTime: "8 min read",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Water Conservation Techniques for Drought-Prone Areas",
|
||||
description: "Essential strategies to maintain your crops during water shortages and drought conditions.",
|
||||
date: "2023-07-05",
|
||||
author: "David Rodriguez",
|
||||
topic: "Sustainability",
|
||||
image: "/placeholder.svg?height=400&width=600",
|
||||
readTime: "6 min read",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "Organic Pest Control Methods That Actually Work",
|
||||
description: "Natural and effective ways to keep pests at bay without resorting to harmful chemicals.",
|
||||
date: "2023-07-22",
|
||||
author: "Lisa Thompson",
|
||||
topic: "Organic",
|
||||
image: "/placeholder.svg?height=400&width=600",
|
||||
readTime: "9 min read",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "The Future of Smart Farming: IoT and Agriculture",
|
||||
description: "How Internet of Things technology is revolutionizing the way we monitor and manage farms.",
|
||||
date: "2023-08-10",
|
||||
author: "James Wilson",
|
||||
topic: "Technology",
|
||||
image: "/placeholder.svg?height=400&width=600",
|
||||
readTime: "10 min read",
|
||||
featured: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Extract unique topics from blogs
|
||||
const topics = ["All", ...new Set(blogs.map((blog) => blog.topic))];
|
||||
|
||||
export default function KnowledgeHubPage() {
|
||||
const [selectedTopic, setSelectedTopic] = useState("All");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
// Filter blogs based on selected topic and search query
|
||||
const filteredBlogs = blogs.filter((blog) => {
|
||||
const matchesTopic = selectedTopic === "All" || blog.topic === selectedTopic;
|
||||
const matchesSearch =
|
||||
blog.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
blog.description.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
return matchesTopic && matchesSearch;
|
||||
});
|
||||
|
||||
// Get featured blogs
|
||||
const featuredBlogs = blogs.filter((blog) => blog.featured);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background">
|
||||
{/* Main content */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
<main className="flex-1 p-6 md:p-10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-bold tracking-tight">Knowledge Hub</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
Explore our collection of articles, guides, and resources to help you grow better.
|
||||
</p>
|
||||
</div>
|
||||
<div className="relative w-full md:w-64">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search articles..."
|
||||
className="pl-8"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Featured article */}
|
||||
{featuredBlogs.length > 0 && (
|
||||
<div className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-6">Featured Articles</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
{featuredBlogs.slice(0, 2).map((blog) => (
|
||||
<Card key={blog.id} className="overflow-hidden group">
|
||||
<div className="relative h-64 overflow-hidden">
|
||||
<Image
|
||||
src={blog.image || "/placeholder.svg"}
|
||||
alt={blog.title}
|
||||
fill
|
||||
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 p-6 text-white">
|
||||
<Badge className="bg-primary hover:bg-primary/90 mb-2">{blog.topic}</Badge>
|
||||
<h3 className="text-xl font-bold">{blog.title}</h3>
|
||||
<div className="flex items-center mt-2 text-sm">
|
||||
<CalendarIcon className="h-4 w-4 mr-1" />
|
||||
<span>{new Date(blog.date).toLocaleDateString()}</span>
|
||||
<span className="mx-2">•</span>
|
||||
<span>{blog.readTime}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CardFooter className="p-4 flex justify-between items-center">
|
||||
<div className="text-sm text-muted-foreground">By {blog.author}</div>
|
||||
<Link href={`/blog/${blog.id}`}>
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
Read more <ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Topic filters */}
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl font-semibold mb-4">Browse by Topic</h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{topics.map((topic) => (
|
||||
<Button
|
||||
key={topic}
|
||||
variant={selectedTopic === topic ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => setSelectedTopic(topic)}
|
||||
className="rounded-full">
|
||||
{topic === "Sustainability" && <Leaf className="mr-1 h-4 w-4" />}
|
||||
{topic}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator className="my-8" />
|
||||
|
||||
{/* Blog grid */}
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredBlogs.length === 0 ? (
|
||||
<div className="col-span-full text-center py-12">
|
||||
<h3 className="text-xl font-medium mb-2">No articles found</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Try adjusting your search or filter to find what you're looking for.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredBlogs.map((blog) => (
|
||||
<Card key={blog.id} className="overflow-hidden group h-full flex flex-col">
|
||||
<div className="relative h-48 overflow-hidden">
|
||||
<Image
|
||||
src={blog.image || "/placeholder.svg"}
|
||||
alt={blog.title}
|
||||
fill
|
||||
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
<CardHeader className="p-4 pb-2">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Badge variant="outline">{blog.topic}</Badge>
|
||||
<div className="text-xs text-muted-foreground">{new Date(blog.date).toLocaleDateString()}</div>
|
||||
</div>
|
||||
<CardTitle className="text-lg">{blog.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 pt-0 flex-grow">
|
||||
<CardDescription className="line-clamp-2">{blog.description}</CardDescription>
|
||||
</CardContent>
|
||||
<CardFooter className="p-4 pt-0 flex justify-between items-center">
|
||||
<div className="text-sm text-muted-foreground">By {blog.author}</div>
|
||||
<Link href={`/blog/${blog.id}`}>
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
Read <ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Pagination - simplified for this example */}
|
||||
{filteredBlogs.length > 0 && (
|
||||
<div className="flex justify-center mt-12">
|
||||
<Button variant="outline" size="sm" className="mx-1">
|
||||
1
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="mx-1">
|
||||
2
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="mx-1">
|
||||
3
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="mx-1">
|
||||
...
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="mx-1">
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
115
frontend/app/(sidebar)/inventory/add-inventory-item.tsx
Normal file
115
frontend/app/(sidebar)/inventory/add-inventory-item.tsx
Normal file
@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { format } from "date-fns";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function AddInventoryItem() {
|
||||
const [date, setDate] = useState<Date>();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>Add New Item</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add Inventory Item</DialogTitle>
|
||||
<DialogDescription>Add a new plantation or fertilizer item to your inventory.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="name" className="text-right">
|
||||
Name
|
||||
</Label>
|
||||
<Input id="name" className="col-span-3" />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="type" className="text-right">
|
||||
Type
|
||||
</Label>
|
||||
<Select>
|
||||
<SelectTrigger className="col-span-3">
|
||||
<SelectValue placeholder="Select type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Type</SelectLabel>
|
||||
<SelectItem value="plantation">Plantation</SelectItem>
|
||||
<SelectItem value="fertilizer">Fertilizer</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="category" className="text-right">
|
||||
Category
|
||||
</Label>
|
||||
<Input id="category" className="col-span-3" placeholder="e.g., Seeds, Organic" />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="quantity" className="text-right">
|
||||
Quantity
|
||||
</Label>
|
||||
<Input id="quantity" type="number" className="col-span-3" />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="unit" className="text-right">
|
||||
Unit
|
||||
</Label>
|
||||
<Input id="unit" className="col-span-3" placeholder="e.g., kg, packets" />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="date" className="text-right">
|
||||
Date
|
||||
</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn("col-span-3 justify-start text-left font-normal", !date && "text-muted-foreground")}>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, "PPP") : "Pick a date"}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar mode="single" selected={date} onSelect={setDate} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="submit" onClick={() => setOpen(false)}>
|
||||
Save Item
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
183
frontend/app/(sidebar)/inventory/page.tsx
Normal file
183
frontend/app/(sidebar)/inventory/page.tsx
Normal file
@ -0,0 +1,183 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Calendar, ChevronDown, Plus, Search } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Pagination, PaginationContent, PaginationItem, PaginationLink } from "@/components/ui/pagination";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { Calendar as CalendarComponent } from "@/components/ui/calendar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
export default function InventoryPage() {
|
||||
const [date, setDate] = useState<Date>();
|
||||
const [inventoryType, setInventoryType] = useState("all");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
// Sample inventory data
|
||||
const inventoryItems = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tomato Seeds",
|
||||
category: "Seeds",
|
||||
type: "Plantation",
|
||||
quantity: 500,
|
||||
unit: "packets",
|
||||
lastUpdated: "2023-03-01",
|
||||
status: "In Stock",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "NPK Fertilizer",
|
||||
category: "Fertilizer",
|
||||
type: "Fertilizer",
|
||||
quantity: 200,
|
||||
unit: "kg",
|
||||
lastUpdated: "2023-03-05",
|
||||
status: "Low Stock",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Corn Seeds",
|
||||
category: "Seeds",
|
||||
type: "Plantation",
|
||||
quantity: 300,
|
||||
unit: "packets",
|
||||
lastUpdated: "2023-03-10",
|
||||
status: "In Stock",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Organic Compost",
|
||||
category: "Fertilizer",
|
||||
type: "Fertilizer",
|
||||
quantity: 150,
|
||||
unit: "kg",
|
||||
lastUpdated: "2023-03-15",
|
||||
status: "In Stock",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Wheat Seeds",
|
||||
category: "Seeds",
|
||||
type: "Plantation",
|
||||
quantity: 250,
|
||||
unit: "packets",
|
||||
lastUpdated: "2023-03-20",
|
||||
status: "In Stock",
|
||||
},
|
||||
];
|
||||
|
||||
// Filter items based on selected type
|
||||
const filteredItems =
|
||||
inventoryType === "all"
|
||||
? inventoryItems
|
||||
: inventoryItems.filter((item) =>
|
||||
inventoryType === "plantation" ? item.type === "Plantation" : item.type === "Fertilizer"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background">
|
||||
{/* Main content */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
<main className="flex-1 p-6">
|
||||
<h1 className="text-2xl font-bold tracking-tight mb-6">Inventory</h1>
|
||||
|
||||
{/* Filters and search */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant={inventoryType === "all" ? "default" : "outline"}
|
||||
onClick={() => setInventoryType("all")}
|
||||
className="w-24">
|
||||
All
|
||||
</Button>
|
||||
<Select value={inventoryType} onValueChange={setInventoryType}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue placeholder="Crop" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="all">All</SelectItem>
|
||||
<SelectItem value="plantation">Plantation</SelectItem>
|
||||
<SelectItem value="fertilizer">Fertilizer</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 gap-4">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="flex-1 justify-between">
|
||||
<div className="flex items-center">
|
||||
<Calendar className="mr-2 h-4 w-4" />
|
||||
{date ? date.toLocaleDateString() : "Time filter"}
|
||||
</div>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<CalendarComponent mode="single" selected={date} onSelect={setDate} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input type="search" placeholder="Search Farms" className="pl-8" />
|
||||
</div>
|
||||
|
||||
<Button>
|
||||
<Plus className="mr-2 h-4 w-4" /> Add
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="border rounded-md">
|
||||
<h3 className="px-4 py-2 border-b font-medium">Table Fields</h3>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Category</TableHead>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead className="text-right">Quantity</TableHead>
|
||||
<TableHead>Last Updated</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredItems.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="text-center py-8 text-muted-foreground">
|
||||
No inventory items found
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
filteredItems.map((item) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell className="font-medium">{item.name}</TableCell>
|
||||
<TableCell>{item.category}</TableCell>
|
||||
<TableCell>{item.type}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{item.quantity} {item.unit}
|
||||
</TableCell>
|
||||
<TableCell>{item.lastUpdated}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={item.status === "Low Stock" ? "destructive" : "default"}>{item.status}</Badge>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user