fix: landing, geolocation, and description card
* feat: add step-specific DescriptionCard instructions before user input Each flow page shows English guidance in muted instruction mode until the user makes a selection, then switches to dynamic summary copy. Co-authored-by: Cursor <cursoragent@cursor.com> * feat: polish route page, map geolocation, and landing artwork Replace landing growth SVGs with flow artwork, align Start Creating with FlowContinueBar, and search nearby florists from the user's current location. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: 이지은 <ijieun@ijieun-ui-MacBookPro.local> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
let {
|
||||
title = 'Title',
|
||||
description = 'Description Description Description',
|
||||
/** @type {'instruction' | 'summary'} */
|
||||
cardMode = 'summary',
|
||||
/** @type {import('./artworkVariants.js').ArtworkVariant} */
|
||||
variant = 'create1',
|
||||
/** edit Continue 이후 확정된 꽃다발만 전달 (그 전에는 null → Vase) */
|
||||
@@ -42,7 +44,7 @@
|
||||
</div>
|
||||
|
||||
<div class="min-w-0 shrink-0 lg:w-full lg:flex lg:justify-center">
|
||||
<DescriptionCard {title} {description} />
|
||||
<DescriptionCard {title} {description} mode={cardMode} />
|
||||
</div>
|
||||
</div>
|
||||
{#if comingSoon}
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
<script>
|
||||
let { title = 'Title', description = 'Description Description Description' } = $props();
|
||||
let {
|
||||
title = 'Title',
|
||||
description = 'Description Description Description',
|
||||
/** instruction: 입력 전 안내 톤 (muted) */
|
||||
mode = 'summary'
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="w-64 max-w-full flex-none border border-line-strong bg-white px-4 py-3 shadow-sm lg:px-6 lg:py-5">
|
||||
<h3 class="text-sm leading-snug font-semibold">{title}</h3>
|
||||
<p class="mt-2 text-xs leading-relaxed">{description}</p>
|
||||
<h3
|
||||
class={['text-sm leading-snug font-semibold', mode === 'instruction' ? 'text-muted' : 'text-ink']}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
class={[
|
||||
'mt-2 text-xs',
|
||||
mode === 'instruction' ? 'leading-snug text-muted' : 'leading-relaxed text-ink'
|
||||
]}
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import GrowthMetaphorIllustration from '$lib/components/ui/landing/GrowthMetaphorIllustration.svelte';
|
||||
|
||||
function handleStart() {
|
||||
@@ -10,7 +12,7 @@
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="relative flex min-h-dvh flex-col bg-surface px-6 py-8 font-sans text-ink sm:px-10 sm:py-10 lg:px-14"
|
||||
class="relative flex min-h-dvh flex-col bg-surface px-6 py-8 pb-[3.75rem] font-sans text-ink sm:px-10 sm:py-10 lg:px-14 lg:pb-8"
|
||||
aria-label="Every bouquet starts with a muse — seed to bouquet growth metaphor"
|
||||
>
|
||||
<div class="mx-auto flex w-full max-w-6xl min-h-0 flex-1 flex-col justify-center">
|
||||
@@ -21,16 +23,20 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto flex w-full max-w-6xl items-end justify-between gap-6 pb-2 pt-10">
|
||||
<div class="min-w-0 text-left">
|
||||
<p class="text-lg leading-none tracking-wide text-ink">AI Florist</p>
|
||||
<h1 class="mt-2 text-4xl leading-none font-bold tracking-wide sm:text-5xl lg:text-6xl">
|
||||
DearYou
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 pb-1">
|
||||
<Button onclick={handleStart}>start creating</Button>
|
||||
</div>
|
||||
<div class="mx-auto w-full max-w-6xl pt-10 pb-2">
|
||||
<p class="text-lg leading-none tracking-wide text-ink">AI Florist</p>
|
||||
<h1 class="mt-2 text-4xl leading-none font-bold tracking-wide sm:text-5xl lg:text-6xl">
|
||||
Fleumuse
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
create 등 플로우 페이지 FlowContinueBar와 동일 위치:
|
||||
mobile — 하단 고정 / desktop — 우측 56% 패널 하단 오른쪽
|
||||
-->
|
||||
<FlowContinueBar class="lg:!fixed lg:top-auto lg:right-0 lg:bottom-8 lg:left-[44%]">
|
||||
<button type="button" onclick={handleStart} class={FLOW_CONTINUE_BUTTON}>
|
||||
Start Creating ->
|
||||
</button>
|
||||
</FlowContinueBar>
|
||||
</section>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import FloristOrderMessage from './FloristOrderMessage.svelte';
|
||||
import KakaoMap from './KakaoMap.svelte';
|
||||
import ShopList from './ShopList.svelte';
|
||||
import { DEFAULT_MAP_CENTER } from '$lib/map/userLocation.js';
|
||||
|
||||
let {
|
||||
shops = [],
|
||||
@@ -12,14 +13,14 @@
|
||||
fitBounds = false,
|
||||
orderPlainText = '',
|
||||
orderKoPlainText = '',
|
||||
initialLat = DEFAULT_MAP_CENTER.lat,
|
||||
initialLng = DEFAULT_MAP_CENTER.lng,
|
||||
locationNotice = '',
|
||||
onrefresh
|
||||
} = $props();
|
||||
|
||||
const DEFAULT_LAT = 37.5665;
|
||||
const DEFAULT_LNG = 126.978;
|
||||
|
||||
let mapCenterLat = $state(DEFAULT_LAT);
|
||||
let mapCenterLng = $state(DEFAULT_LNG);
|
||||
let mapCenterLat = $state(initialLat);
|
||||
let mapCenterLng = $state(initialLng);
|
||||
let panTarget = $state(null);
|
||||
|
||||
function handleCenterChange(lat, lng) {
|
||||
@@ -46,6 +47,9 @@
|
||||
Find a nearby florist
|
||||
</h1>
|
||||
<p class="mt-3 text-sm text-muted">Move the map, then refresh to search this area.</p>
|
||||
{#if locationNotice}
|
||||
<p class="mt-2 text-xs text-muted">{locationNotice}</p>
|
||||
{/if}
|
||||
{#if mock}
|
||||
<p class="mt-2 text-xs text-muted">Showing sample shops (no Kakao API key).</p>
|
||||
{/if}
|
||||
@@ -63,8 +67,8 @@
|
||||
<div class="flex min-h-0 flex-1 flex-col gap-6 px-6 pb-8 md:px-10 lg:flex-row lg:px-12 lg:pb-10">
|
||||
<div class="relative flex min-h-64 flex-1 flex-col overflow-hidden border border-line lg:min-h-0">
|
||||
<KakaoMap
|
||||
initialLat={DEFAULT_LAT}
|
||||
initialLng={DEFAULT_LNG}
|
||||
initialLat={initialLat}
|
||||
initialLng={initialLng}
|
||||
{shops}
|
||||
selectedId={selectedShopId}
|
||||
{fitBounds}
|
||||
|
||||
27
src/lib/flowerFlow/artworkCardCopy.js
Normal file
27
src/lib/flowerFlow/artworkCardCopy.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/** Artwork DescriptionCard — 단계별 기본 instruction (입력 전) */
|
||||
|
||||
/** @typedef {{ title: string, description: string }} ArtworkCardCopy */
|
||||
|
||||
/** @type {Record<'create' | 'message' | 'generating' | 'map', ArtworkCardCopy>} */
|
||||
export const ARTWORK_CARD_DEFAULTS = {
|
||||
create: {
|
||||
title: 'Who is this bouquet for?',
|
||||
description:
|
||||
'Choose who will receive it, the occasion, and a style on the right. Adjust the budget if you like.'
|
||||
},
|
||||
message: {
|
||||
title: 'Write a card message',
|
||||
description:
|
||||
'Type a short note or pick a preset on the right. It will appear on your bouquet card.'
|
||||
},
|
||||
generating: {
|
||||
title: 'Crafting your bouquet',
|
||||
description:
|
||||
'We are turning their mood, photos, and message into a one-of-a-kind arrangement.'
|
||||
},
|
||||
map: {
|
||||
title: 'Choose a florist',
|
||||
description:
|
||||
'Browse nearby shops on the map and select where you would like to place your order.'
|
||||
}
|
||||
};
|
||||
@@ -1,14 +1,31 @@
|
||||
import seedSrc from '$lib/assets/landing/seed.svg';
|
||||
import sproutSrc from '$lib/assets/landing/sprout.svg';
|
||||
import flowerSrc from '$lib/assets/landing/flower.svg';
|
||||
import bouquetSrc from '$lib/assets/landing/bouquet.svg';
|
||||
import { ARTWORK_SRC } from '$lib/components/ui/Artwork/artworkVariants.js';
|
||||
|
||||
/** 랜딩 growth metaphor — ref/route illustration SVG 4단계 */
|
||||
/** 랜딩 growth metaphor — artwork 2 → 3 → 5 → 6 순서 */
|
||||
export const LANDING_GROWTH_STAGES = [
|
||||
{ id: 'seed', src: seedSrc, heightClass: 'h-[2.125rem] sm:h-9', delayMs: 0 },
|
||||
{ id: 'sprout', src: sproutSrc, heightClass: 'h-16 sm:h-20', delayMs: 520 },
|
||||
{ id: 'flower', src: flowerSrc, heightClass: 'h-24 sm:h-28', delayMs: 1040 },
|
||||
{ id: 'bouquet', src: bouquetSrc, heightClass: 'h-36 sm:h-44 lg:h-52', delayMs: 1560 }
|
||||
{
|
||||
id: 'create2',
|
||||
src: ARTWORK_SRC.create2,
|
||||
heightClass: 'h-16 sm:h-20',
|
||||
delayMs: 0
|
||||
},
|
||||
{
|
||||
id: 'upload1',
|
||||
src: ARTWORK_SRC.upload1,
|
||||
heightClass: 'h-24 sm:h-28',
|
||||
delayMs: 520
|
||||
},
|
||||
{
|
||||
id: 'message1',
|
||||
src: ARTWORK_SRC.message1,
|
||||
heightClass: 'h-32 sm:h-36 lg:h-40',
|
||||
delayMs: 1040
|
||||
},
|
||||
{
|
||||
id: 'generated',
|
||||
src: ARTWORK_SRC.generated,
|
||||
heightClass: 'h-36 sm:h-44 lg:h-52',
|
||||
delayMs: 1560
|
||||
}
|
||||
];
|
||||
|
||||
export const LANDING_STAGE_GAP_MS = 520;
|
||||
|
||||
39
src/lib/map/userLocation.js
Normal file
39
src/lib/map/userLocation.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/** 서울시청 — 위치 권한 거부·미지원 시 fallback */
|
||||
export const DEFAULT_MAP_CENTER = { lat: 37.5665, lng: 126.978 };
|
||||
|
||||
/**
|
||||
* @typedef {{ lat: number, lng: number, fromDevice: boolean }} UserMapCenter
|
||||
*/
|
||||
|
||||
/**
|
||||
* 브라우저 Geolocation API로 현재 위치를 가져옵니다.
|
||||
* 실패 시 DEFAULT_MAP_CENTER를 반환합니다.
|
||||
*
|
||||
* @returns {Promise<UserMapCenter>}
|
||||
*/
|
||||
export function getUserMapCenter() {
|
||||
return new Promise((resolve) => {
|
||||
if (typeof navigator === 'undefined' || !navigator.geolocation) {
|
||||
resolve({ ...DEFAULT_MAP_CENTER, fromDevice: false });
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
resolve({
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude,
|
||||
fromDevice: true
|
||||
});
|
||||
},
|
||||
() => {
|
||||
resolve({ ...DEFAULT_MAP_CENTER, fromDevice: false });
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10_000,
|
||||
maximumAge: 60_000
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
isDevSeeded,
|
||||
saveFlow
|
||||
} from '$lib/flowerFlow/session.js';
|
||||
import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
|
||||
|
||||
// 항상 빈 폼으로 시작 — Dev Fill은 onMount에서 1회만 스냅샷 적용
|
||||
let who = $state(null);
|
||||
@@ -25,7 +26,7 @@
|
||||
const hasAnySelection = $derived(who !== null || whatFor !== null || style !== null);
|
||||
|
||||
const artworkTitle = $derived.by(() => {
|
||||
if (!hasAnySelection) return 'Title';
|
||||
if (!hasAnySelection) return ARTWORK_CARD_DEFAULTS.create.title;
|
||||
const occasion = whatFor ? `A ${whatFor} bouquet for` : 'A bouquet for';
|
||||
return `${occasion} ${who ?? '...'}`;
|
||||
});
|
||||
@@ -34,10 +35,12 @@
|
||||
|
||||
const artworkDescription = $derived(
|
||||
hasAnySelection
|
||||
? `${style ?? '...'} style · ₩${budget.toLocaleString('ko-KR')} budget`
|
||||
: 'Description Description Description'
|
||||
? `${style ?? '—'} style · ₩${budget.toLocaleString('ko-KR')} budget`
|
||||
: ARTWORK_CARD_DEFAULTS.create.description
|
||||
);
|
||||
|
||||
const artworkCardMode = $derived(hasAnySelection ? 'summary' : 'instruction');
|
||||
|
||||
onMount(() => {
|
||||
const hadSnapshot = !!getFlowObject('devCreateSnapshot');
|
||||
const snap = consumeDevCreateSnapshot();
|
||||
@@ -87,7 +90,12 @@
|
||||
<Header step={1} total={7} />
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
||||
<Artwork variant={artworkVariant} title={artworkTitle} description={artworkDescription} />
|
||||
<Artwork
|
||||
variant={artworkVariant}
|
||||
title={artworkTitle}
|
||||
description={artworkDescription}
|
||||
cardMode={artworkCardMode}
|
||||
/>
|
||||
|
||||
<section class="relative flex min-h-0 flex-1 flex-col pb-[3.75rem] lg:overflow-hidden lg:pb-8">
|
||||
<div class="min-h-0 flex-1 overflow-y-auto">
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
loadFlow,
|
||||
saveFlow
|
||||
} from '$lib/flowerFlow/session.js';
|
||||
import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
|
||||
|
||||
const MAX_RETRIES = 5;
|
||||
const userInput = getFlowUserInput();
|
||||
@@ -24,12 +25,20 @@
|
||||
const artworkTitle = $derived.by(() => {
|
||||
const who = typeof userInput.relationship === 'string' ? userInput.relationship : null;
|
||||
const whatFor = typeof userInput.occasion === 'string' ? userInput.occasion : null;
|
||||
if (!who && !whatFor) return 'Your bouquet';
|
||||
if (!who && !whatFor) return ARTWORK_CARD_DEFAULTS.generating.title;
|
||||
const occasion = whatFor ? `A ${whatFor} bouquet for` : 'A bouquet for';
|
||||
return `${occasion} ${who ?? '...'}`;
|
||||
});
|
||||
|
||||
const artworkDescription = $derived(cardMessage || '잠시 관리중 ~');
|
||||
const artworkDescription = $derived(
|
||||
cardMessage?.trim() || ARTWORK_CARD_DEFAULTS.generating.description
|
||||
);
|
||||
|
||||
const artworkCardMode = $derived.by(() => {
|
||||
const who = typeof userInput.relationship === 'string' ? userInput.relationship : null;
|
||||
const whatFor = typeof userInput.occasion === 'string' ? userInput.occasion : null;
|
||||
return who || whatFor || cardMessage?.trim() ? 'summary' : 'instruction';
|
||||
});
|
||||
|
||||
/** @type {import('$lib/components/ui/Artwork/artworkVariants.js').ArtworkVariant} */
|
||||
let artworkVariant = $state('create2');
|
||||
@@ -212,7 +221,13 @@
|
||||
<Header step={4} total={7} />
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
||||
<Artwork comingSoon variant={artworkVariant} title={artworkTitle} description={artworkDescription} />
|
||||
<Artwork
|
||||
comingSoon
|
||||
variant={artworkVariant}
|
||||
title={artworkTitle}
|
||||
description={artworkDescription}
|
||||
cardMode={artworkCardMode}
|
||||
/>
|
||||
|
||||
<section class="relative flex min-h-0 flex-1 flex-col lg:overflow-y-auto">
|
||||
<GenerationActivityFeed
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
import { fetchJob, toDataUrl } from '$lib/flowerFlow/api.js';
|
||||
import { buildFloristOrderMessage } from '$lib/flowerFlow/buildFloristOrderMessage.js';
|
||||
import { getFlowObject, getFlowString } from '$lib/flowerFlow/session.js';
|
||||
import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
|
||||
import { getUserMapCenter } from '$lib/map/userLocation.js';
|
||||
|
||||
const jobId = getFlowString('jobId');
|
||||
|
||||
const DEFAULT_LAT = 37.5665;
|
||||
const DEFAULT_LNG = 126.978;
|
||||
|
||||
let shops = $state([]);
|
||||
let loading = $state(true);
|
||||
let error = $state('');
|
||||
@@ -24,12 +23,24 @@
|
||||
let orderPlainText = $state('');
|
||||
let orderKoPlainText = $state('');
|
||||
let selectedImage = $state(null);
|
||||
let locationReady = $state(false);
|
||||
let searchLat = $state(37.5665);
|
||||
let searchLng = $state(126.978);
|
||||
let locationNotice = $state('');
|
||||
|
||||
const sessionUserInput = getFlowObject('userInput') ?? {};
|
||||
|
||||
const artworkTitle = $derived(selectedShopId ? 'Ready to order' : 'Your bouquet');
|
||||
const artworkTitle = $derived(
|
||||
selectedShopId ? 'Ready to order' : ARTWORK_CARD_DEFAULTS.map.title
|
||||
);
|
||||
|
||||
const artworkDescription = $derived(floristNote || 'Your selected bouquet design.');
|
||||
const artworkDescription = $derived(
|
||||
selectedShopId
|
||||
? floristNote || 'Your selected bouquet design.'
|
||||
: ARTWORK_CARD_DEFAULTS.map.description
|
||||
);
|
||||
|
||||
const artworkCardMode = $derived(selectedShopId ? 'summary' : 'instruction');
|
||||
|
||||
const bouquetImageSrc = $derived(selectedImage ? toDataUrl(selectedImage) : null);
|
||||
|
||||
@@ -88,7 +99,16 @@
|
||||
// job 없어도 지도·꽃집 검색은 계속
|
||||
}
|
||||
|
||||
await loadShops(DEFAULT_LAT, DEFAULT_LNG, { fitBounds: true });
|
||||
const center = await getUserMapCenter();
|
||||
searchLat = center.lat;
|
||||
searchLng = center.lng;
|
||||
if (!center.fromDevice) {
|
||||
locationNotice =
|
||||
'Location access unavailable. Showing flower shops near Seoul City Hall instead.';
|
||||
}
|
||||
locationReady = true;
|
||||
|
||||
await loadShops(searchLat, searchLng, { fitBounds: true });
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -98,20 +118,34 @@
|
||||
<Header step={7} total={7} />
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
||||
<Artwork title={artworkTitle} description={artworkDescription} imageSrc={bouquetImageSrc} />
|
||||
<Artwork
|
||||
title={artworkTitle}
|
||||
description={artworkDescription}
|
||||
imageSrc={bouquetImageSrc}
|
||||
cardMode={artworkCardMode}
|
||||
/>
|
||||
|
||||
<section class="relative flex min-h-0 flex-1 flex-col lg:overflow-y-auto">
|
||||
<MapPanel
|
||||
bind:selectedShopId
|
||||
{shops}
|
||||
{loading}
|
||||
{error}
|
||||
{mock}
|
||||
{orderPlainText}
|
||||
{orderKoPlainText}
|
||||
fitBounds={fitMapBounds}
|
||||
onrefresh={(lat, lng) => loadShops(lat, lng, { fitBounds: false })}
|
||||
/>
|
||||
{#if locationReady}
|
||||
<MapPanel
|
||||
bind:selectedShopId
|
||||
initialLat={searchLat}
|
||||
initialLng={searchLng}
|
||||
{locationNotice}
|
||||
{shops}
|
||||
{loading}
|
||||
{error}
|
||||
{mock}
|
||||
{orderPlainText}
|
||||
{orderKoPlainText}
|
||||
fitBounds={fitMapBounds}
|
||||
onrefresh={(lat, lng) => loadShops(lat, lng, { fitBounds: false })}
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex flex-1 items-center justify-center px-6 py-16 text-sm text-muted">
|
||||
Getting your location...
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
loadFlow,
|
||||
saveFlow
|
||||
} from '$lib/flowerFlow/session.js';
|
||||
import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
|
||||
|
||||
const userInput = getFlowUserInput();
|
||||
|
||||
@@ -27,11 +28,19 @@
|
||||
let error = $state('');
|
||||
let skipping = $state(false);
|
||||
|
||||
const artworkVariant = $derived(message.trim() ? 'message1' : 'upload2');
|
||||
const hasMessage = $derived(message.trim().length > 0);
|
||||
|
||||
const artworkTitle = $derived(message ? 'Your message' : 'Title');
|
||||
const artworkVariant = $derived(hasMessage ? 'message1' : 'upload2');
|
||||
|
||||
const artworkDescription = $derived(message || 'Description Description Description');
|
||||
const artworkTitle = $derived(
|
||||
hasMessage ? 'Your message' : ARTWORK_CARD_DEFAULTS.message.title
|
||||
);
|
||||
|
||||
const artworkDescription = $derived(
|
||||
hasMessage ? message.trim() : ARTWORK_CARD_DEFAULTS.message.description
|
||||
);
|
||||
|
||||
const artworkCardMode = $derived(hasMessage ? 'summary' : 'instruction');
|
||||
|
||||
onMount(() => {
|
||||
const hadSnapshot = !!getFlowObject('devMessageSnapshot');
|
||||
@@ -107,7 +116,12 @@
|
||||
<Header step={3} total={7} />
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
||||
<Artwork variant={artworkVariant} title={artworkTitle} description={artworkDescription} />
|
||||
<Artwork
|
||||
variant={artworkVariant}
|
||||
title={artworkTitle}
|
||||
description={artworkDescription}
|
||||
cardMode={artworkCardMode}
|
||||
/>
|
||||
|
||||
<section class="relative flex min-h-0 flex-1 flex-col pb-[3.75rem] lg:overflow-hidden lg:pb-8">
|
||||
<div class="min-h-0 flex-1 overflow-y-auto">
|
||||
|
||||
@@ -127,6 +127,15 @@
|
||||
const artworkTitle = $derived(artworkCopy.title);
|
||||
const artworkDescription = $derived(artworkCopy.description);
|
||||
|
||||
const artworkCardMode = $derived.by(() => {
|
||||
if (mode === 'sns') return snsHasImage ? 'summary' : 'instruction';
|
||||
|
||||
const count = ['color', 'season', 'character', 'location'].filter(
|
||||
(key) => moodboardTiles[key]
|
||||
).length;
|
||||
return count === 0 ? 'instruction' : 'summary';
|
||||
});
|
||||
|
||||
/** create2(시작) → upload1(1장+) → upload2(전체 채움) */
|
||||
const artworkVariant = $derived.by(() => {
|
||||
if (mode === 'sns') {
|
||||
@@ -193,7 +202,12 @@
|
||||
<Header step={2} total={7} />
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
||||
<Artwork variant={artworkVariant} title={artworkTitle} description={artworkDescription} />
|
||||
<Artwork
|
||||
variant={artworkVariant}
|
||||
title={artworkTitle}
|
||||
description={artworkDescription}
|
||||
cardMode={artworkCardMode}
|
||||
/>
|
||||
|
||||
<section
|
||||
class="relative flex min-h-0 flex-1 flex-col pt-4 pb-[3.75rem] lg:grid lg:grid-rows-[auto_minmax(0,1fr)_auto] lg:overflow-hidden lg:pt-6 lg:pb-8"
|
||||
|
||||
Reference in New Issue
Block a user