Initialize the user app and load home banners from the API.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useCatalogStore } from '@/stores/catalog'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
import SearchField from '@/components/ui/SearchField.vue'
|
||||
import EmptyState from '@/components/ui/EmptyState.vue'
|
||||
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
|
||||
import IconCatalog from '@/components/icons/IconCatalog.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const catalogStore = useCatalogStore()
|
||||
const loading = ref(true)
|
||||
|
||||
const companyId = computed(() => route.params.companyId as string)
|
||||
const company = computed(() => catalogStore.getCompany(companyId.value))
|
||||
const search = ref('')
|
||||
|
||||
const catalogs = computed(() => catalogStore.fetchCatalogs(companyId.value, search.value))
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
if (!catalogStore.getCompany(companyId.value)) {
|
||||
await catalogStore.loadCompanies()
|
||||
}
|
||||
await catalogStore.loadCatalogs(companyId.value)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
watch(companyId, load)
|
||||
|
||||
function openCatalog(catalogId: string) {
|
||||
router.push(`/companies/${companyId.value}/catalogs/${catalogId}/products`)
|
||||
}
|
||||
|
||||
function getProductCount(catalogId: string) {
|
||||
const catalog = catalogStore.getCatalog(catalogId)
|
||||
if (catalog?.productCount != null) return catalog.productCount
|
||||
return catalogStore.fetchProducts(catalogId).length
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Mobile -->
|
||||
<div class="page catalog-mobile-page mobile-only">
|
||||
<PageHeader
|
||||
:title="company?.name ?? 'کالیتهها'"
|
||||
subtitle="کالیته مورد نظر را انتخاب کنید"
|
||||
show-back
|
||||
centered
|
||||
@back="router.push('/')"
|
||||
/>
|
||||
|
||||
<SearchField
|
||||
v-model="search"
|
||||
placeholder="جستجو بر اساس کد یا نام کالیته..."
|
||||
class="catalog-page__search"
|
||||
/>
|
||||
|
||||
<div class="page__scroll page__scroll--with-nav catalog-mobile-scroll">
|
||||
<PageSkeleton v-if="loading" variant="cards" :rows="5" />
|
||||
<div v-else-if="catalogs.length > 0" class="catalog-mobile-list">
|
||||
<button
|
||||
v-for="catalog in catalogs"
|
||||
:key="catalog.id"
|
||||
type="button"
|
||||
class="catalog-card"
|
||||
@click="openCatalog(catalog.id)"
|
||||
>
|
||||
<span class="catalog-card__icon"><IconCatalog :size="22" /></span>
|
||||
<div class="catalog-card__info">
|
||||
<h3>کالیته کد {{ catalog.code }}</h3>
|
||||
<p>{{ catalog.name }}</p>
|
||||
</div>
|
||||
<span class="catalog-card__count">{{ getProductCount(catalog.id) }} محصول</span>
|
||||
</button>
|
||||
</div>
|
||||
<EmptyState
|
||||
v-else
|
||||
:variant="search ? 'search' : 'catalogs'"
|
||||
:message="search ? 'با این جستجو کالیتهای پیدا نشد' : 'برای این شرکت هنوز کالیتهای ثبت نشده است'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop -->
|
||||
<div class="desktop-page catalog-desktop-page desktop-only">
|
||||
<header class="desktop-page__head">
|
||||
<h1 class="desktop-page__title">{{ company?.name ?? 'کالیتهها' }}</h1>
|
||||
<p class="desktop-page__subtitle">کالیته مورد نظر را انتخاب کنید</p>
|
||||
</header>
|
||||
|
||||
<SearchField
|
||||
v-model="search"
|
||||
placeholder="جستجو بر اساس کد یا نام کالیته..."
|
||||
class="catalog-page__search"
|
||||
/>
|
||||
|
||||
<div v-if="catalogs.length > 0" class="desktop-grid-3">
|
||||
<button
|
||||
v-for="catalog in catalogs"
|
||||
:key="catalog.id"
|
||||
type="button"
|
||||
class="catalog-card catalog-card--desktop"
|
||||
@click="openCatalog(catalog.id)"
|
||||
>
|
||||
<span class="catalog-card__icon"><IconCatalog :size="24" /></span>
|
||||
<div class="catalog-card__info">
|
||||
<h3>کالیته کد {{ catalog.code }}</h3>
|
||||
<p>{{ catalog.name }}</p>
|
||||
</div>
|
||||
<span class="catalog-card__count">{{ getProductCount(catalog.id) }} محصول</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<EmptyState
|
||||
v-else
|
||||
:variant="search ? 'search' : 'catalogs'"
|
||||
:message="search ? 'با این جستجو کالیتهای پیدا نشد' : 'برای این شرکت هنوز کالیتهای ثبت نشده است'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.catalog-mobile-page {
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 12px 0 0;
|
||||
}
|
||||
|
||||
.catalog-page__search {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.catalog-mobile-scroll {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.catalog-mobile-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.catalog-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-elevated);
|
||||
box-shadow: var(--shadow-sm);
|
||||
color: var(--color-text);
|
||||
text-align: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.catalog-card--desktop {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
min-height: 140px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.catalog-card:hover {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.catalog-card__icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-primary-light);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.catalog-card__info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.catalog-card__info h3 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.catalog-card__info p {
|
||||
margin: 0;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.catalog-card__count {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,211 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
|
||||
import { useCrmStore } from '@/stores/crm'
|
||||
import { useCatalogRequestsStore } from '@/stores/catalogRequests'
|
||||
import type { CatalogProductCard } from '@/types/crm'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const crm = useCrmStore()
|
||||
const requestsStore = useCatalogRequestsStore()
|
||||
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const note = ref('')
|
||||
const products = ref<CatalogProductCard[]>([])
|
||||
const quantities = ref<Record<string, number>>({})
|
||||
|
||||
const preselectId = computed(() => String(route.query.productId ?? ''))
|
||||
|
||||
const selectedItems = computed(() =>
|
||||
products.value
|
||||
.filter((p) => (quantities.value[p.id] ?? 0) > 0)
|
||||
.map((p) => ({
|
||||
productId: p.id,
|
||||
productName: p.name,
|
||||
productCode: p.code,
|
||||
quantity: quantities.value[p.id] ?? 0,
|
||||
})),
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const list = await crm.loadBrowseProducts()
|
||||
products.value = list
|
||||
if (preselectId.value) {
|
||||
quantities.value = { ...quantities.value, [preselectId.value]: 1 }
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'بارگذاری محصولات ناموفق بود.'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
function setQty(id: string, value: number) {
|
||||
quantities.value = {
|
||||
...quantities.value,
|
||||
[id]: Math.max(0, Math.floor(value) || 0),
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
saving.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const result = await requestsStore.create({
|
||||
items: selectedItems.value,
|
||||
note: note.value,
|
||||
})
|
||||
if (!result.success) {
|
||||
error.value = result.message
|
||||
return
|
||||
}
|
||||
router.push('/catalog-requests')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<PageHeader title="درخواست کالیته" show-back centered @back="router.back()" />
|
||||
<div class="page__scroll page__scroll--with-nav body">
|
||||
<p class="hint">محصولات و تعداد اقلام موردنظر را مشخص کنید.</p>
|
||||
<PageSkeleton v-if="loading" variant="form" />
|
||||
<p v-else-if="error" class="error">{{ error }}</p>
|
||||
<template v-else>
|
||||
<article v-for="item in products" :key="item.id" class="row">
|
||||
<div>
|
||||
<strong>{{ item.name }}</strong>
|
||||
<span>کد {{ item.code }}</span>
|
||||
</div>
|
||||
<input
|
||||
class="qty"
|
||||
type="number"
|
||||
min="0"
|
||||
:value="quantities[item.id] ?? 0"
|
||||
@input="setQty(item.id, Number(($event.target as HTMLInputElement).value))"
|
||||
/>
|
||||
</article>
|
||||
<label class="note">
|
||||
توضیحات
|
||||
<textarea v-model="note" rows="3" placeholder="آدرس یا توضیحات اختیاری" />
|
||||
</label>
|
||||
<p class="summary">اقلام انتخابشده: {{ formatNumber(selectedItems.length) }}</p>
|
||||
<button type="button" class="submit" :disabled="saving" @click="submit">
|
||||
{{ saving ? 'در حال ثبت...' : 'ثبت درخواست کالیته' }}
|
||||
</button>
|
||||
<button type="button" class="linkish" @click="router.push('/catalog-requests')">
|
||||
وضعیت درخواستهای قبلی
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.body {
|
||||
padding: 12px var(--page-padding) 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin: 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #b42318;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 12px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.row strong {
|
||||
display: block;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.row span {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.qty {
|
||||
width: 64px;
|
||||
height: 40px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
font: inherit;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.note {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.note textarea {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 12px;
|
||||
padding: 10px 12px;
|
||||
font: inherit;
|
||||
resize: vertical;
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.summary {
|
||||
margin: 4px 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
min-height: 48px;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.submit:disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.linkish {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
import { useCatalogRequestsStore } from '@/stores/catalogRequests'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useCatalogRequestsStore()
|
||||
|
||||
onMounted(() => store.hydrate())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<PageHeader title="وضعیت کالیتهها" show-back centered @back="router.push('/catalogs')" />
|
||||
<div class="page__scroll page__scroll--with-nav body">
|
||||
<button type="button" class="new-btn" @click="router.push('/catalog-request')">
|
||||
درخواست کالیته جدید
|
||||
</button>
|
||||
|
||||
<p v-if="!store.sorted.length" class="empty">هنوز درخواستی ثبت نکردهاید.</p>
|
||||
|
||||
<article v-for="item in store.sorted" :key="item.id" class="card">
|
||||
<div class="card__top">
|
||||
<strong>درخواست {{ item.id.slice(-6) }}</strong>
|
||||
<span class="badge">{{ store.statusLabel(item.status) }}</span>
|
||||
</div>
|
||||
<p>{{ item.createdAt.slice(0, 10) }}</p>
|
||||
<p>{{ formatNumber(item.items.length) }} قلم</p>
|
||||
<ul>
|
||||
<li v-for="line in item.items" :key="line.productId">
|
||||
{{ line.productName }} × {{ formatNumber(line.quantity) }}
|
||||
</li>
|
||||
</ul>
|
||||
<p v-if="item.note" class="note">{{ item.note }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.body {
|
||||
padding: 12px var(--page-padding) 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.new-btn {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
min-height: 48px;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.card__top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
font-size: 0.72rem;
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 6px 0 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.card ul {
|
||||
margin: 8px 0 0;
|
||||
padding-inline-start: 18px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.note {
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,164 @@
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
|
||||
import { useCatalogStore } from '@/stores/catalog'
|
||||
import type { Catalog } from '@/types'
|
||||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const catalogStore = useCatalogStore()
|
||||
const catalogs = ref<Catalog[]>([])
|
||||
const loading = ref(true)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const companies = await catalogStore.loadCompanies()
|
||||
const lists = await Promise.all(companies.map((c) => catalogStore.loadCatalogs(c.id)))
|
||||
catalogs.value = lists.flat()
|
||||
const focusId = String(route.query.id ?? '')
|
||||
if (focusId) {
|
||||
const hit = catalogs.value.find((c) => c.id === focusId)
|
||||
if (hit) {
|
||||
await nextTick()
|
||||
openCatalog(hit)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'بارگذاری کالیتهها ناموفق بود.'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
function openCatalog(item: Catalog) {
|
||||
router.push({
|
||||
path: `/companies/${item.companyId}/catalogs/${item.id}/products`,
|
||||
})
|
||||
}
|
||||
|
||||
function companyName(companyId: string) {
|
||||
return catalogStore.getCompany(companyId)?.name ?? 'برند'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<PageHeader title="کالیتهها" show-back centered @back="router.push('/')" />
|
||||
<div class="page__scroll page__scroll--with-nav catalog-hub">
|
||||
<button type="button" class="hub-card" @click="router.push('/catalog-request')">
|
||||
<strong>درخواست کالیته فیزیکی</strong>
|
||||
<span>ثبت اقلام و پیگیری وضعیت</span>
|
||||
</button>
|
||||
<button type="button" class="hub-card" @click="router.push('/catalog-requests')">
|
||||
<strong>وضعیت درخواستها</strong>
|
||||
<span>پیگیری کالیتههای ثبتشده</span>
|
||||
</button>
|
||||
<button type="button" class="hub-card" @click="router.push('/categories')">
|
||||
<strong>مشاهده دستهبندیها</strong>
|
||||
<span>انتخاب بر اساس کاربرد و فصل</span>
|
||||
</button>
|
||||
<button type="button" class="hub-card" @click="router.push('/companies')">
|
||||
<strong>کالیتههای برندها</strong>
|
||||
<span>مسیر انتخاب شرکت و کالیته</span>
|
||||
</button>
|
||||
|
||||
<PageSkeleton v-if="loading" variant="grid" />
|
||||
<p v-else-if="error">{{ error }}</p>
|
||||
<template v-else>
|
||||
<h2 class="section-title">کالیتههای موجود</h2>
|
||||
<button
|
||||
v-for="item in catalogs"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="calite-item"
|
||||
:class="{ 'calite-item--focus': String(route.query.id ?? '') === item.id }"
|
||||
@click="openCatalog(item)"
|
||||
>
|
||||
<div class="calite-item__text">
|
||||
<strong>{{ item.name }}</strong>
|
||||
<span>کد {{ item.code }}</span>
|
||||
<em>{{ companyName(item.companyId) }}</em>
|
||||
</div>
|
||||
<IconChevronLeft :size="18" color="#9aa3a0" />
|
||||
</button>
|
||||
<p v-if="!catalogs.length" class="empty-hint">کالیتهای یافت نشد.</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.catalog-hub {
|
||||
padding: 12px var(--page-padding) 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 8px 0 0;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.hub-card,
|
||||
.calite-item {
|
||||
border: none;
|
||||
text-align: start;
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.calite-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.calite-item--focus {
|
||||
outline: 2px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.calite-item__text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.calite-item strong {
|
||||
display: block;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.hub-card span,
|
||||
.calite-item span,
|
||||
.calite-item em {
|
||||
display: block;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.5;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
color: var(--color-text-muted);
|
||||
text-align: center;
|
||||
margin: 12px 0 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user