mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-19 16:55:02 +02:00
fix(editor): Fix the catalog card chrome
Three problems, one of them a real bug. The icon buttons carried `type="tertiary"`, but `N8nIconButton` takes `variant` — `type` fell through to the DOM as an HTML attribute and the buttons rendered in the default solid style. All three actions therefore looked equally primary, and a disabled schedule button looked no different from an enabled one. They are `variant="ghost"` now, so only Run reads as the primary action. The footer put the badges and the buttons on one row. A card is ~280px and the badges alone can fill it, so the buttons were pushed past the card's edge — and because a grid item defaults to `min-width: auto`, a nowrap badge wider than its track dragged the whole card out of its column. The two now stack, and the card may shrink. The next-run badge showed seconds, which are always `00` for a schedule that fires on the minute. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f0b71fd1f3
commit
8d2e1ad7c2
|
|
@ -161,10 +161,14 @@ const scheduleAction = (entry: CatalogEntry) => {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* To the minute, not the second. A schedule fires on the minute, so the seconds
|
||||
* are always `00` — three characters of noise on a badge that has to fit a card.
|
||||
*/
|
||||
const displayTime = (value: string | null) => {
|
||||
if (!value) return '';
|
||||
const { date, time } = convertToDisplayDate(value);
|
||||
return `${date} ${time}`;
|
||||
return `${date} ${time.slice(0, 'HH:MM'.length)}`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -291,62 +295,69 @@ const removeSchedule = async (subscription: CatalogSubscription) => {
|
|||
</N8nText>
|
||||
|
||||
<template #footer>
|
||||
<div :class="$style.meta">
|
||||
<N8nBadge theme="tertiary">
|
||||
<N8nIcon
|
||||
:icon="entry.trigger === 'manual-trigger' ? 'mouse-pointer' : 'workflow'"
|
||||
size="xsmall"
|
||||
:class="$style.badgeIcon"
|
||||
/>
|
||||
{{ triggerLabel(entry) }}
|
||||
</N8nBadge>
|
||||
<N8nBadge theme="default">{{ inputsLabel(entry) }}</N8nBadge>
|
||||
<N8nBadge
|
||||
v-if="ownSchedule(entry).state !== 'none'"
|
||||
:theme="ownSchedule(entry).state === 'scheduled' ? 'success' : 'warning'"
|
||||
data-test-id="catalog-own-schedule"
|
||||
>
|
||||
<N8nIcon icon="calendar" size="xsmall" :class="$style.badgeIcon" />
|
||||
{{ ownScheduleLabel(entry) }}
|
||||
</N8nBadge>
|
||||
</div>
|
||||
|
||||
<div :class="$style.actions">
|
||||
<N8nTooltip :content="i18n.baseText('catalog.card.history')">
|
||||
<N8nIconButton
|
||||
icon="history"
|
||||
type="tertiary"
|
||||
size="small"
|
||||
:aria-label="i18n.baseText('catalog.card.history')"
|
||||
data-test-id="catalog-history-button"
|
||||
@click="openExecutions(entry)"
|
||||
/>
|
||||
</N8nTooltip>
|
||||
<N8nTooltip :content="scheduleAction(entry).label">
|
||||
<!-- Wrapped so the tooltip still fires when the button is disabled. -->
|
||||
<span>
|
||||
<N8nIconButton
|
||||
icon="calendar"
|
||||
type="tertiary"
|
||||
size="small"
|
||||
:disabled="scheduleAction(entry).disabled"
|
||||
:aria-label="scheduleAction(entry).label"
|
||||
data-test-id="catalog-schedule-button"
|
||||
@click="
|
||||
scheduling = { entry, subscription: scheduleAction(entry).subscription }
|
||||
"
|
||||
<!--
|
||||
Stacked rather than side by side: a card is ~280px, and the badges
|
||||
alone can fill that, so competing for one row is what pushed the
|
||||
buttons off the card.
|
||||
-->
|
||||
<div :class="$style.footer">
|
||||
<div :class="$style.meta">
|
||||
<N8nBadge theme="tertiary">
|
||||
<N8nIcon
|
||||
:icon="entry.trigger === 'manual-trigger' ? 'mouse-pointer' : 'workflow'"
|
||||
size="xsmall"
|
||||
:class="$style.badgeIcon"
|
||||
/>
|
||||
</span>
|
||||
</N8nTooltip>
|
||||
<N8nButton
|
||||
size="small"
|
||||
icon="play"
|
||||
:label="i18n.baseText('catalog.run')"
|
||||
:loading="runningId === entry.id"
|
||||
:disabled="runningId !== null && runningId !== entry.id"
|
||||
data-test-id="catalog-run-button"
|
||||
@click="onRunClick(entry)"
|
||||
/>
|
||||
{{ triggerLabel(entry) }}
|
||||
</N8nBadge>
|
||||
<N8nBadge theme="default">{{ inputsLabel(entry) }}</N8nBadge>
|
||||
<N8nBadge
|
||||
v-if="ownSchedule(entry).state !== 'none'"
|
||||
:theme="ownSchedule(entry).state === 'scheduled' ? 'success' : 'warning'"
|
||||
data-test-id="catalog-own-schedule"
|
||||
>
|
||||
<N8nIcon icon="calendar" size="xsmall" :class="$style.badgeIcon" />
|
||||
{{ ownScheduleLabel(entry) }}
|
||||
</N8nBadge>
|
||||
</div>
|
||||
|
||||
<div :class="$style.actions">
|
||||
<N8nTooltip :content="i18n.baseText('catalog.card.history')">
|
||||
<N8nIconButton
|
||||
icon="history"
|
||||
variant="ghost"
|
||||
size="small"
|
||||
:aria-label="i18n.baseText('catalog.card.history')"
|
||||
data-test-id="catalog-history-button"
|
||||
@click="openExecutions(entry)"
|
||||
/>
|
||||
</N8nTooltip>
|
||||
<N8nTooltip :content="scheduleAction(entry).label">
|
||||
<!-- Wrapped so the tooltip still fires when the button is disabled. -->
|
||||
<span>
|
||||
<N8nIconButton
|
||||
icon="calendar"
|
||||
variant="ghost"
|
||||
size="small"
|
||||
:disabled="scheduleAction(entry).disabled"
|
||||
:aria-label="scheduleAction(entry).label"
|
||||
data-test-id="catalog-schedule-button"
|
||||
@click="
|
||||
scheduling = { entry, subscription: scheduleAction(entry).subscription }
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
</N8nTooltip>
|
||||
<N8nButton
|
||||
size="small"
|
||||
icon="play"
|
||||
:label="i18n.baseText('catalog.run')"
|
||||
:loading="runningId === entry.id"
|
||||
:disabled="runningId !== null && runningId !== entry.id"
|
||||
data-test-id="catalog-run-button"
|
||||
@click="onRunClick(entry)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</N8nCard>
|
||||
|
|
@ -395,7 +406,7 @@ const removeSchedule = async (subscription: CatalogSubscription) => {
|
|||
<N8nTooltip :content="i18n.baseText('catalog.schedules.edit')">
|
||||
<N8nIconButton
|
||||
icon="pen"
|
||||
type="tertiary"
|
||||
variant="ghost"
|
||||
size="small"
|
||||
:aria-label="i18n.baseText('catalog.schedules.edit')"
|
||||
data-test-id="catalog-subscription-edit"
|
||||
|
|
@ -405,7 +416,7 @@ const removeSchedule = async (subscription: CatalogSubscription) => {
|
|||
<N8nTooltip :content="i18n.baseText('catalog.schedules.remove')">
|
||||
<N8nIconButton
|
||||
icon="trash-2"
|
||||
type="tertiary"
|
||||
variant="ghost"
|
||||
size="small"
|
||||
:aria-label="i18n.baseText('catalog.schedules.remove')"
|
||||
data-test-id="catalog-subscription-remove"
|
||||
|
|
@ -465,6 +476,17 @@ const removeSchedule = async (subscription: CatalogSubscription) => {
|
|||
|
||||
align-items: stretch;
|
||||
gap: var(--spacing--2xs);
|
||||
// A grid item defaults to min-width: auto, so a nowrap badge wider than the
|
||||
// track pushes the card past its column instead of letting the text clip.
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing--2xs);
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
|
|
@ -499,6 +521,9 @@ const removeSchedule = async (subscription: CatalogSubscription) => {
|
|||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// Right-aligned so the primary action sits where the eye lands last, and the
|
||||
// two icon buttons read as secondary to it.
|
||||
justify-content: flex-end;
|
||||
gap: var(--spacing--4xs);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user