.
This commit is contained in:
@@ -0,0 +1,830 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
|
||||
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
||||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||
import IconShare from '@/components/icons/IconShare.vue'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import { useCatalogStore } from '@/stores/catalog'
|
||||
import { useCrmStore } from '@/stores/crm'
|
||||
import { useCartStore } from '@/stores/cart'
|
||||
import { useFavoritesStore } from '@/stores/favorites'
|
||||
import { useGoBack } from '@/composables/useGoBack'
|
||||
import type { Catalog } from '@/types'
|
||||
import type { Product } from '@/types'
|
||||
import type { CatalogProductCard } from '@/types/crm'
|
||||
import {
|
||||
SEASON_LABEL,
|
||||
catalogDisplayTitle,
|
||||
catalogLatinName,
|
||||
groupCatalogPatterns,
|
||||
type PatternKind,
|
||||
} from '@/utils/catalogGallery'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import { demoProductsFor, findMockCalite } from '@/data/caliteCollections'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { goBack } = useGoBack('/catalogs')
|
||||
const catalogStore = useCatalogStore()
|
||||
const crm = useCrmStore()
|
||||
const cartStore = useCartStore()
|
||||
const favorites = useFavoritesStore()
|
||||
|
||||
const catalogId = computed(() => String(route.params.catalogId ?? ''))
|
||||
const catalog = ref<Catalog | null>(null)
|
||||
const products = ref<CatalogProductCard[]>([])
|
||||
const loading = ref(true)
|
||||
const error = ref<string | null>(null)
|
||||
const adding = ref(false)
|
||||
const toast = ref('')
|
||||
const tab = ref<'all' | PatternKind>('all')
|
||||
|
||||
const title = computed(() => catalogDisplayTitle(catalog.value?.name || 'کالیته'))
|
||||
const heroImage = computed(
|
||||
() =>
|
||||
catalog.value?.imageUrl
|
||||
|| (catalog.value ? findMockCalite(catalog.value.id)?.imageUrl : undefined)
|
||||
|| products.value.find((item) => item.imageUrl)?.imageUrl,
|
||||
)
|
||||
|
||||
const latinName = computed(() => {
|
||||
const mock = catalog.value ? findMockCalite(catalog.value.id) : undefined
|
||||
if (mock?.latinName) return mock.latinName
|
||||
return catalog.value ? catalogLatinName(catalog.value) : ''
|
||||
})
|
||||
|
||||
const usageLine = computed(() => {
|
||||
const mock = catalog.value ? findMockCalite(catalog.value.id) : undefined
|
||||
if (mock?.subtitle) return mock.subtitle
|
||||
const id = products.value[0]?.categoryId
|
||||
if (id === 'trousers') return 'پارچههای شلواری'
|
||||
if (id === 'shirt') return 'پارچههای پیراهنی'
|
||||
return 'پارچههای کتوشلواری'
|
||||
})
|
||||
|
||||
const patterns = computed(() => groupCatalogPatterns(products.value))
|
||||
const visiblePatterns = computed(() =>
|
||||
tab.value === 'all' ? patterns.value : patterns.value.filter((item) => item.kind === tab.value),
|
||||
)
|
||||
|
||||
const seasonLabel = computed(() => {
|
||||
const mock = catalog.value ? findMockCalite(catalog.value.id) : undefined
|
||||
if (mock) return SEASON_LABEL[mock.season]
|
||||
const counts: Record<string, number> = {}
|
||||
for (const product of products.value) {
|
||||
counts[product.season] = (counts[product.season] ?? 0) + 1
|
||||
}
|
||||
const top = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] as keyof typeof SEASON_LABEL | undefined
|
||||
return top ? SEASON_LABEL[top] : SEASON_LABEL.all_season
|
||||
})
|
||||
|
||||
const tabs = computed(() => [
|
||||
{ id: 'all' as const, label: 'همه طرحها', count: patterns.value.length },
|
||||
{
|
||||
id: 'plain' as const,
|
||||
label: 'ساده',
|
||||
count: patterns.value.filter((item) => item.kind === 'plain').length,
|
||||
},
|
||||
{
|
||||
id: 'melange' as const,
|
||||
label: 'ملانژ',
|
||||
count: patterns.value.filter((item) => item.kind === 'melange').length,
|
||||
},
|
||||
{
|
||||
id: 'patterned' as const,
|
||||
label: 'طرحدار',
|
||||
count: patterns.value.filter((item) => item.kind === 'patterned').length,
|
||||
},
|
||||
])
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
catalog.value = null
|
||||
products.value = []
|
||||
tab.value = 'all'
|
||||
try {
|
||||
const companies = await catalogStore.loadCompanies()
|
||||
if (!catalogStore.getCatalog(catalogId.value)) {
|
||||
await Promise.all(companies.map((company) => catalogStore.loadCatalogs(company.id)))
|
||||
}
|
||||
catalog.value = catalogStore.getCatalog(catalogId.value) ?? null
|
||||
const mock = findMockCalite(catalogId.value)
|
||||
if (!catalog.value && mock) {
|
||||
catalog.value = {
|
||||
id: mock.id,
|
||||
companyId: '',
|
||||
name: mock.name,
|
||||
code: mock.latinName || mock.name,
|
||||
description: 'ترکیبی از اصالت، کیفیت و استایل مدرن',
|
||||
displayOrder: 0,
|
||||
isActive: true,
|
||||
}
|
||||
products.value = demoProductsFor(mock)
|
||||
return
|
||||
}
|
||||
if (!catalog.value) {
|
||||
error.value = 'کالیته مورد نظر یافت نشد.'
|
||||
return
|
||||
}
|
||||
products.value = await crm.loadBrowseProducts({
|
||||
catalogId: catalog.value.id,
|
||||
companyId: catalog.value.companyId,
|
||||
})
|
||||
if (!products.value.length && mock) {
|
||||
products.value = demoProductsFor(mock)
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'بارگذاری کالیته ناموفق بود.'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await favorites.hydrate()
|
||||
await load()
|
||||
})
|
||||
watch(catalogId, load)
|
||||
|
||||
function openProduct(id: string) {
|
||||
if (id.startsWith('demo-')) return
|
||||
router.push({ path: '/product', query: { id } })
|
||||
}
|
||||
|
||||
function toggleFavorite(id: string) {
|
||||
void favorites.toggle(id)
|
||||
}
|
||||
|
||||
function requestPhysical() {
|
||||
router.push({
|
||||
path: '/catalog-request',
|
||||
query: { catalogId: catalogId.value },
|
||||
})
|
||||
}
|
||||
|
||||
function shareCatalog() {
|
||||
const url = window.location.href
|
||||
if (navigator.share) {
|
||||
void navigator.share({ title: title.value, url }).catch(() => undefined)
|
||||
return
|
||||
}
|
||||
void navigator.clipboard?.writeText(url)
|
||||
}
|
||||
|
||||
function toCartProduct(item: CatalogProductCard): Product {
|
||||
return {
|
||||
id: item.id,
|
||||
companyId: item.companyId || catalog.value?.companyId || '',
|
||||
catalogId: item.catalogId || catalogId.value,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
color: item.color || item.colors[0] || '—',
|
||||
minMeterage: item.minMeterage ?? 30,
|
||||
orderMultiplier: item.orderMultiplier ?? 10,
|
||||
basePrice: item.basePrice,
|
||||
isActive: true,
|
||||
}
|
||||
}
|
||||
|
||||
async function addAllToInquiry() {
|
||||
if (!products.value.length || adding.value) return
|
||||
adding.value = true
|
||||
toast.value = ''
|
||||
let added = 0
|
||||
let failed = ''
|
||||
try {
|
||||
for (const item of products.value) {
|
||||
const product = toCartProduct(item)
|
||||
const result = await cartStore.addItem(
|
||||
product,
|
||||
catalog.value?.code || item.catalogCode || item.code,
|
||||
product.minMeterage,
|
||||
)
|
||||
if (result.success) added += 1
|
||||
else failed = result.message || failed
|
||||
}
|
||||
toast.value = added
|
||||
? `${formatNumber(added)} طرح به سبد استعلام اضافه شد.`
|
||||
: failed || 'افزودن به سبد ناموفق بود.'
|
||||
if (added) {
|
||||
window.setTimeout(() => router.push('/cart'), 650)
|
||||
}
|
||||
} finally {
|
||||
adding.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page calite-detail">
|
||||
<div class="page__scroll page__scroll--with-nav calite-detail__scroll">
|
||||
<section class="calite-hero">
|
||||
<div
|
||||
class="calite-hero__photo"
|
||||
:style="heroImage ? { backgroundImage: `url(${heroImage})` } : undefined"
|
||||
/>
|
||||
<header class="calite-hero__nav">
|
||||
<button type="button" class="icon-btn" aria-label="بازگشت" @click="goBack">
|
||||
<IconChevronBack :size="22" color="#ffffff" />
|
||||
</button>
|
||||
<h1>{{ loading ? 'کالیته' : title }}</h1>
|
||||
<button type="button" class="icon-btn" aria-label="اشتراکگذاری" @click="shareCatalog">
|
||||
<IconShare :size="20" color="#ffffff" />
|
||||
</button>
|
||||
</header>
|
||||
<div v-if="!loading && !error" class="calite-hero__copy">
|
||||
<h2>{{ title }}</h2>
|
||||
<p class="calite-hero__usage">{{ usageLine }}</p>
|
||||
<strong v-if="latinName" class="calite-hero__latin">{{ latinName }}</strong>
|
||||
<span class="calite-hero__tagline">{{
|
||||
catalog?.description || 'ترکیبی از اصالت، کیفیت و استایل مدرن'
|
||||
}}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="calite-detail__body">
|
||||
<PageSkeleton v-if="loading" variant="cards" :rows="5" />
|
||||
<p v-else-if="error" class="calite-detail__error">{{ error }}</p>
|
||||
<template v-else>
|
||||
|
||||
<div class="attr-row">
|
||||
<span class="attr">
|
||||
<svg class="attr__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
{{ formatNumber(patterns.length) }} طرح
|
||||
</span>
|
||||
<span class="attr">
|
||||
<svg class="attr__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="1.5" />
|
||||
<path
|
||||
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
{{ seasonLabel }}
|
||||
</span>
|
||||
<span class="attr">
|
||||
<svg class="attr__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
پرطرفدار
|
||||
</span>
|
||||
<span class="attr">
|
||||
<svg class="attr__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41L13.7 2.71a2.41 2.41 0 0 0-3.41 0z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
کیفیت ممتاز
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button type="button" class="physical-cta" @click="requestPhysical">
|
||||
<span class="physical-cta__icon" aria-hidden="true">
|
||||
<BasicIcon icon="vuesax-linear:truck-time" :size="20" color="#ffffff" />
|
||||
</span>
|
||||
درخواست دریافت فیزیکی کالیته
|
||||
<span class="physical-cta__chevron" aria-hidden="true">
|
||||
<IconChevronLeft :size="18" color="#ffffff" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="pattern-tabs" role="tablist" aria-label="نوع طرح">
|
||||
<button
|
||||
v-for="item in tabs"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="pattern-tab"
|
||||
:class="{ 'is-on': tab === item.id }"
|
||||
@click="tab = item.id"
|
||||
>
|
||||
{{ item.label }}<template v-if="item.id === 'all'"> ({{ formatNumber(item.count) }})</template>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-grid">
|
||||
<article
|
||||
v-for="pattern in visiblePatterns"
|
||||
:key="pattern.id"
|
||||
class="pattern-card"
|
||||
@click="openProduct(pattern.id)"
|
||||
>
|
||||
<div
|
||||
class="pattern-card__photo"
|
||||
:style="pattern.imageUrl ? { backgroundImage: `url(${pattern.imageUrl})` } : undefined"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="pattern-card__fav"
|
||||
:aria-pressed="favorites.isFavorite(pattern.id)"
|
||||
:aria-label="
|
||||
favorites.isFavorite(pattern.id) ? 'حذف از علاقهمندیها' : 'افزودن به علاقهمندیها'
|
||||
"
|
||||
@click.stop="toggleFavorite(pattern.id)"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M12.1 20.3s-6.6-4.1-8.4-8.1c-1.4-3.1.1-6.7 3.4-7.5 1.9-.5 3.7.3 4.9 1.8 1.2-1.5 3-2.3 4.9-1.8 3.3.8 4.8 4.4 3.4 7.5-1.8 4-8.2 8.1-8.2 8.1Z"
|
||||
:fill="favorites.isFavorite(pattern.id) ? 'var(--color-primary)' : 'none'"
|
||||
stroke="var(--color-primary)"
|
||||
stroke-width="1.5"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pattern-card__meta">
|
||||
<strong>{{ pattern.name }}</strong>
|
||||
<div class="pattern-card__colors">
|
||||
<span>{{ formatNumber(pattern.colorCount) }} رنگ</span>
|
||||
<div class="swatches">
|
||||
<i
|
||||
v-for="(color, idx) in pattern.colors"
|
||||
:key="idx"
|
||||
:style="{ background: color }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<p v-if="!visiblePatterns.length" class="calite-detail__error">طرحی در این دسته نیست.</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!loading && products.length" class="sticky-footer">
|
||||
<button type="button" class="inquiry-bar" :disabled="adding" @click="addAllToInquiry">
|
||||
{{ adding ? 'در حال افزودن...' : 'افزودن همه به سبد استعلام' }}
|
||||
<small>{{ formatNumber(patterns.length) }} طرح انتخاب میشود</small>
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="toast" class="toast">{{ toast }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.calite-detail {
|
||||
background: #f4f0ed;
|
||||
color: #2a2622;
|
||||
}
|
||||
|
||||
.calite-detail__nav,
|
||||
.calite-hero__nav {
|
||||
display: grid;
|
||||
grid-template-columns: 44px 1fr 44px;
|
||||
align-items: center;
|
||||
padding: calc(6px + env(safe-area-inset-top, 0px)) 8px 2px;
|
||||
}
|
||||
|
||||
.calite-hero__nav {
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.calite-hero__nav h1 {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.calite-detail__scroll {
|
||||
padding: 0 0 112px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.calite-hero {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 340px;
|
||||
height: min(52vh, 440px);
|
||||
overflow: hidden;
|
||||
border-radius: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.calite-hero__photo {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(160deg, #3d4a55, #151b22);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.calite-hero__photo::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(12, 10, 8, 0.28) 0%,
|
||||
rgba(12, 10, 8, 0.08) 38%,
|
||||
rgba(12, 10, 8, 0.55) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.calite-hero__copy {
|
||||
position: absolute;
|
||||
inset-inline: 20px 20px;
|
||||
bottom: 22px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
gap: 2px;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.calite-hero__copy h2 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
text-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.calite-hero__usage {
|
||||
margin: 0;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 400;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.calite-hero__latin {
|
||||
margin: 2px 0 0;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: 1.75rem;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.calite-hero__tagline {
|
||||
margin-top: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.calite-detail__body {
|
||||
padding: 8px 16px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.attr-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 0;
|
||||
padding: 4px 0 8px;
|
||||
}
|
||||
|
||||
.attr {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-height: 0;
|
||||
padding: 10px 8px;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: #6f6b66;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.attr:not(:last-child)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-inline-end: 0;
|
||||
top: 16%;
|
||||
bottom: 16%;
|
||||
width: 1px;
|
||||
background: #e4dfd8;
|
||||
}
|
||||
|
||||
.attr__icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.physical-cta {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-height: 52px;
|
||||
padding: 0 44px;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font: inherit;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.physical-cta__icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.physical-cta__chevron {
|
||||
position: absolute;
|
||||
inset-inline-end: 16px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pattern-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.pattern-tab {
|
||||
flex-shrink: 0;
|
||||
height: 34px;
|
||||
padding: 0 14px;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
background: #ece8e2;
|
||||
color: #6a645e;
|
||||
font: inherit;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pattern-tab.is-on {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pattern-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pattern-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 0 0 10px;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
background: #fff;
|
||||
text-align: start;
|
||||
cursor: pointer;
|
||||
color: #1c1c1c;
|
||||
box-shadow: 0 1px 3px rgba(40, 32, 24, 0.05);
|
||||
}
|
||||
|
||||
.pattern-card__photo {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1.08;
|
||||
border-radius: 0;
|
||||
background: linear-gradient(145deg, #2a3544, #111827);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.pattern-card__fav {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pattern-card__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 0 10px 2px;
|
||||
}
|
||||
|
||||
.pattern-card strong {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.pattern-card__colors {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pattern-card__colors span {
|
||||
color: #8a847c;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.swatches {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.swatches i {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.inquiry-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
width: 100%;
|
||||
min-height: 56px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font: inherit;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inquiry-bar small {
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 500;
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.inquiry-bar:disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.calite-detail__error {
|
||||
margin: 8px 0 0;
|
||||
text-align: center;
|
||||
color: #8a847c;
|
||||
}
|
||||
|
||||
.toast {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: calc(var(--nav-height) + 84px);
|
||||
z-index: 20;
|
||||
transform: translateX(-50%);
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
html.dark .calite-detail {
|
||||
background: #0b0f14;
|
||||
color: #eef2f8;
|
||||
}
|
||||
|
||||
html.dark .calite-hero__nav h1,
|
||||
html.dark .icon-btn {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
html.dark .attr {
|
||||
background: transparent;
|
||||
color: #c5d0de;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html.dark .attr:not(:last-child)::after {
|
||||
background: #2a3444;
|
||||
}
|
||||
|
||||
html.dark .physical-cta {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
html.dark .pattern-tab {
|
||||
background: #1a2230;
|
||||
color: #c5d0de;
|
||||
}
|
||||
|
||||
html.dark .pattern-tab.is-on {
|
||||
background: #e8e4df;
|
||||
color: #121821;
|
||||
}
|
||||
|
||||
html.dark .pattern-card {
|
||||
background: #121821;
|
||||
color: #eef2f8;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html.dark .pattern-card__colors span,
|
||||
html.dark .calite-detail__error {
|
||||
color: #9aa8bc;
|
||||
}
|
||||
|
||||
html.dark .inquiry-bar {
|
||||
background: #5eb8ad;
|
||||
color: #0b0f14;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.calite-detail {
|
||||
padding: 0;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.calite-hero {
|
||||
min-height: 380px;
|
||||
height: min(48vh, 460px);
|
||||
}
|
||||
|
||||
.toast {
|
||||
bottom: 96px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user