<template>
<div class="reactions-instructions" :data-doc="doc">
<div class="reactions-row-title">{{ title }}</div>
<div class="reactions-row-status">{{ note }}</div>
<GnTextarea
:model-value="modelValue"
:rows="6"
:placeholder="placeholder"
class="reactions-instructions-textarea"
@update:model-value="$emit('update:modelValue', $event)"
/>
<div class="reactions-instructions-actions">
<GnButton
variant="primary"
:disabled="!dirty || saving"
@click="$emit('save')"
>
{{ saveLabel }}
</GnButton>
</div>
<div v-if="versions.length" class="reactions-history">
<div class="reactions-history-title">{{ t('settings.reactionsHistory') }}</div>
<div
v-for="v in versions"
:key="v.created_at + ':' + v.content.slice(0, 24)"
class="reactions-history-item"
>
<span class="reactions-history-meta">
<i
class="ph"
:class="v.edited_by === 'navi' ? 'ph-robot' : 'ph-user'"
></i>
{{ v.edited_by === 'navi' ? 'Navi' : t('common.you') }}
· {{ formatWhen(v.created_at) }}
<template v-if="v.reason"> · {{ v.reason }}</template>
</span>
<div class="reactions-history-text">{{ v.content }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { locale, t } from '@/i18n/index.js'
import { GnButton, GnTextarea } from 'gnexus-ui-kit/vue'
// One instruction document (reaction / dispatcher) rendered the same way:
// same classes, `data-doc` tells the two blocks apart in tests and style hooks.
defineProps({
doc: { type: String, required: true },
title: { type: String, required: true },
note: { type: String, required: true },
placeholder: { type: String, default: '' },
saveLabel: { type: String, required: true },
modelValue: { type: String, default: '' },
versions: { type: Array, default: () => [] },
dirty: { type: Boolean, default: false },
saving: { type: Boolean, default: false },
})
defineEmits(['update:modelValue', 'save'])
function formatWhen(iso) {
const d = new Date(iso)
return d.toLocaleDateString(locale.value, { month: 'short', day: 'numeric' }) +
' ' + d.toLocaleTimeString(locale.value, { hour: '2-digit', minute: '2-digit' })
}
</script>
<style scoped lang="scss">
.reactions-instructions {
padding: 14px 16px;
margin-bottom: 12px;
border: 1px solid var(--gn-border-color-muted);
border-radius: 8px;
}
.reactions-row-title {
font-size: 0.9rem;
color: var(--gn-color-text-medium);
font-weight: 500;
}
.reactions-row-status {
font-size: 0.8rem;
color: var(--gn-color-text-dark);
margin-top: 2px;
max-width: 420px;
}
.reactions-instructions-textarea {
margin-top: 10px;
}
.reactions-instructions-actions {
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
.reactions-history {
margin-top: 14px;
}
.reactions-history-title {
font-size: 0.85rem;
font-weight: 600;
color: var(--gn-color-text-medium);
margin-bottom: 8px;
}
.reactions-history-item {
padding: 8px 10px;
border-left: 2px solid var(--gn-border-color-muted);
& + & {
margin-top: 8px;
}
}
.reactions-history-meta {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.75rem;
color: var(--gn-color-text-dark);
i {
font-size: 12px;
}
}
.reactions-history-text {
margin-top: 4px;
font-size: 0.8rem;
color: var(--gn-color-text-medium);
white-space: pre-wrap;
word-break: break-word;
}
</style>