The recipes.blue monorepo recipes.blue
recipes appview atproto
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: brand new individual recipe page

+49 -47
+49 -47
apps/web/src/routes/_.(app)/recipes/$author/$rkey/index.lazy.tsx
··· 9 9 } from '@/components/ui/breadcrumb' 10 10 import { Separator } from '@/components/ui/separator' 11 11 import { SidebarTrigger } from '@/components/ui/sidebar' 12 - import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' 12 + import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' 13 13 import { recipeQueryOptions } from '@/queries/recipe' 14 14 import { useSuspenseQuery } from '@tanstack/react-query' 15 15 import { useXrpc } from '@/hooks/use-xrpc' 16 16 import { Badge } from '@/components/ui/badge' 17 17 import { Clock, Users } from 'lucide-react' 18 + import { useAuth } from '@/state/auth' 19 + import { Button } from '@/components/ui/button' 18 20 19 21 export const Route = createLazyFileRoute('/_/(app)/recipes/$author/$rkey/')({ 20 22 component: RouteComponent, ··· 27 29 data: { recipe }, 28 30 error, 29 31 } = useSuspenseQuery(recipeQueryOptions(rpc, author, rkey)) 32 + const { isLoggedIn, agent } = useAuth(); 30 33 31 34 if (error) return <p>Error</p> 32 35 ··· 66 69 </div> 67 70 </header> 68 71 <div className="flex flex-col gap-4 px-4 py-8 items-center max-w-2xl w-full mx-auto"> 69 - <div className="relative h-48 w-full"> 70 - <img 71 - src={"https://www.foodandwine.com/thmb/fjNakOY7IcuvZac1hR3JcSo7vzI=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/FAW-recipes-pasta-sausage-basil-and-mustard-hero-06-cfd1c0a2989e474ea7e574a38182bbee.jpg"} 72 - alt={recipe.title} 73 - className="h-full w-full object-cover rounded-xl shadow-card" 74 - /> 75 - </div> 72 + <Card className="w-full"> 76 73 77 - <p className="text-muted-foreground"> 78 - By {recipe.author.displayName} 79 - </p> 80 - <h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl"> 81 - {recipe.title} 82 - </h1> 83 - <div className="flex items-center gap-4"> 84 - <Badge className="flex items-center gap-2"> 85 - <Clock className="size-4" /> 86 - <span>{recipe.time} mins</span> 87 - </Badge> 74 + <CardHeader> 75 + <CardTitle className="text-3xl font-bold">{recipe.title}</CardTitle> 76 + <CardDescription>{recipe.description}</CardDescription> 77 + </CardHeader> 88 78 89 - <Badge className="flex items-center gap-2"> 90 - <Users className="size-4" /> 91 - <span>Serves 2</span> 92 - </Badge> 93 - </div> 94 - <p className="leading-7 text-center"> 95 - {recipe.description} 96 - </p> 97 - </div> 98 - <div className="flex flex-1 flex-col gap-4 p-4 pt-0 w-full max-w-2xl items-center mx-auto"> 99 - <div className="grid gap-4 w-full"> 100 - <Card> 101 - <CardHeader> 102 - <CardTitle>Ingredients</CardTitle> 103 - </CardHeader> 104 - <CardContent> 105 - <ul className="flex flex-col"> 79 + <CardContent className="space-y-6"> 80 + <img 81 + src={"https://www.foodandwine.com/thmb/fjNakOY7IcuvZac1hR3JcSo7vzI=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/FAW-recipes-pasta-sausage-basil-and-mustard-hero-06-cfd1c0a2989e474ea7e574a38182bbee.jpg"} 82 + alt={recipe.title} 83 + className="h-64 w-full object-cover rounded-md" 84 + /> 85 + <div className="flex flex-wrap gap-4"> 86 + <Badge variant="secondary" className="flex items-center gap-2"> 87 + <Clock className="size-4" /> 88 + <span>{recipe.time} mins</span> 89 + </Badge> 90 + <Badge variant="secondary" className="flex items-center gap-2"> 91 + <Users className="size-4" /> 92 + <span>Serves 2</span> 93 + </Badge> 94 + </div> 95 + 96 + <div> 97 + <h3 className="text-xl font-semibold mb-2">Ingredients</h3> 98 + <ul className="list-disc list-inside space-y-1"> 106 99 {recipe.ingredients.map((ing, idx) => ( 107 100 <li key={idx}> 108 101 <b>{ing.amount}</b> {ing.name} 109 102 </li> 110 103 ))} 111 104 </ul> 112 - </CardContent> 113 - </Card> 114 - <Card> 115 - <CardHeader> 116 - <CardTitle>Steps</CardTitle> 117 - </CardHeader> 118 - <CardContent> 119 - <ol className="list-decimal gap-y-4 flex flex-col ml-4"> 105 + </div> 106 + 107 + <div> 108 + <h3 className="text-xl font-semibold mb-2">Steps</h3> 109 + <ol className="list-decimal list-outside space-y-1 ml-4"> 120 110 {recipe.steps.map((ing, idx) => ( 121 111 <li key={idx}>{ing.text}</li> 122 112 ))} 123 113 </ol> 124 - </CardContent> 125 - </Card> 126 - </div> 114 + </div> 115 + </CardContent> 116 + <CardFooter className="flex justify-between"> 117 + {(isLoggedIn && agent?.sub == recipe.author.did) && ( 118 + <div className="flex items-center gap-x-4"> 119 + <Button variant="outline">Edit</Button> 120 + <Button variant="destructive">Delete</Button> 121 + </div> 122 + )} 123 + 124 + <div className="flex items-center gap-x-4"> 125 + {/* TODO: share options */} 126 + </div> 127 + </CardFooter> 128 + </Card> 127 129 </div> 128 130 </> 129 131 )