diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 3c9f826..61d3838 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -258,12 +258,21 @@ { key: "status", label: "Status" }, { key: "actions", label: "" } ]; +// created_at values arrive as UTC ISO strings; render the server timestamp +// as local "YYYY-MM-DD HH:mm" — the kit timeline draws it in a narrow +// .timeline-time, where a compact fixed format reads better than Locale +const formatTimestamp = (iso) => { + const date = new Date(iso); + if (Number.isNaN(date.getTime())) return iso; + const pad = (n) => String(n).padStart(2, "0"); + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`; +}; const versionItems = computed(() => versions.value.map((version) => ({ key: String(version.id), title: `Version ${version.version_number}`, - time: version.created_at, - text: `${version.fields.length} fields`, + time: formatTimestamp(version.created_at), + text: `${version.fields.length} ${version.fields.length === 1 ? "field" : "fields"}`, icon: "ph-clock-counter-clockwise", raw: version })) @@ -271,7 +280,7 @@ const auditRows = computed(() => auditEvents.value.map((event) => ({ id: event.id, - time: event.created_at, + time: formatTimestamp(event.created_at), action: event.action, channel: event.channel, ip: event.ip_address || "—", @@ -1193,6 +1202,9 @@ :visible-field-keys="revealedVisibleFields" @toggle-field-visibility="toggleRevealedField" /> + + {{ item.text }} — - + + diff --git a/frontend/src/styles.css b/frontend/src/styles.css index f31deec..948f4fc 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -48,21 +48,9 @@ } } -.modal.a-show .modal-backdrop { - animation: gnexus-creds-modal-backdrop 0.18s ease-out both; -} - -.modal.a-show .modal-dialog { - animation: gnexus-creds-modal-dialog 0.22s ease-out both; -} - -.modal.a-hide .modal-backdrop { - animation: gnexus-creds-modal-backdrop-out 0.22s ease-in both; -} - -.modal.a-hide .modal-dialog { - animation: gnexus-creds-modal-dialog-out 0.28s ease-in both; -} +/* Kit 1.0 owns modal + toast transitions itself, via .a-show/.a-hide state + classes and CSS transitions (dialog: margin-top 200px -> 0 + opacity; + toast: bottom -100px -> 15px). No app-side keyframes here. */ .secrets-page { display: grid; @@ -233,7 +221,7 @@ .create-modal-body { display: grid; - gap: 12px; + gap: 15px; width: min(900px, calc(100vw - 48px)); max-width: 100%; } @@ -278,7 +266,12 @@ .modal-section { display: grid; - gap: 10px; + gap: 15px; +} + +.modal-section + .modal-section { + padding-top: 15px; + border-top: 2px solid rgba(192, 202, 245, 0.08); } .create-fields { @@ -370,6 +363,20 @@ } } +/* Vertical rail linking the markers (kit 1.0 ships no connector). The + marker is a 40px box in the first grid column, so its center sits at + 19px; the rail stops 18px short of the next marker, matching the + .timeline-content bottom padding. */ +.timeline-item:not(:last-child)::before { + content: ""; + position: absolute; + left: 19px; + top: 44px; + bottom: 0; + width: 2px; + background: rgba(192, 202, 245, 0.24); +} + /* Page header compact fixes */ .page-header.page-header-compact { overflow: initial; @@ -471,62 +478,4 @@ } } -@keyframes gnexus-creds-modal-backdrop { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -@keyframes gnexus-creds-modal-dialog { - from { - opacity: 0; - transform: translateY(-18px); - } - - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes gnexus-creds-modal-backdrop-out { - from { - opacity: 1; - } - - to { - opacity: 0; - } -} - -@keyframes gnexus-creds-modal-dialog-out { - from { - opacity: 1; - transform: translateY(0); - } - - to { - opacity: 0; - transform: translateY(-18px); - } -} - -@keyframes gnexus-creds-toast-in { - from { - bottom: -100px; - opacity: 0; - } - - to { - bottom: 15px; - opacity: 1; - } -} - -.toast.a-show { - animation: gnexus-creds-toast-in 0.28s ease both; -} +/* Toast + modal entry/exit animations are kit-owned (see note above). */