mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 22:14:08 +01:00
ui: modify new crop dialog
This commit is contained in:
parent
215c178249
commit
20c1efa325
@ -4,9 +4,10 @@ import { Crop } from "@/types";
|
|||||||
|
|
||||||
interface CropCardProps {
|
interface CropCardProps {
|
||||||
crop: Crop;
|
crop: Crop;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CropCard({ crop }: CropCardProps) {
|
export function CropCard({ crop, onClick }: CropCardProps) {
|
||||||
const statusColors = {
|
const statusColors = {
|
||||||
growing: "text-green-500",
|
growing: "text-green-500",
|
||||||
harvested: "text-yellow-500",
|
harvested: "text-yellow-500",
|
||||||
@ -14,8 +15,10 @@ export function CropCard({ crop }: CropCardProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full bg-muted/50 hover:bg-muted/80 transition-all cursor-pointer group hover:shadow-lg">
|
<Card
|
||||||
<CardHeader className="p-4 pb-2">
|
onClick={onClick}
|
||||||
|
className="w-full bg-muted/50 hover:bg-muted/80 transition-all cursor-pointer group hover:shadow-lg">
|
||||||
|
<CardHeader className="p-4 pb-0">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center">
|
<div className="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center">
|
||||||
<Sprout className="h-4 w-4 text-primary" />
|
<Sprout className="h-4 w-4 text-primary" />
|
||||||
@ -24,10 +27,10 @@ export function CropCard({ crop }: CropCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-medium truncate">{crop.name}</h3>
|
<h3 className="text-xl font-medium truncate">{crop.name}</h3>
|
||||||
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||||
<Calendar className="h-3 w-3" />
|
<Calendar className="h-4 w-4" />
|
||||||
<p>Planted: {crop.plantedDate.toLocaleDateString()}</p>
|
<p>Planted: {crop.plantedDate.toLocaleDateString()}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
131
frontend/app/(sidebar)/farms/[farmId]/crop-dialog.tsx
Normal file
131
frontend/app/(sidebar)/farms/[farmId]/crop-dialog.tsx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { Check, MapPin } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import type { Crop } from "@/types";
|
||||||
|
|
||||||
|
interface Plant {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
image: string;
|
||||||
|
growthTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const plants: Plant[] = [
|
||||||
|
{
|
||||||
|
id: "durian",
|
||||||
|
name: "Durian",
|
||||||
|
image: "/placeholder.svg?height=80&width=80",
|
||||||
|
growthTime: "4-5 months",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "mango",
|
||||||
|
name: "Mango",
|
||||||
|
image: "/placeholder.svg?height=80&width=80",
|
||||||
|
growthTime: "3-4 months",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "coconut",
|
||||||
|
name: "Coconut",
|
||||||
|
image: "/placeholder.svg?height=80&width=80",
|
||||||
|
growthTime: "5-6 months",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface CropDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
onSubmit: (data: Partial<Crop>) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CropDialog({ open, onOpenChange, onSubmit }: CropDialogProps) {
|
||||||
|
const [selectedPlant, setSelectedPlant] = useState<string | null>(null);
|
||||||
|
const [location, setLocation] = useState({ lat: 13.7563, lng: 100.5018 }); // Bangkok coordinates
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!selectedPlant) return;
|
||||||
|
|
||||||
|
await onSubmit({
|
||||||
|
name: plants.find((p) => p.id === selectedPlant)?.name || "",
|
||||||
|
plantedDate: new Date(),
|
||||||
|
status: "planned",
|
||||||
|
});
|
||||||
|
|
||||||
|
setSelectedPlant(null);
|
||||||
|
onOpenChange(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-[900px] p-0">
|
||||||
|
<div className="grid md:grid-cols-2 h-[600px]">
|
||||||
|
{/* Left side - Plant Selection */}
|
||||||
|
<div className="p-6 overflow-y-auto border-r">
|
||||||
|
<h2 className="text-lg font-semibold mb-4">Select Plant to Grow</h2>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{plants.map((plant) => (
|
||||||
|
<Card
|
||||||
|
key={plant.id}
|
||||||
|
className={cn(
|
||||||
|
"p-4 cursor-pointer hover:bg-muted/50 transition-colors",
|
||||||
|
selectedPlant === plant.id && "border-primary bg-primary/5"
|
||||||
|
)}
|
||||||
|
onClick={() => setSelectedPlant(plant.id)}>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<img
|
||||||
|
src={plant.image || "/placeholder.svg"}
|
||||||
|
alt={plant.name}
|
||||||
|
className="w-20 h-20 rounded-lg object-cover"
|
||||||
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="font-medium">{plant.name}</h3>
|
||||||
|
{selectedPlant === plant.id && <Check className="h-4 w-4 text-primary" />}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">Growth time: {plant.growthTime}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side - Map */}
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-0 bg-muted/10">
|
||||||
|
{/* Placeholder map - Replace with your map component */}
|
||||||
|
<div className="h-full w-full bg-muted/20 flex items-center justify-center">
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<MapPin className="h-8 w-8 mx-auto text-muted-foreground" />
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Map placeholder
|
||||||
|
<br />
|
||||||
|
Lat: {location.lat.toFixed(4)}
|
||||||
|
<br />
|
||||||
|
Lng: {location.lng.toFixed(4)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 p-4 bg-background border-t">
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSubmit} disabled={!selectedPlant}>
|
||||||
|
Plant Crop
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -6,29 +6,28 @@ import { ArrowLeft, MapPin, Plus, Sprout } from "lucide-react";
|
|||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
import { CropDialog } from "./crop-dialog";
|
||||||
import { AddCropForm } from "./add-crop-form";
|
|
||||||
import { CropCard } from "./crop-card";
|
import { CropCard } from "./crop-card";
|
||||||
import { Farm, Crop } from "@/types";
|
import { Farm, Crop } from "@/types";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const crops: Crop[] = [
|
const crops: Crop[] = [
|
||||||
{
|
{
|
||||||
id: "crop1",
|
id: "1",
|
||||||
farmId: "1",
|
farmId: "1",
|
||||||
name: "Monthong Durian",
|
name: "Monthong Durian",
|
||||||
plantedDate: new Date("2023-03-15"),
|
plantedDate: new Date("2023-03-15"),
|
||||||
status: "growing",
|
status: "growing",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "crop2",
|
id: "2",
|
||||||
farmId: "1",
|
farmId: "1",
|
||||||
name: "Chanee Durian",
|
name: "Chanee Durian",
|
||||||
plantedDate: new Date("2023-02-20"),
|
plantedDate: new Date("2023-02-20"),
|
||||||
status: "planned",
|
status: "planned",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "crop3",
|
id: "3",
|
||||||
farmId: "2",
|
farmId: "2",
|
||||||
name: "Kradum Durian",
|
name: "Kradum Durian",
|
||||||
plantedDate: new Date("2022-11-05"),
|
plantedDate: new Date("2022-11-05"),
|
||||||
@ -76,6 +75,7 @@ export default function FarmDetailPage({ params }: { params: Promise<{ farmId: s
|
|||||||
status: data.status!,
|
status: data.status!,
|
||||||
};
|
};
|
||||||
setCrops((prevCrops) => [...prevCrops, newCrop]);
|
setCrops((prevCrops) => [...prevCrops, newCrop]);
|
||||||
|
// When the crop gets added, close the dialog
|
||||||
setIsDialogOpen(false);
|
setIsDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ export default function FarmDetailPage({ params }: { params: Promise<{ farmId: s
|
|||||||
<h2 className="text-xl font-bold mb-4">Crops</h2>
|
<h2 className="text-xl font-bold mb-4">Crops</h2>
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
{/* Clickable "Add Crop" Card */}
|
||||||
<Card
|
<Card
|
||||||
className="w-full bg-muted/50 hover:bg-muted/80 transition-all cursor-pointer group hover:shadow-lg"
|
className="w-full bg-muted/50 hover:bg-muted/80 transition-all cursor-pointer group hover:shadow-lg"
|
||||||
onClick={() => setIsDialogOpen(true)}>
|
onClick={() => setIsDialogOpen(true)}>
|
||||||
@ -137,17 +137,18 @@ export default function FarmDetailPage({ params }: { params: Promise<{ farmId: s
|
|||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<DialogContent className="sm:max-w-[425px]">
|
|
||||||
<DialogHeader>
|
{/* New Crop Dialog */}
|
||||||
<DialogTitle>Add New Crop</DialogTitle>
|
<CropDialog open={isDialogOpen} onOpenChange={setIsDialogOpen} onSubmit={handleAddCrop} />
|
||||||
<DialogDescription>Fill out the form to add a new crop to your farm.</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<AddCropForm onSubmit={handleAddCrop} onCancel={() => setIsDialogOpen(false)} />
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{crops.map((crop) => (
|
{crops.map((crop) => (
|
||||||
<CropCard key={crop.id} crop={crop} />
|
<CropCard
|
||||||
|
key={crop.id}
|
||||||
|
crop={crop}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/farms/${crop.farmId}/crops/${crop.id}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,12 +4,14 @@ import { useState } from "react";
|
|||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Search } from "lucide-react";
|
import { Link, Search } from "lucide-react";
|
||||||
import { FarmCard } from "./farm-card";
|
import { FarmCard } from "./farm-card";
|
||||||
import { AddFarmForm } from "./add-farm-form";
|
import { AddFarmForm } from "./add-farm-form";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import type { Farm } from "@/types";
|
import type { Farm } from "@/types";
|
||||||
|
|
||||||
export default function FarmSetupPage() {
|
export default function FarmSetupPage() {
|
||||||
|
const router = useRouter();
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [farms, setFarms] = useState<Farm[]>([
|
const [farms, setFarms] = useState<Farm[]>([
|
||||||
@ -17,7 +19,7 @@ export default function FarmSetupPage() {
|
|||||||
id: "1",
|
id: "1",
|
||||||
name: "Green Valley Farm",
|
name: "Green Valley Farm",
|
||||||
location: "Bangkok",
|
location: "Bangkok",
|
||||||
type: "durian",
|
type: "Durian",
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@ -70,7 +72,14 @@ export default function FarmSetupPage() {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{filteredFarms.map((farm) => (
|
{filteredFarms.map((farm) => (
|
||||||
<FarmCard key={farm.id} variant="farm" farm={farm} />
|
<FarmCard
|
||||||
|
key={farm.id}
|
||||||
|
variant="farm"
|
||||||
|
farm={farm}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/farms/${farm.id}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user