diff --git a/app/components/messages/ChatAliasModal.vue b/app/components/messages/ChatAliasModal.vue
new file mode 100644
index 0000000..eea99cc
--- /dev/null
+++ b/app/components/messages/ChatAliasModal.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
Editar Alias
+
+
+
+
+
+
+ Asigna un nombre personalizado a este chat. El alias tiene prioridad sobre el nombre de WhatsApp.
+
+
+
+
+
+
+
+
Nombre original:
+
{{ chat.originalName || chat.jid }}
+
+
+
+
+
+
+ Quitar alias
+
+
+
+ Cancelar
+
+
+ Guardar
+
+
+
+
+
+
+
+
+
diff --git a/app/components/messages/ChatItem.vue b/app/components/messages/ChatItem.vue
index c4f2d61..f47ca97 100644
--- a/app/components/messages/ChatItem.vue
+++ b/app/components/messages/ChatItem.vue
@@ -13,9 +13,25 @@
-
{{ chat.name }}
+
{{ formatTime(chat.lastMessageAt) }}
+
+
@@ -251,6 +252,14 @@
+
+
+
@@ -344,6 +353,10 @@ const newChatPhoneNumber = ref('')
const newChatLoading = ref(false)
const newChatError = ref('')
+// Alias modal state
+const showAliasModal = ref(false)
+const chatToEditAlias = ref(null)
+
// Instance options for selector
const instanceOptions = computed(() =>
instances.value
@@ -723,6 +736,36 @@ const handleReact = async (message: any, emoji: string) => {
}
}
+// Alias modal functions
+const openAliasModal = (chat: any) => {
+ chatToEditAlias.value = {
+ ...chat,
+ originalName: chat.originalName || chat.name
+ }
+ showAliasModal.value = true
+}
+
+const handleAliasSaved = (updatedChat: any) => {
+ // Update chat in list
+ const index = chats.value.findIndex(c => c.id === updatedChat.id)
+ if (index !== -1) {
+ chats.value[index] = {
+ ...chats.value[index],
+ alias: updatedChat.alias,
+ name: updatedChat.name
+ }
+ }
+
+ // Update selected chat if it's the same
+ if (selectedChat.value?.id === updatedChat.id) {
+ selectedChat.value = {
+ ...selectedChat.value,
+ alias: updatedChat.alias,
+ name: updatedChat.name
+ }
+ }
+}
+
// New chat modal functions
const closeNewChatModal = () => {
showNewChatModal.value = false
diff --git a/server/api/messages/[instanceId]/[chatId]/alias.put.ts b/server/api/messages/[instanceId]/[chatId]/alias.put.ts
new file mode 100644
index 0000000..40fb73f
--- /dev/null
+++ b/server/api/messages/[instanceId]/[chatId]/alias.put.ts
@@ -0,0 +1,68 @@
+/**
+ * PUT /api/messages/:instanceId/:chatId/alias
+ * Update the alias of a chat
+ */
+import { query } from '../../../../utils/database'
+
+export default defineEventHandler(async (event) => {
+ const username = getHeader(event, 'x-authentik-username')
+ if (!username) {
+ throw createError({ statusCode: 401, message: 'Unauthorized' })
+ }
+
+ const instanceId = getRouterParam(event, 'instanceId')
+ const chatId = getRouterParam(event, 'chatId')
+
+ if (!instanceId) {
+ throw createError({ statusCode: 400, message: 'Missing instanceId' })
+ }
+
+ if (!chatId) {
+ throw createError({ statusCode: 400, message: 'Missing chatId' })
+ }
+
+ const body = await readBody<{ alias: string | null }>(event)
+
+ // alias can be null to remove it, or a string to set it
+ if (body?.alias === undefined) {
+ throw createError({ statusCode: 400, message: 'Missing alias in request body' })
+ }
+
+ try {
+ // Update the alias
+ const result = await query(
+ `UPDATE chats
+ SET alias = $1, updated_at = NOW()
+ WHERE id = $2 AND instance_id = $3
+ RETURNING id, jid, name, alias, is_group`,
+ [body.alias || null, chatId, instanceId]
+ )
+
+ if (result.rows.length === 0) {
+ throw createError({ statusCode: 404, message: 'Chat not found' })
+ }
+
+ const chat = result.rows[0]
+ return {
+ success: true,
+ chat: {
+ id: chat.id,
+ jid: chat.jid,
+ name: chat.name,
+ alias: chat.alias,
+ isGroup: chat.is_group
+ }
+ }
+ } catch (error: any) {
+ console.error('[Alias API] Error updating alias:', error)
+
+ if (error.statusCode) {
+ throw error
+ }
+
+ throw createError({
+ statusCode: 500,
+ message: error.message || 'Error updating alias'
+ })
+ }
+})
diff --git a/server/database/migrations/003_add_alias.sql b/server/database/migrations/003_add_alias.sql
new file mode 100644
index 0000000..13f37c4
--- /dev/null
+++ b/server/database/migrations/003_add_alias.sql
@@ -0,0 +1,12 @@
+-- =====================================================
+-- Migration 003: Add alias field to chats
+-- =====================================================
+-- Allows users to assign custom names (aliases) to chats
+-- Alias takes priority over automatic names from WhatsApp
+-- =====================================================
+
+-- Add alias column to chats table
+ALTER TABLE chats ADD COLUMN IF NOT EXISTS alias VARCHAR(255);
+
+-- Add index for alias searches (partial index for non-null values)
+CREATE INDEX IF NOT EXISTS idx_chats_alias ON chats(alias) WHERE alias IS NOT NULL;
diff --git a/server/utils/mcp.ts b/server/utils/mcp.ts
index f0d6a39..82aa391 100644
--- a/server/utils/mcp.ts
+++ b/server/utils/mcp.ts
@@ -688,10 +688,10 @@ export async function handleToolCall(toolName: string, args: Record
const result = await query(
`SELECT
- c.id, c.jid, c.is_group, c.unread_count, c.last_message_at, c.last_message_type,
+ c.id, c.jid, c.is_group, c.unread_count, c.last_message_at, c.last_message_type, c.alias,
CASE
- WHEN c.is_group THEN COALESCE(gm.subject, c.name, c.jid)
- ELSE COALESCE(ct.name, ct.push_name, c.name, SPLIT_PART(c.jid, '@', 1))
+ WHEN c.is_group THEN COALESCE(c.alias, gm.subject, c.name, c.jid)
+ ELSE COALESCE(c.alias, ct.name, ct.push_name, c.name, SPLIT_PART(c.jid, '@', 1))
END as name
FROM chats c
LEFT JOIN contacts ct ON c.instance_id = ct.instance_id AND c.jid = ct.jid
@@ -708,6 +708,7 @@ export async function handleToolCall(toolName: string, args: Record
id: row.id,
jid: row.jid,
name: row.name,
+ alias: row.alias,
isGroup: row.is_group,
unreadCount: row.unread_count || 0,
lastMessageAt: row.last_message_at,
@@ -725,10 +726,10 @@ export async function handleToolCall(toolName: string, args: Record
const result = await query(
`SELECT
- c.id, c.jid, c.is_group, c.unread_count, c.last_message_at, c.last_message_type,
+ c.id, c.jid, c.is_group, c.unread_count, c.last_message_at, c.last_message_type, c.alias,
CASE
- WHEN c.is_group THEN COALESCE(gm.subject, c.name, c.jid)
- ELSE COALESCE(ct.name, ct.push_name, c.name, SPLIT_PART(c.jid, '@', 1))
+ WHEN c.is_group THEN COALESCE(c.alias, gm.subject, c.name, c.jid)
+ ELSE COALESCE(c.alias, ct.name, ct.push_name, c.name, SPLIT_PART(c.jid, '@', 1))
END as name,
gm.participants as group_participants
FROM chats c
@@ -747,6 +748,7 @@ export async function handleToolCall(toolName: string, args: Record
id: chat.id,
jid: chat.jid,
name: chat.name,
+ alias: chat.alias,
isGroup: chat.is_group,
unreadCount: chat.unread_count || 0,
lastMessageAt: chat.last_message_at,
@@ -773,16 +775,16 @@ export async function handleToolCall(toolName: string, args: Record
const result = await query(
`SELECT
- c.id, c.jid, c.is_group, c.unread_count, c.last_message_at,
+ c.id, c.jid, c.is_group, c.unread_count, c.last_message_at, c.alias,
CASE
- WHEN c.is_group THEN COALESCE(gm.subject, c.name, c.jid)
- ELSE COALESCE(ct.name, ct.push_name, c.name, SPLIT_PART(c.jid, '@', 1))
+ WHEN c.is_group THEN COALESCE(c.alias, gm.subject, c.name, c.jid)
+ ELSE COALESCE(c.alias, ct.name, ct.push_name, c.name, SPLIT_PART(c.jid, '@', 1))
END as name
FROM chats c
LEFT JOIN contacts ct ON c.instance_id = ct.instance_id AND c.jid = ct.jid
LEFT JOIN group_metadata gm ON c.instance_id = gm.instance_id AND c.jid = gm.jid
WHERE c.instance_id = $1
- AND (c.name ILIKE $2 OR c.jid ILIKE $2 OR ct.name ILIKE $2 OR ct.push_name ILIKE $2 OR gm.subject ILIKE $2)
+ AND (c.alias ILIKE $2 OR c.name ILIKE $2 OR c.jid ILIKE $2 OR ct.name ILIKE $2 OR ct.push_name ILIKE $2 OR gm.subject ILIKE $2)
ORDER BY c.last_message_at DESC NULLS LAST
LIMIT $3`,
[instanceId, `%${searchQuery}%`, Math.min(limit, 50)]
@@ -794,6 +796,7 @@ export async function handleToolCall(toolName: string, args: Record
id: row.id,
jid: row.jid,
name: row.name,
+ alias: row.alias,
isGroup: row.is_group,
unreadCount: row.unread_count || 0,
lastMessageAt: row.last_message_at