Skip to content
Merged
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
67 changes: 67 additions & 0 deletions src/component/ai_explore/DistanceSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useMemo } from 'react';
import { cn } from '@/utils/cn';

type Props = {
min?: number;
max?: number;
step?: number;
value: number | null;
onChange: (v: number) => void;
className?: string;
};

export default function DistanceSlider({
min = 1,
max = 50,
step = 1,
value,
onChange,
className,
}: Props) {
const safeValue = value ?? min;
const percent = useMemo(
() => Math.round(((safeValue - min) / (max - min)) * 100),
[safeValue, min, max],
);
return (
<div className={cn('w-full', className)}>
<div className="flex items-baseline justify-between">
<span className="text-title1 text-green1">거리(km)</span>
<span className="text-title1 text-green1">
{value === null ? `N km` : `${safeValue}km`}
</span>
</div>
<div className="text-caption4 text-green1 mt-3 flex justify-between">
<span>{min}</span>
<span>{max}</span>
</div>
<div className="relative h-6">
<div className="bg-gray2 pointer-events-none absolute top-1/2 right-0 left-0 h-2 -translate-y-1/2 rounded-full" />
<div
className="bg-yellow1 pointer-events-none absolute top-1/2 left-0 h-2 -translate-y-1/2 rounded-full"
style={{ width: `${percent}%` }}
/>
{/*노브*/}
<div
className="pointer-events-none absolute top-1/2 -translate-y-1/2"
style={{ left: `calc(${percent}% - 10px)` }}
>
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-white shadow-[0_3px_5px_rgba(0,0,0,0.25)] ring-2 ring-white">
<span className="bg-yellow1 block h-2.5 w-2.5 rounded-full" />
</div>
</div>

<input
type="range"
min={min}
max={max}
step={step}
value={safeValue}
onChange={(e) => onChange(Number(e.target.value))}
aria-label="거리 (km)"
className="absolute inset-0 w-full cursor-pointer opacity-0"
/>
</div>
</div>
);
}
14 changes: 11 additions & 3 deletions src/pages/ai/MainAI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@ import { useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { LabeledInput, Button, Header, Sidebar } from '@/component';
import { useAIExploreStore } from '@/stores/useAIExploreStore';
import DistanceSlider from '@/component/ai_explore/DistanceSlider';

export default function AiExplorePage() {
const navigate = useNavigate();
const address = useAIExploreStore((s) => s.address);
const theme = useAIExploreStore((s) => s.theme);
const distanceKm = useAIExploreStore((s) => s.distanceKm);
const setAddress = useAIExploreStore((s) => s.setAddress);
const setDistanceKm = useAIExploreStore((s) => s.setDistanceKm);

const isReady = useMemo(() => !!address && !!theme?.subs.length, [address, theme]);
const isReady = useMemo(
() => !!address && !!theme?.subs.length && distanceKm !== null,
[address, theme, distanceKm],
);

const fillSampleAddress = () => {
setAddress('경기도 성남시 수정구 성남대로 1342 이렇게 길어지면'); // UI 확인용입니당... 추후 API 연결
};

const goThemeSelect = () => navigate('/explore/theme'); // ← 쿼리/state 없이 이동
const goThemeSelect = () => navigate('/explore/theme');

const startSearch = () => navigate('/explore/result'); // ← 결과 페이지에서 AI 호출
const startSearch = () => navigate('/explore/result');

const [isSidebarOpen, setIsSidebarOpen] = useState(false);

Expand Down Expand Up @@ -56,6 +62,8 @@ export default function AiExplorePage() {
onClick={goThemeSelect}
/>

<DistanceSlider value={distanceKm} onChange={setDistanceKm} />

<div className="fixed right-0 bottom-[10px] left-0 z-10 mx-auto w-full max-w-[430px] px-10 pt-4 pb-6">
<Button
variant="lg"
Expand Down
7 changes: 5 additions & 2 deletions src/pages/ai/ThemeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ const ThemeSelcetPage = () => {

const done = () => {
setTheme({ main, subs });
navigate('/explore');
navigate('/explore', { replace: true });
};

return (
<div className="min-h-screen">
<div className="bg-green3-light relative h-10 w-full">
<button onClick={() => navigate(-1)} className="absolute top-1/2 -translate-y-1/2 pl-4">
<button
onClick={() => navigate('/explore', { replace: true })}
className="absolute top-1/2 -translate-y-1/2 pl-4"
>
<ArrowLeft className="w-4" />
</button>
<span className="text-caption3 text-green1 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
Expand Down
6 changes: 5 additions & 1 deletion src/stores/useAIExploreStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ export type ThemeGroup = { main: string; subs: string[] };
type ExploreState = {
address: string;
theme: ThemeGroup | null;
distanceKm: number | null;
setAddress: (v: string) => void;
setTheme: (v: ThemeGroup | null) => void;
setDistanceKm: (v: number | null) => void;
reset: () => void;
};

export const useAIExploreStore = create<ExploreState>((set) => ({
address: '',
theme: null,
distanceKm: null,
setAddress: (v) => set({ address: v }),
setTheme: (v) => set({ theme: v }),
reset: () => set({ address: '', theme: null }),
setDistanceKm: (v) => set({ distanceKm: v }),
reset: () => set({ address: '', theme: null, distanceKm: null }),
}));