diff --git a/src/App.tsx b/src/App.tsx index 2e33306..6df4f86 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -334,14 +334,14 @@ const App: React.FC = () => { }; // 업체명·주소 수동 입력으로 마케팅 분석 API 호출 - const handleManualInput = async (businessName: string, address: string) => { + const handleManualInput = async (businessName: string, address: string, category: string) => { setAfterLoadTarget('generation_flow'); setViewMode('loading'); setIsAnalysisComplete(false); setError(null); try { - const data = await marketingAnalysis(businessName, address); + const data = await marketingAnalysis(businessName, address, category); if (!validateCrawlingResponse(data)) { throw new Error(t('app.autocompleteError')); diff --git a/src/components/BusinessNameInputModal.tsx b/src/components/BusinessNameInputModal.tsx index 804c585..22f844a 100644 --- a/src/components/BusinessNameInputModal.tsx +++ b/src/components/BusinessNameInputModal.tsx @@ -5,7 +5,7 @@ import CitySelectModal, { REGIONS } from './CitySelectModal'; interface BusinessNameInputModalProps { onClose: () => void; - onSubmit: (businessName: string, address: string) => void; + onSubmit: (businessName: string, address: string, category: string) => void; } const BusinessNameInputModal: React.FC = ({ onClose, onSubmit }) => { @@ -13,6 +13,7 @@ const BusinessNameInputModal: React.FC = ({ onClose const [businessName, setBusinessName] = useState(''); const [selectedCity, setSelectedCity] = useState(''); const [detailAddress, setDetailAddress] = useState(''); + const [category, setCategory] = useState(''); const [isCityModalOpen, setIsCityModalOpen] = useState(false); useEffect(() => { @@ -41,7 +42,7 @@ const BusinessNameInputModal: React.FC = ({ onClose const handleSubmit = () => { if (!isValid) return; const fullAddress = `${selectedCity} ${detailAddress.trim()}`; - onSubmit(businessName.trim(), fullAddress); + onSubmit(businessName.trim(), fullAddress, category.trim()); onClose(); }; @@ -104,6 +105,19 @@ const BusinessNameInputModal: React.FC = ({ onClose /> +
+ + setCategory(e.target.value)} + onKeyDown={handleKeyDown} + maxLength={30} + /> +
+
+ {/* 진행 표시 전용 — Azure Blob 익명 응답에 Accept-Ranges가 없어 seek가 불안정하므로 드래그 미지원 */}
= ({ {/* Lyrics Display */}
- {lyrics ? (() => { - const lines = lyrics.split('\n').filter(l => l.trim()); - const sections: { tag: string | null; lines: string[] }[] = []; - lines.forEach(line => { - const tagMatch = line.trim().match(/^\[(.+)\]$/); - if (tagMatch) { - sections.push({ tag: `[${tagMatch[1]}]`, lines: [] }); - } else if (sections.length === 0) { - sections.push({ tag: null, lines: [line] }); - } else { - sections[sections.length - 1].lines.push(line); - } - }); - return ( -
- {sections.filter(s => s.lines.length > 0).map((section, i) => ( -
- {section.tag && {section.tag}} -

{section.lines.join('\n')}

-
- ))} -
- ); - })() : ( + {lyrics ? ( +
+ {lyricsSections.map((section, i) => ( +
+ {section.tag && {section.tag}} +

{section.lines.join('\n')}

+
+ ))} +
+ ) : (
{selectedType === '배경음악' ? t('soundStudio.lyricsPlaceholderBGM') : t('soundStudio.lyricsPlaceholder')}
diff --git a/src/pages/Dashboard/UrlInputContent.tsx b/src/pages/Dashboard/UrlInputContent.tsx index 2ddcdea..32f9b7d 100644 --- a/src/pages/Dashboard/UrlInputContent.tsx +++ b/src/pages/Dashboard/UrlInputContent.tsx @@ -5,7 +5,7 @@ import SearchInputForm, { SearchType } from '../../components/SearchInputForm'; interface UrlInputContentProps { onAnalyze: (value: string, type?: SearchType) => void; onAutocomplete?: (data: AutocompleteRequest) => void; - onManualInput?: (businessName: string, address: string) => void; + onManualInput?: (businessName: string, address: string, category: string) => void; error: string | null; } diff --git a/src/pages/Landing/HeroSection.tsx b/src/pages/Landing/HeroSection.tsx index fc36817..b76922f 100755 --- a/src/pages/Landing/HeroSection.tsx +++ b/src/pages/Landing/HeroSection.tsx @@ -35,7 +35,7 @@ const orbConfigs: OrbConfig[] = [ interface HeroSectionProps { onAnalyze?: (value: string, type?: SearchType) => void; onAutocomplete?: (data: AutocompleteRequest) => void; - onManualInput?: (businessName: string, address: string) => void; + onManualInput?: (businessName: string, address: string, category: string) => void; onNext?: () => void; error?: string | null; scrollProgress?: number; @@ -184,10 +184,10 @@ const HeroSection: React.FC = ({ onAnalyze, onAutocomplete, on {isManualModalOpen && ( setIsManualModalOpen(false)} - onSubmit={(businessName, address) => { + onSubmit={(businessName, address, category) => { if (tutorial.isActive) tutorial.nextHint(); setIsManualModalOpen(false); - onManualInput?.(businessName, address); + onManualInput?.(businessName, address, category); }} /> )} diff --git a/src/styles/base-components.css b/src/styles/base-components.css index f4b5531..7648c4a 100644 --- a/src/styles/base-components.css +++ b/src/styles/base-components.css @@ -63,7 +63,7 @@ height: 100%; display: flex; flex-direction: column; - background-color: #002224; + background-color: var(--color-bg-darker); overflow-y: auto; } diff --git a/src/styles/generation-flow.css b/src/styles/generation-flow.css index 0e2efb1..4051182 100644 --- a/src/styles/generation-flow.css +++ b/src/styles/generation-flow.css @@ -910,7 +910,7 @@ } .comp2-lyrics-tag { - font-size: 12px; + font-size: 14px; font-weight: 700; color: #379599; letter-spacing: 0.05em; diff --git a/src/styles/studio-assets.css b/src/styles/studio-assets.css index 809ffdd..96820e9 100644 --- a/src/styles/studio-assets.css +++ b/src/styles/studio-assets.css @@ -550,7 +550,7 @@ padding: 0 1rem; display: flex; flex-direction: column; - gap: 1.25rem; + gap: 0.5rem; scrollbar-color: #046266 transparent; } @@ -810,7 +810,7 @@ /* Asset Page Layout */ .asset-page { height: 100%; - background-color: #002224; + background-color: var(--color-darker); position: relative; } diff --git a/src/utils/api.ts b/src/utils/api.ts index 415b412..e5d6a42 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -911,7 +911,7 @@ export async function autocomplete(request: AutocompleteRequest): Promise { +export async function marketingAnalysis(storeName: string, address: string, category = ''): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), CRAWL_TIMEOUT); @@ -921,7 +921,7 @@ export async function marketingAnalysis(storeName: string, address: string): Pro headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ store_name: storeName, address }), + body: JSON.stringify({ store_name: storeName, address, category }), signal: controller.signal, });