Compare commits

...

2 Commits

Author SHA1 Message Date
Telecom
b79e99246e ux(audit): typography pass
- Action chip now uses monoweight glyphs (+, ×, ↻, ⚙, ∑, ⚿) instead
  of mixed emoji, so all rows share a baseline; chip shrunk to 32px
  with font-semibold and leading-none.
- Removed the actor avatar circle (visual noise); actor name carries
  the primary weight on its own (semibold, gray-900).
- Sentence layout reworked as a single <p> with inline margins
  instead of a flex row: "rop удалил Лида ceb8…". Actor — semibold
  900, verb — 700, entity word — 500, ID — font-mono 11px sky-600.
- Entity labels capitalised ("Лида" / "Пользователя" / "Систему")
  so the sentence reads as natural Russian, not technical jargon.
- Day header demoted from sticky+gradient block to a plain h2 with
  tracking-[0.18em] — keeps rhythm without the muted-bar look.
- Timestamp uses <time> + text-[11px] leading-none; less prominent
  than the message itself.
- Dropped the unused initials() helper after removing the avatar.
2026-05-22 13:52:43 +05:00
Telecom
a92298daf2 fix(audit): add gap between action label, entity word and id
The inline whitespace between the three spans collapsed in some
browsers, producing 'удалиллидаceb8...'. Switched the inner
container to flex + gap-1.5 so the spacing is explicit.
2026-05-22 13:48:24 +05:00

View File

@ -17,9 +17,9 @@ const entries = ref<AuditEntry[]>([]);
const loading = ref(false);
const ENTITY_LABELS: Record<string, string> = {
lead: 'лида',
user: 'пользователя',
system: 'систему',
lead: 'Лида',
user: 'Пользователя',
system: 'Систему',
};
interface ActionMeta {
@ -29,12 +29,12 @@ interface ActionMeta {
}
const ACTIONS: Record<string, ActionMeta> = {
create: { label: 'создал', tone: 'success', icon: '+' },
delete: { label: 'удалил', tone: 'danger', icon: '🗑' },
create: { label: 'создал', tone: 'success', icon: '+' },
delete: { label: 'удалил', tone: 'danger', icon: '×' },
reassign: { label: 'переназначил', tone: 'warning', icon: '↻' },
reset_password: { label: 'сменил пароль', tone: 'warning', icon: '🔑' },
change_role: { label: 'изменил роль', tone: 'warning', icon: '👤' },
recalc: { label: 'пересчитал score', tone: 'neutral', icon: '⟳' },
reset_password: { label: 'сменил пароль', tone: 'warning', icon: '⚿' },
change_role: { label: 'изменил роль', tone: 'warning', icon: '⚙' },
recalc: { label: 'пересчитал score', tone: 'neutral', icon: '∑' },
};
function actionMeta(a: string): ActionMeta {
@ -132,11 +132,6 @@ const grouped = computed(() => {
return groups;
});
function initials(name: string): string {
const parts = name.split(/[\s._-]+/).filter(Boolean).slice(0, 2);
return parts.map((p) => p[0]?.toUpperCase() ?? '').join('') || name[0]?.toUpperCase() || '?';
}
function shortId(id: string): string {
return id.length > 10 ? id.slice(0, 8) + '…' : id;
}
@ -194,26 +189,26 @@ onMounted(load);
>
Событий не найдено.
</div>
<div v-else class="space-y-8">
<div
<div v-else class="space-y-6">
<section
v-for="group in grouped"
:key="group.day"
class="space-y-3"
class="space-y-2"
>
<h2
class="text-[10px] font-semibold uppercase tracking-[0.18em] text-gray-400 px-1"
>
<div class="sticky top-16 z-10 bg-gradient-to-b from-white via-white to-white/0 pb-1">
<div class="text-[11px] font-semibold uppercase tracking-widest text-gray-400">
{{ group.day }}
</div>
</div>
</h2>
<div class="card divide-y divide-gray-100">
<div
v-for="e in group.items"
:key="e.id"
class="flex items-center gap-4 px-4 py-3 hover:bg-gray-50/70 transition"
class="flex items-center gap-3 px-4 py-3 hover:bg-gray-50/70 transition"
>
<div
class="w-9 h-9 rounded-full flex items-center justify-center text-base ring-1 shrink-0"
class="w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold ring-1 shrink-0 leading-none"
:class="[
TONE_CLASSES[actionMeta(e.action).tone].bg,
TONE_CLASSES[actionMeta(e.action).tone].ring,
@ -224,39 +219,37 @@ onMounted(load);
{{ actionMeta(e.action).icon }}
</div>
<div class="flex items-center gap-2 shrink-0">
<div
class="w-7 h-7 rounded-full bg-gray-100 text-gray-700 flex items-center justify-center text-[11px] font-semibold"
:title="e.actor"
<p
class="flex-1 min-w-0 text-sm text-gray-700 leading-snug truncate"
>
{{ initials(e.actor) }}
</div>
<span class="text-sm font-medium text-gray-900">{{ e.actor }}</span>
</div>
<div class="flex-1 min-w-0 text-sm text-gray-700 truncate">
<span>{{ actionMeta(e.action).label }}</span>
<span class="font-semibold text-gray-900">{{ e.actor }}</span>
<span class="mx-1.5">{{ actionMeta(e.action).label }}</span>
<span class="text-gray-500">{{ ENTITY_LABELS[e.entity] ?? e.entity }}</span>
<button
v-if="e.entity === 'lead'"
type="button"
class="font-mono text-sky-600 hover:underline"
class="ml-1.5 font-mono text-xs text-sky-600 hover:underline align-middle"
@click="gotoEntity(e)"
>
{{ shortId(e.entity_id) }}
</button>
<span v-else class="font-mono text-gray-600">{{ e.entity_id }}</span>
</div>
<span
v-else
class="ml-1.5 font-mono text-xs text-gray-500 align-middle"
>
{{ e.entity_id }}
</span>
</p>
<div
class="text-xs text-gray-400 tabular-nums shrink-0"
<time
class="text-[11px] text-gray-400 tabular-nums shrink-0 leading-none"
:title="fmtFullTime(e.created_at)"
>
{{ relativeTime(e.created_at) }}
</time>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</template>