fix: thread logged_at through message stack; Esc handler and localNow fixes

- scripts/messaging.py: add logged_at param to create_message; use provided value or fall back to _now_utc()
- dev-api.py: add logged_at: Optional[str] = None to MessageCreateBody
- web/src/stores/messaging.ts: remove logged_at from Omit, add as optional intersection so callers can pass it through
- web/src/components/MessageLogModal.vue: pass logged_at in handleSubmit payload; move @keydown.esc from backdrop to modal-dialog (which holds focus); compute localNow fresh inside watch so it reflects actual open time
This commit is contained in:
pyr0ball 2026-04-20 12:55:41 -07:00
parent 8df3297ab6
commit b77ec81cc6
4 changed files with 14 additions and 12 deletions

View file

@ -4192,6 +4192,7 @@ class MessageCreateBody(BaseModel):
from_addr: Optional[str] = None from_addr: Optional[str] = None
to_addr: Optional[str] = None to_addr: Optional[str] = None
template_id: Optional[int] = None template_id: Optional[int] = None
logged_at: Optional[str] = None
class TemplateCreateBody(BaseModel): class TemplateCreateBody(BaseModel):

View file

@ -47,6 +47,7 @@ def create_message(
from_addr: Optional[str], from_addr: Optional[str],
to_addr: Optional[str], to_addr: Optional[str],
template_id: Optional[int], template_id: Optional[int],
logged_at: Optional[str] = None,
) -> dict: ) -> dict:
"""Insert a new message row and return it as a dict.""" """Insert a new message row and return it as a dict."""
con = _connect(db_path) con = _connect(db_path)
@ -55,12 +56,12 @@ def create_message(
""" """
INSERT INTO messages INSERT INTO messages
(job_id, job_contact_id, type, direction, subject, body, (job_id, job_contact_id, type, direction, subject, body,
from_addr, to_addr, template_id) from_addr, to_addr, logged_at, template_id)
VALUES VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?) (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
(job_id, job_contact_id, type, direction, subject, body, (job_id, job_contact_id, type, direction, subject, body,
from_addr, to_addr, template_id), from_addr, to_addr, logged_at or _now_utc(), template_id),
) )
con.commit() con.commit()
row = con.execute( row = con.execute(

View file

@ -5,7 +5,6 @@
v-if="show" v-if="show"
class="modal-backdrop" class="modal-backdrop"
@click.self="emit('close')" @click.self="emit('close')"
@keydown.esc="emit('close')"
> >
<div <div
ref="dialogEl" ref="dialogEl"
@ -14,6 +13,7 @@
aria-modal="true" aria-modal="true"
:aria-label="title" :aria-label="title"
tabindex="-1" tabindex="-1"
@keydown.esc="emit('close')"
> >
<header class="modal-header"> <header class="modal-header">
<h2 class="modal-title">{{ title }}</h2> <h2 class="modal-title">{{ title }}</h2>
@ -93,21 +93,20 @@ const title = computed(() =>
props.type === 'call_note' ? 'Log a call' : 'Log an in-person note' props.type === 'call_note' ? 'Log a call' : 'Log an in-person note'
) )
const now = new Date()
const localNow = new Date(now.getTime() - now.getTimezoneOffset() * 60000)
.toISOString()
.slice(0, 16)
const form = ref({ const form = ref({
direction: '', direction: '',
subject: '', subject: '',
body: '', body: '',
logged_at: localNow, logged_at: '',
}) })
// Focus the dialog when it opens // Focus the dialog when it opens; compute localNow fresh each time
watch(() => props.show, async (val) => { watch(() => props.show, async (val) => {
if (val) { if (val) {
const now = new Date()
const localNow = new Date(now.getTime() - now.getTimezoneOffset() * 60000)
.toISOString()
.slice(0, 16)
error.value = null error.value = null
form.value = { direction: '', subject: '', body: '', logged_at: localNow } form.value = { direction: '', subject: '', body: '', logged_at: localNow }
await nextTick() await nextTick()
@ -129,6 +128,7 @@ async function handleSubmit() {
from_addr: null, from_addr: null,
to_addr: null, to_addr: null,
template_id: null, template_id: null,
logged_at: form.value.logged_at || undefined,
}) })
saving.value = false saving.value = false
if (result) emit('saved') if (result) emit('saved')

View file

@ -60,7 +60,7 @@ export const useMessagingStore = defineStore('messaging', () => {
templates.value = data ?? [] templates.value = data ?? []
} }
async function createMessage(payload: Omit<Message, 'id' | 'logged_at' | 'approved_at' | 'osprey_call_id'>) { async function createMessage(payload: Omit<Message, 'id' | 'approved_at' | 'osprey_call_id'> & { logged_at?: string }) {
saving.value = true saving.value = true
error.value = null error.value = null
const { data, error: fetchErr } = await useApiFetch<Message>( const { data, error: fetchErr } = await useApiFetch<Message>(