feat: streamline content channel tabs
All checks were successful
deploy-socialize / image (push) Successful in 51s
deploy-socialize / deploy (push) Successful in 19s

This commit is contained in:
2026-05-09 11:36:50 -04:00
parent ebfa37f8cd
commit 87530bed84
4 changed files with 31 additions and 85 deletions

View File

@@ -283,11 +283,11 @@
}
}
function toggleChannel(channel) {
function selectTargetChannel(channel) {
const existing = form.placements.find(placement => placement.channelId === channel.id);
if (existing) {
removePlacement(existing.id);
activePlacementId.value = existing.id;
return;
}
@@ -298,10 +298,6 @@
return form.placements.some(placement => placement.channelId === channelId);
}
function setActivePlacement(placement) {
activePlacementId.value = placement.id;
}
function addMedia(placement) {
placement.mediaItems.push(blankMedia());
}
@@ -1104,7 +1100,7 @@
</div>
<div class="preview-editor">
<aside class="target-rail">
<div class="target-tabs" role="tablist" aria-label="Target channels">
<template v-if="groupedChannels.length">
<div
v-for="group in groupedChannels"
@@ -1122,7 +1118,11 @@
active: activePlacement?.channelId === channel.id,
}"
type="button"
@click="toggleChannel(channel)"
role="tab"
:aria-selected="activePlacement?.channelId === channel.id"
:aria-label="channel.name"
:title="channel.name"
@click="selectTargetChannel(channel)"
>
<v-icon
class="target-check"
@@ -1132,10 +1132,6 @@
class="target-network"
:icon="channelIcon(channel.network)"
/>
<span>
<strong>{{ channel.name }}</strong>
<small>{{ channel.handle || channel.network }}</small>
</span>
</v-btn>
</div>
</template>
@@ -1146,26 +1142,9 @@
>
Add workspace channels before choosing publication targets.
</div>
</aside>
</div>
<div class="preview-stage">
<div
v-if="selectedPlacements.length"
class="selected-preview-tabs"
>
<v-btn variant="text" :ripple="false"
v-for="placement in selectedPlacements"
:key="placement.id"
class="selected-preview-tab"
:class="{ active: activePlacement?.id === placement.id }"
type="button"
@click="setActivePlacement(placement)"
>
<v-icon :icon="channelIcon(placement.network)" />
<span>{{ placement.channelName || placement.network }}</span>
</v-btn>
</div>
<article
v-if="activePlacement"
class="social-preview-card"
@@ -1827,26 +1806,26 @@
}
.preview-editor {
@apply grid gap-4 lg:grid-cols-[18rem_minmax(0,1fr)];
@apply grid gap-4 lg:grid-cols-[6rem_minmax(0,1fr)];
}
.target-rail {
@apply flex min-w-0 flex-col gap-4 rounded-[1rem] border p-3;
.target-tabs {
@apply flex min-w-0 flex-col gap-4 rounded-[1rem] border p-2;
background: var(--app-color-on-primary);
border-color: var(--app-border-subtle);
}
.target-group {
@apply flex flex-col gap-2;
@apply flex flex-col items-center gap-2;
}
.target-group > span {
@apply px-2 text-xs font-bold uppercase tracking-[0.16em];
@apply text-[0.62rem] font-bold uppercase tracking-[0.12em];
color: var(--app-text-muted);
}
.target-channel {
@apply grid min-h-11 w-full grid-cols-[1rem_1.75rem_minmax(0,1fr)] items-center gap-2 rounded-[0.75rem] border px-2.5 py-1.5 text-left transition;
@apply relative grid h-10 w-10 place-items-center rounded-[0.75rem] border p-0 transition;
background: var(--app-control-subtle);
border-color: transparent;
color: var(--app-color-on-surface);
@@ -1864,7 +1843,7 @@
}
.target-check {
@apply text-lg;
@apply absolute -right-1 -top-1 text-base;
color: var(--app-color-on-tertiary);
}
@@ -1884,53 +1863,10 @@
color: var(--app-color-on-primary);
}
.target-channel span {
@apply flex min-w-0 flex-col gap-0.5;
}
.target-channel strong,
.target-channel small {
@apply block truncate;
}
.target-channel strong {
@apply text-xs font-bold;
}
.target-channel small {
@apply text-[0.68rem];
color: var(--app-text-muted);
}
.target-channel.active small {
color: rgba(255, 255, 255, 0.72);
}
.preview-stage {
@apply flex min-w-0 flex-col gap-3;
}
.selected-preview-tabs {
@apply flex gap-2 overflow-x-auto pb-1;
}
.selected-preview-tab {
@apply inline-flex min-h-8 max-w-36 shrink-0 items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-bold transition;
background: var(--app-color-on-primary);
border-color: var(--app-border-subtle);
color: var(--app-color-on-surface);
}
.selected-preview-tab span {
@apply truncate;
}
.selected-preview-tab.active {
background: var(--app-color-on-surface);
border-color: var(--app-color-on-surface);
color: var(--app-color-on-primary);
}
.social-preview-card {
@apply flex min-h-[28rem] flex-col gap-4 rounded-[1.25rem] border p-4 shadow-sm;
background: var(--app-color-on-primary);

View File

@@ -97,6 +97,17 @@
}
switch (route.name) {
case 'workspace-dashboard':
case 'content-items':
case 'content-item-detail':
return authStore.isManager || authStore.isProvider
? [{
key: 'create-content',
label: t('contentItems.newItem'),
icon: mdiPlus,
route: { name: 'content-item-create' },
}]
: [];
case 'campaigns':
return [{
key: 'create-campaign',

View File

@@ -57,7 +57,6 @@
const primaryLinks = [
{ to: '/app/dashboard', labelKey: 'nav.overview', icon: mdiHomeOutline },
{ to: { name: 'content-item-create' }, key: 'create-content', labelKey: 'contentItems.newItem', icon: mdiPlus },
{ to: '/app/media-library', labelKey: 'nav.mediaLibrary', icon: mdiImageMultipleOutline },
{ to: '/app/developer/release-notes', labelKey: 'nav.releaseNotes', icon: mdiSourceCommit, roles: ['developer'], badge: 'commits' },
];