|
|
|
@@ -1,7 +1,12 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import {
|
|
|
|
|
ArrowDownOutlined,
|
|
|
|
|
ArrowUpOutlined,
|
|
|
|
|
CheckOutlined,
|
|
|
|
|
CloseOutlined,
|
|
|
|
|
DeleteOutlined,
|
|
|
|
|
EditOutlined,
|
|
|
|
|
HolderOutlined,
|
|
|
|
|
PlusOutlined,
|
|
|
|
|
ThunderboltOutlined,
|
|
|
|
|
} from '@ant-design/icons-vue'
|
|
|
|
@@ -41,6 +46,10 @@ const questionEditOpen = ref(false)
|
|
|
|
|
const editingQuestionId = ref<number | null>(null)
|
|
|
|
|
const competitorModalOpen = ref(false)
|
|
|
|
|
const editingCompetitorId = ref<number | null>(null)
|
|
|
|
|
const sortMode = ref(false)
|
|
|
|
|
const draftBrandIds = ref<number[]>([])
|
|
|
|
|
const draggedBrandId = ref<number | null>(null)
|
|
|
|
|
const dragOverBrandId = ref<number | null>(null)
|
|
|
|
|
|
|
|
|
|
const brandForm = reactive({
|
|
|
|
|
name: '',
|
|
|
|
@@ -106,6 +115,25 @@ const selectedBrand = computed(
|
|
|
|
|
() => brandListQuery.data.value?.find((item) => item.id === selectedBrandId.value) ?? null,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const brandList = computed(() => brandListQuery.data.value ?? [])
|
|
|
|
|
const orderedBrands = computed(() => {
|
|
|
|
|
if (!sortMode.value) {
|
|
|
|
|
return brandList.value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const brandById = new Map(brandList.value.map((brand) => [brand.id, brand]))
|
|
|
|
|
const ordered = draftBrandIds.value
|
|
|
|
|
.map((brandId) => brandById.get(brandId))
|
|
|
|
|
.filter((brand): brand is Brand => Boolean(brand))
|
|
|
|
|
const orderedIds = new Set(ordered.map((brand) => brand.id))
|
|
|
|
|
const appended = brandList.value.filter((brand) => !orderedIds.has(brand.id))
|
|
|
|
|
return [...ordered, ...appended]
|
|
|
|
|
})
|
|
|
|
|
const canSortBrands = computed(() => brandList.value.length > 1)
|
|
|
|
|
const hasBrandOrderChanges = computed(
|
|
|
|
|
() => !isSameBrandOrder(draftBrandIds.value, brandList.value.map((brand) => brand.id)),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const currentQuestions = computed(() => questionsQuery.data.value?.items ?? [])
|
|
|
|
|
const questionTotal = computed(() => questionsQuery.data.value?.total ?? 0)
|
|
|
|
|
|
|
|
|
@@ -195,6 +223,19 @@ const brandMutations = {
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const brandOrderMutation = useMutation({
|
|
|
|
|
mutationFn: (brandIds: number[]) => brandsApi.reorder({ brand_ids: brandIds }),
|
|
|
|
|
onSuccess: async (brands) => {
|
|
|
|
|
message.success(t('brands.messages.reorderBrand'))
|
|
|
|
|
queryClient.setQueryData(['brands', 'list'], brands)
|
|
|
|
|
companyStore.setBrands(brands)
|
|
|
|
|
sortMode.value = false
|
|
|
|
|
resetBrandDragState()
|
|
|
|
|
await queryClient.invalidateQueries({ queryKey: ['brands'] })
|
|
|
|
|
},
|
|
|
|
|
onError: (error) => message.error(formatError(error)),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const questionMutations = {
|
|
|
|
|
createSingle: useMutation({
|
|
|
|
|
mutationFn: () =>
|
|
|
|
@@ -322,6 +363,16 @@ watch(
|
|
|
|
|
{ immediate: true },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => brandList.value.map((brand) => brand.id).join(','),
|
|
|
|
|
() => {
|
|
|
|
|
if (!sortMode.value) {
|
|
|
|
|
draftBrandIds.value = currentBrandIds()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch([questionTotal, questionPageSize], ([total, pageSize]) => {
|
|
|
|
|
const maxPage = Math.max(1, Math.ceil(total / pageSize))
|
|
|
|
|
if (questionPage.value > maxPage) {
|
|
|
|
@@ -344,6 +395,110 @@ function stringifyProductLines(value: unknown): string {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function currentBrandIds(): number[] {
|
|
|
|
|
return brandList.value.map((brand) => brand.id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isSameBrandOrder(left: number[], right: number[]): boolean {
|
|
|
|
|
if (left.length !== right.length) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return left.every((brandId, index) => brandId === right[index])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resetBrandDragState(): void {
|
|
|
|
|
draggedBrandId.value = null
|
|
|
|
|
dragOverBrandId.value = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function startSortMode(): void {
|
|
|
|
|
if (!canSortBrands.value) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
draftBrandIds.value = currentBrandIds()
|
|
|
|
|
sortMode.value = true
|
|
|
|
|
resetBrandDragState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cancelSortMode(): void {
|
|
|
|
|
draftBrandIds.value = currentBrandIds()
|
|
|
|
|
sortMode.value = false
|
|
|
|
|
resetBrandDragState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveBrandOrder(): Promise<void> {
|
|
|
|
|
const brandIds = orderedBrands.value.map((brand) => brand.id)
|
|
|
|
|
if (!isSameBrandOrder(brandIds, currentBrandIds())) {
|
|
|
|
|
await brandOrderMutation.mutateAsync(brandIds)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
cancelSortMode()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function moveDraftBrand(brandId: number, direction: -1 | 1): void {
|
|
|
|
|
const nextIds = [...draftBrandIds.value]
|
|
|
|
|
const currentIndex = nextIds.indexOf(brandId)
|
|
|
|
|
const nextIndex = currentIndex + direction
|
|
|
|
|
if (currentIndex < 0 || nextIndex < 0 || nextIndex >= nextIds.length) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const [movedId] = nextIds.splice(currentIndex, 1)
|
|
|
|
|
nextIds.splice(nextIndex, 0, movedId)
|
|
|
|
|
draftBrandIds.value = nextIds
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function moveDraftBrandToTarget(sourceBrandId: number, targetBrandId: number): void {
|
|
|
|
|
if (sourceBrandId === targetBrandId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const nextIds = [...draftBrandIds.value]
|
|
|
|
|
const sourceIndex = nextIds.indexOf(sourceBrandId)
|
|
|
|
|
const targetIndex = nextIds.indexOf(targetBrandId)
|
|
|
|
|
if (sourceIndex < 0 || targetIndex < 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const [movedId] = nextIds.splice(sourceIndex, 1)
|
|
|
|
|
nextIds.splice(targetIndex, 0, movedId)
|
|
|
|
|
draftBrandIds.value = nextIds
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleBrandCardClick(brandId: number): void {
|
|
|
|
|
if (sortMode.value) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
selectBrand(brandId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleBrandDragStart(brandId: number, event: DragEvent): void {
|
|
|
|
|
draggedBrandId.value = brandId
|
|
|
|
|
dragOverBrandId.value = null
|
|
|
|
|
event.dataTransfer?.setData('text/plain', String(brandId))
|
|
|
|
|
if (event.dataTransfer) {
|
|
|
|
|
event.dataTransfer.effectAllowed = 'move'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleBrandDragOver(brandId: number, event: DragEvent): void {
|
|
|
|
|
if (!sortMode.value || draggedBrandId.value === null || draggedBrandId.value === brandId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
dragOverBrandId.value = brandId
|
|
|
|
|
if (event.dataTransfer) {
|
|
|
|
|
event.dataTransfer.dropEffect = 'move'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleBrandDrop(brandId: number, event: DragEvent): void {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
if (!sortMode.value || draggedBrandId.value === null) {
|
|
|
|
|
resetBrandDragState()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
moveDraftBrandToTarget(draggedBrandId.value, brandId)
|
|
|
|
|
resetBrandDragState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openBrandModal(brand?: Brand): void {
|
|
|
|
|
if (!brand && brandLimitReached.value) {
|
|
|
|
|
message.warning(
|
|
|
|
@@ -490,23 +645,98 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="brand-rail">
|
|
|
|
|
<div class="brand-rail__toolbar">
|
|
|
|
|
<div>
|
|
|
|
|
<p class="eyebrow">{{ t('brands.order.eyebrow') }}</p>
|
|
|
|
|
<h3>{{ t('brands.order.title') }}</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="brand-rail__actions">
|
|
|
|
|
<template v-if="sortMode">
|
|
|
|
|
<a-button :disabled="brandOrderMutation.isPending.value" @click="cancelSortMode">
|
|
|
|
|
<template #icon><CloseOutlined /></template>
|
|
|
|
|
{{ t('common.cancel') }}
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button
|
|
|
|
|
type="primary"
|
|
|
|
|
:disabled="!hasBrandOrderChanges"
|
|
|
|
|
:loading="brandOrderMutation.isPending.value"
|
|
|
|
|
@click="saveBrandOrder"
|
|
|
|
|
>
|
|
|
|
|
<template #icon><CheckOutlined /></template>
|
|
|
|
|
{{ t('brands.order.done') }}
|
|
|
|
|
</a-button>
|
|
|
|
|
</template>
|
|
|
|
|
<a-button v-else :disabled="!canSortBrands" @click="startSortMode">
|
|
|
|
|
<template #icon><HolderOutlined /></template>
|
|
|
|
|
{{ t('brands.order.adjust') }}
|
|
|
|
|
</a-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="brandListQuery.isPending.value" class="brand-grid">
|
|
|
|
|
<a-skeleton v-for="item in 3" :key="item" active />
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else-if="brandListQuery.data.value?.length" class="brand-grid">
|
|
|
|
|
<article
|
|
|
|
|
v-for="brand in brandListQuery.data.value"
|
|
|
|
|
v-for="(brand, index) in orderedBrands"
|
|
|
|
|
:key="brand.id"
|
|
|
|
|
class="brand-card"
|
|
|
|
|
:class="{ 'brand-card--active': brand.id === selectedBrandId }"
|
|
|
|
|
@click="selectBrand(brand.id)"
|
|
|
|
|
:class="{
|
|
|
|
|
'brand-card--active': brand.id === selectedBrandId,
|
|
|
|
|
'brand-card--sorting': sortMode,
|
|
|
|
|
'brand-card--dragging': draggedBrandId === brand.id,
|
|
|
|
|
'brand-card--drop-target': dragOverBrandId === brand.id,
|
|
|
|
|
}"
|
|
|
|
|
@click="handleBrandCardClick(brand.id)"
|
|
|
|
|
@dragover="handleBrandDragOver(brand.id, $event)"
|
|
|
|
|
@drop="handleBrandDrop(brand.id, $event)"
|
|
|
|
|
@dragend="resetBrandDragState"
|
|
|
|
|
>
|
|
|
|
|
<div class="brand-card__top">
|
|
|
|
|
<div>
|
|
|
|
|
<h3>{{ brand.name }}</h3>
|
|
|
|
|
<p>{{ brand.description || t('brands.empty.brandDescription') }}</p>
|
|
|
|
|
<div class="brand-card__identity">
|
|
|
|
|
<div v-if="sortMode" class="brand-card__order">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class="brand-card__drag-handle"
|
|
|
|
|
draggable="true"
|
|
|
|
|
:aria-label="t('brands.order.dragHandle')"
|
|
|
|
|
@click.stop
|
|
|
|
|
@dragstart.stop="handleBrandDragStart(brand.id, $event)"
|
|
|
|
|
@dragend="resetBrandDragState"
|
|
|
|
|
>
|
|
|
|
|
<HolderOutlined />
|
|
|
|
|
</button>
|
|
|
|
|
<span>{{ index + 1 }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="brand-card__copy">
|
|
|
|
|
<h3>{{ brand.name }}</h3>
|
|
|
|
|
<p>{{ brand.description || t('brands.empty.brandDescription') }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="brand-card__actions">
|
|
|
|
|
<div v-if="sortMode" class="brand-card__sort-actions">
|
|
|
|
|
<a-tooltip :title="t('brands.order.moveUp')">
|
|
|
|
|
<a-button
|
|
|
|
|
type="text"
|
|
|
|
|
shape="circle"
|
|
|
|
|
size="small"
|
|
|
|
|
:disabled="index === 0"
|
|
|
|
|
@click.stop="moveDraftBrand(brand.id, -1)"
|
|
|
|
|
>
|
|
|
|
|
<ArrowUpOutlined />
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-tooltip :title="t('brands.order.moveDown')">
|
|
|
|
|
<a-button
|
|
|
|
|
type="text"
|
|
|
|
|
shape="circle"
|
|
|
|
|
size="small"
|
|
|
|
|
:disabled="index === orderedBrands.length - 1"
|
|
|
|
|
@click.stop="moveDraftBrand(brand.id, 1)"
|
|
|
|
|
>
|
|
|
|
|
<ArrowDownOutlined />
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else class="brand-card__actions">
|
|
|
|
|
<a-tooltip :title="t('common.edit')">
|
|
|
|
|
<a-button
|
|
|
|
|
type="text"
|
|
|
|
@@ -533,7 +763,12 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</article>
|
|
|
|
|
<button type="button" class="brand-card brand-card--add" @click="openBrandModal()">
|
|
|
|
|
<button
|
|
|
|
|
v-if="!sortMode"
|
|
|
|
|
type="button"
|
|
|
|
|
class="brand-card brand-card--add"
|
|
|
|
|
@click="openBrandModal()"
|
|
|
|
|
>
|
|
|
|
|
<PlusOutlined />
|
|
|
|
|
<span>{{ t('brands.actions.addBrand') }}</span>
|
|
|
|
|
</button>
|
|
|
|
@@ -808,6 +1043,28 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-rail__toolbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-rail__toolbar h3 {
|
|
|
|
|
margin: 4px 0 0;
|
|
|
|
|
color: #111827;
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: 740;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-rail__actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
|
@@ -843,6 +1100,23 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(37, 99, 235, 0.12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card--sorting {
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card--dragging {
|
|
|
|
|
opacity: 0.46;
|
|
|
|
|
transform: scale(0.985);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card--drop-target {
|
|
|
|
|
border-color: #1677ff;
|
|
|
|
|
background: #f0f7ff;
|
|
|
|
|
box-shadow:
|
|
|
|
|
inset 0 0 0 1px rgba(22, 119, 255, 0.18),
|
|
|
|
|
0 14px 28px rgba(22, 119, 255, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card--add {
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
@@ -860,6 +1134,80 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
gap: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__identity {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__copy {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__order {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
min-width: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__order span {
|
|
|
|
|
min-width: 24px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
color: #2563eb;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 760;
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__drag-handle {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 0;
|
|
|
|
|
color: #64748b;
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
border: 1px solid #e2e8f0;
|
|
|
|
|
border-radius: 7px;
|
|
|
|
|
cursor: grab;
|
|
|
|
|
transition:
|
|
|
|
|
color 0.18s ease,
|
|
|
|
|
border-color 0.18s ease,
|
|
|
|
|
background 0.18s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__drag-handle :deep(.anticon) {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__drag-handle :deep(svg) {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__drag-handle:hover {
|
|
|
|
|
color: #1677ff;
|
|
|
|
|
background: #eef6ff;
|
|
|
|
|
border-color: #93c5fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__drag-handle:active {
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-detail__actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
@@ -892,6 +1240,13 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
transition: opacity 0.18s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card__sort-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-card:hover .brand-card__actions,
|
|
|
|
|
.brand-card:focus-within .brand-card__actions {
|
|
|
|
|
opacity: 1;
|
|
|
|
@@ -970,11 +1325,16 @@ async function invalidateBrandQueries(): Promise<void> {
|
|
|
|
|
@media (max-width: 760px) {
|
|
|
|
|
.brand-page-head,
|
|
|
|
|
.brand-guide,
|
|
|
|
|
.brand-rail__toolbar,
|
|
|
|
|
.brand-detail__head {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-rail__actions {
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-detail__actions {
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
|
|
|
|