Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client"

import { useState } from "react"
import Image from "next/image"
import profiles from "mock/profiles.json"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Plus } from "lucide-react"
import ImageManager from "./modals/image-manager";

type ImageItem = {
id: string
url: string
}

export default function ProfileImageScroll({ userId }: { userId: string }) {
const user = profiles[0]

const [images, setImages] = useState<ImageItem[]>(() =>
user.lifestyle_images?.map((url: string, index: number) => ({
id: `${user.user_id}-${index}`,
url,
})) ?? []
)

const [open, setOpen] = useState(false)

return (
<div>
<ScrollArea className="max-w-300 rounded-md border whitespace-nowrap">
<div className="flex w-max space-x-4 p-4">

<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<button className="h-80 w-60 rounded-md bg-muted border-gray-300 hover:bg-neutral-200 border-2 border-dashed flex items-center justify-center transition">
<Plus size={32} />
</button>
</DialogTrigger>

<DialogContent>
<DialogHeader>
<DialogTitle>Manage Lifestyle Images</DialogTitle>
</DialogHeader>

<ImageManager
images={images}
setImages={setImages}
onClose={() => setOpen(false)}
/>
</DialogContent>
</Dialog>

{images.map((img) => (
<div key={img.id} className="h-80 w-60 relative">
<Image
src={img.url}
alt="Lifestyle image"
fill
className="rounded-md object-cover"
/>
</div>
))}
</div>

<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use client"

import { useState } from "react"
import { Button } from "@/components/ui/button"

type ImageItem = {
id: string
url: string
}

type ImageManagerProps = {
images: ImageItem[]
setImages: React.Dispatch<React.SetStateAction<ImageItem[]>>
onClose: () => void
}

export default function ImageManager({ images, setImages, onClose }: ImageManagerProps) {
const [localImages, setLocalImages] = useState<ImageItem[]>(images)

const handleAddField = () => {
setLocalImages((prev) => [
...prev,
{ id: crypto.randomUUID(), url: "" },
])
}

const handleChange = (id: string, value: string) => {
setLocalImages((prev) =>
prev.map((img) =>
img.id === id ? { ...img, url: value } : img
)
)
}

const handleRemove = (id: string) => {
setLocalImages((prev) =>
prev.filter((img) => img.id !== id)
)
}

const handleSave = () => {
setImages(localImages.filter((img) => img.url.trim() !== ""))
onClose()
}

const handleCancel = () => {
onClose()
}

return (
<div className="space-y-4">
<div className="space-y-2 max-h-64 overflow-y-auto">
{localImages.map((img) => (
<div key={img.id} className="flex gap-2">
<input
type="text"
value={img.url}
onChange={(e) =>
handleChange(img.id, e.target.value)
}
placeholder="Enter image URL (e.g. /demo/image1.jpg)"
className="flex-1 border rounded px-2 py-1"
/>
<button
onClick={() => handleRemove(img.id)}
className="text-red-500 text-sm"
>
</button>
</div>
))}
</div>

<Button variant="outline" onClick={handleAddField} className="w-full">
Add Another Image
</Button>

<div className="flex gap-2">
<Button onClick={handleSave} className="flex-1">
Save
</Button>
<Button variant="secondary" onClick={handleCancel} className="flex-1">
Cancel
</Button>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Card } from "@/components/ui/card"
import EditName from "./modals/edit-name"
import profiles from "mock/profiles.json"
import SelectHobbies from "./modals/hobby-select";
import ProfileImageScroll from "./image-scroll";
export default function ProfilePage() {
const user = profiles[0];

Expand Down Expand Up @@ -38,8 +39,10 @@ export default function ProfilePage() {
</div>

</Card>


<Card className="p-4 gap-1">
<h2 className="text-xl font-semibold">Images</h2>
<ProfileImageScroll userId="1"/>
</Card>
</Card>

</div>
Expand Down
58 changes: 58 additions & 0 deletions web-app/components/ui/scroll-area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client"

import * as React from "react"
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function ScrollArea({
className,
children,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
className={cn("relative", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport"
className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
)
}

function ScrollBar({
className,
orientation = "vertical",
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
return (
<ScrollAreaPrimitive.ScrollAreaScrollbar
data-slot="scroll-area-scrollbar"
orientation={orientation}
className={cn(
"flex touch-none p-px transition-colors select-none",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb
data-slot="scroll-area-thumb"
className="relative flex-1 rounded-full bg-border"
/>
</ScrollAreaPrimitive.ScrollAreaScrollbar>
)
}

export { ScrollArea, ScrollBar }
5 changes: 5 additions & 0 deletions web-app/mock/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
["smoker", "no"],
["pets", "yes"],
["sleep_schedule", "early"]
],
"lifestyle_images": [
"/demo/selfie.png",
"/demo/room1.png",
"/demo/room2.png"
]
},
{
Expand Down