Vue Firebase Chat System

User
Vue js firbase db chat system for any user to admin
Assistant
Here’s a simple **Vue 3 + Firebase Realtime Database chat system** structure where **any user can chat with admin**. This setup includes Firebase auth, chat messages storage, and basic frontend components.

### **1. Firebase Setup**

Enable:
– Firebase Authentication (Email/Password or Anonymous)
– Realtime Database

Create a database structure like:

"chats": {
  "userUid1": {
    "messages": {
      "msgId1": {
        "sender": "user", // or "admin"
        "text": "Hello",
        "timestamp": 1689782321
      },
      ...
    }
  }
}

### **2. Vue Project Setup**

Install:

yarn add firebase pinia vue-router

### **3. Firebase Config (src/firebase.js)**

import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getDatabase } from 'firebase/database'

const firebaseConfig = {
  apiKey: '...',
  authDomain: '...',
  databaseURL: '...',
  projectId: '...',
  storageBucket: '...',
  messagingSenderId: '...',
  appId: '...'
}

const app = initializeApp(firebaseConfig)
export const auth = getAuth(app)
export const db = getDatabase(app)

### **4. Chat Component (UserChat.vue)**

<template>
  <div>
    <div v-for="msg in messages" :key="msg.id" :class="msg.sender">
      <p>{{ msg.text }}</p>
    </div>
    <input v-model="newMessage" @keyup.enter="sendMessage" placeholder="Type your message..." />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { db, auth } from '@/firebase'
import { onValue, push, ref as dbRef, serverTimestamp } from 'firebase/database'
import { signInAnonymously } from 'firebase/auth'

const messages = ref([])
const newMessage = ref('')
let uid = ''

onMounted(async () => {
  const { user } = await signInAnonymously(auth)
  uid = user.uid

  const chatRef = dbRef(db, `chats/${uid}/messages`)
  onValue(chatRef, (snapshot) => {
    messages.value = []
    snapshot.forEach((child) => {
      messages.value.push({ id: child.key, ...child.val() })
    })
  })
})

const sendMessage = () => {
  if (!newMessage.value.trim()) return

  const chatRef = dbRef(db, `chats/${uid}/messages`)
  push(chatRef, {
    sender: 'user',
    text: newMessage.value,
    timestamp: serverTimestamp(),
  })
  newMessage.value = ''
}
</script>

<style>
.user { text-align: right; }
.admin { text-align: left; }
</style>

### **5. Admin Panel (AdminChat.vue)**

Basic idea:

<script setup>
import { db } from '@/firebase'
import { ref as dbRef, onValue, push, serverTimestamp } from 'firebase/database'
import { ref } from 'vue'

const userChats = ref({})

onValue(dbRef(db, 'chats'), (snapshot) => {
  userChats.value = snapshot.val()
})

const sendToUser = (uid, message) => {
  const chatRef = dbRef(db, `chats/${uid}/messages`)
  push(chatRef, {
    sender: 'admin',
    text: message,
    timestamp: serverTimestamp()
  })
}
</script>

### **6. Security Rules (Firebase Realtime DB)**

{
  "rules": {
    "chats": {
      "$uid": {
        ".read": "$uid === auth.uid || auth.token.admin === true",
        ".write": "$uid === auth.uid || auth.token.admin === true"
      }
    }
  }
}

Want me to help integrate it into your current Vue setup with Vuetify or Pinia?

User
Vuetify,pinia
Assistant
Perfect! Here's a **Vuetify 3 + Pinia + Firebase Realtime DB chat system** where **any user can chat with the admin**.

## **1. Project Setup**

Install dependencies:

yarn add firebase pinia vue-router vuetify

Enable Firebase:
– Authentication (Anonymous or Email/Password)
– Realtime Database

## **2. Firebase Init (src/firebase.ts)**

import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getDatabase } from 'firebase/database'

const firebaseConfig = {
  apiKey: '...',
  authDomain: '...',
  databaseURL: '...',
  projectId: '...',
  storageBucket: '...',
  messagingSenderId: '...',
  appId: '...'
}

const app = initializeApp(firebaseConfig)
export const auth = getAuth(app)
export const db = getDatabase(app)

## **3. Pinia Store (src/stores/chatStore.ts)**

import { defineStore } from 'pinia'
import { db } from '@/firebase'
import {
  ref as dbRef,
  onValue,
  push,
  serverTimestamp
} from 'firebase/database'

export const useChatStore = defineStore('chat', {
  state: () => ({
    uid: '',
    messages: [] as Array<{ id: string, text: string, sender: string }>
  }),
  actions: {
    init(uid: string) {
      this.uid = uid
      const chatRef = dbRef(db, `chats/${uid}/messages`)
      onValue(chatRef, snapshot => {
        const msgs: any[] = []
        snapshot.forEach(child => {
          msgs.push({ id: child.key, ...child.val() })
        })
        this.messages = msgs
      })
    },
    sendMessage(text: string, sender = 'user') {
      const chatRef = dbRef(db, `chats/${this.uid}/messages`)
      push(chatRef, {
        sender,
        text,
        timestamp: serverTimestamp()
      })
    }
  }
})

## **4. Vuetify Chat UI (components/ChatBox.vue)**

<template>
  <v-container>
    <v-list max-height="400" class="overflow-y-auto">
      <v-list-item
        v-for="msg in chat.messages"
        :key="msg.id"
        :class="msg.sender === 'admin' ? 'text-left' : 'text-right'"
      >
        <v-chip :color="msg.sender === 'admin' ? 'blue' : 'green'" text-color="white">
          {{ msg.text }}
        </v-chip>
      </v-list-item>
    </v-list>

    <v-text-field
      v-model="input"
      label="Type your message..."
      @keyup.enter="send"
      append-icon="mdi-send"
      @click:append="send"
    />
  </v-container>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { signInAnonymously } from 'firebase/auth'
import { auth } from '@/firebase'
import { useChatStore } from '@/stores/chatStore'

const chat = useChatStore()
const input = ref('')

onMounted(async () => {
  const { user } = await signInAnonymously(auth)
  chat.init(user.uid)
})

const send = () => {
  if (!input.value.trim()) return
  chat.sendMessage(input.value.trim())
  input.value = ''
}
</script>

## **5. Admin View (Optional Example)**

If you want the admin to view all users and respond, I can give a separate component.

## **6. Firebase Rules (Realtime DB)**

{
  "rules": {
    "chats": {
      "$uid": {
        ".read": "$uid === auth.uid || auth.token.admin === true",
        ".write": "$uid === auth.uid || auth.token.admin === true"
      }
    }
  }
}

Would you like the **admin panel version** next? Where admin can select a user and reply?

User
Admin panel version
Assistant
Here’s how to create an **Admin Panel (Vuetify + Firebase + Pinia)** where **admin can view all user chats and respond**.

## **1. Firebase Rules Update**

Allow admin (custom claim or role) to read all chats:

{
  "rules": {
    "chats": {
      "$uid": {
        ".read": "auth.token.admin === true || auth.uid === $uid",
        ".write": "auth.token.admin === true || auth.uid === $uid"
      }
    }
  }
}

> If you're not using custom claims yet, during development you can temporarily bypass with `.read: true`, but it’s **not safe for production**.

## **2. Pinia Admin Store (src/stores/adminChatStore.ts)**

import { defineStore } from 'pinia'
import { db } from '@/firebase'
import {
  ref as dbRef,
  onValue,
  push,
  serverTimestamp
} from 'firebase/database'

export const useAdminChatStore = defineStore('adminChat', {
  state: () => ({
    allChats: {} as Record<string, { messages: any }>
  }),
  actions: {
    loadChats() {
      const chatListRef = dbRef(db, 'chats')
      onValue(chatListRef, snapshot => {
        const chats: Record<string, any> = {}
        snapshot.forEach(child => {
          chats[child.key!] = child.val()
        })
        this.allChats = chats
      })
    },
    sendToUser(uid: string, text: string) {
      const userMsgRef = dbRef(db, `chats/${uid}/messages`)
      push(userMsgRef, {
        sender: 'admin',
        text,
        timestamp: serverTimestamp()
      })
    }
  }
})

## **3. Vuetify Admin Panel (components/AdminChatPanel.vue)**

<template>
  <v-container>
    <v-row>
      <v-col cols="4">
        <v-list>
          <v-list-item
            v-for="(chat, uid) in adminChat.allChats"
            :key="uid"
            @click="selectUser(uid)"
            :active="uid === selectedUid"
          >
            <v-list-item-title>{{ uid }}</v-list-item-title>
          </v-list-item>
        </v-list>
      </v-col>

      <v-col cols="8">
        <v-list max-height="400" class="overflow-y-auto">
          <v-list-item
            v-for="(msg, i) in messages"
            :key="i"
            :class="msg.sender === 'admin' ? 'text-left' : 'text-right'"
          >
            <v-chip :color="msg.sender === 'admin' ? 'blue' : 'green'" text-color="white">
              {{ msg.text }}
            </v-chip>
          </v-list-item>
        </v-list>

        <v-text-field
          v-model="input"
          label="Reply..."
          append-icon="mdi-send"
          @keyup.enter="send"
          @click:append="send"
        />
      </v-col>
    </v-row>
  </v-container>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useAdminChatStore } from '@/stores/adminChatStore'

const adminChat = useAdminChatStore()
adminChat.loadChats()

const selectedUid = ref('')
const input = ref('')

const messages = computed(() => {
  return selectedUid.value && adminChat.allChats[selectedUid.value]?.messages
    ? Object.values(adminChat.allChats[selectedUid.value].messages)
    : []
})

const selectUser = (uid: string) => {
  selectedUid.value = uid
}

const send = () => {
  if (!input.value.trim() || !selectedUid.value) return
  adminChat.sendToUser(selectedUid.value, input.value.trim())
  input.value = ''
}
</script>

## **4. Bonus: Admin Auth**

If needed, you can use a static `admin@example.com` account for testing, or implement [custom claims](https://firebase.google.com/docs/auth/admin/custom-claims) to identify admin users securely.

Would you like to:
– Add real-time unread message indicators?
– Filter messages by date?
– Enable file/image sharing?

Let me know and I can guide further!

User
How admin can reply to chat
Assistant
Great question! Here’s **how the admin can reply to any user's chat** using the setup we discussed:

## **1. Admin Selects a User**

In the `AdminChatPanel.vue`, when the admin clicks on a user from the left column (`v-list`), we store their UID in `selectedUid`.

const selectedUid = ref('')
const selectUser = (uid: string) => {
  selectedUid.value = uid
}

## **2. Admin Types a Message and Sends It**

The message is typed in a `v-text-field` and sent using:

const send = () => {
  if (!input.value.trim() || !selectedUid.value) return
  adminChat.sendToUser(selectedUid.value, input.value.trim())
  input.value = ''
}

## **3. Behind the Scenes: `sendToUser()` in Store**

sendToUser(uid: string, text: string) {
  const userMsgRef = dbRef(db, `chats/${uid}/messages`)
  push(userMsgRef, {
    sender: 'admin',
    text,
    timestamp: serverTimestamp()
  })
}

This writes a new message under that user's `messages` node in Firebase Realtime Database.

Example path:

chats/user_uid_abc123/messages/msg_xyz

And message format:

{
  "sender": "admin",
  "text": "Thanks for reaching out!",
  "timestamp": 1710000000
}

## **4. It Instantly Appears on User Side**

Because the user chat component uses Firebase's `onValue()` listener on their own `uid`, new admin messages appear in real-time.

### **In Summary:**
– Admin clicks a user
– Types a message
– `sendToUser(uid, text)` saves it to that user’s chat node
– User sees the message instantly via Firebase listener

Let me know if you want:
– Timestamp formatting
– Typing indicators
– Notification sounds on new messages
– Vuex-compatible version (if not using Pinia)

User
Typing indicator, notification sound
Assistant
Let’s enhance your **Vue + Vuetify + Firebase chat system** with:

## **1. Typing Indicator (for Admin to see when User is typing)**

### **A. Database Path**
Store typing status like:

chats/{uid}/typing: true or false

### **B. User Side – Set Typing Status**

In `UserChat.vue`, update `v-text-field`:

<v-text-field
  v-model="newMessage"
  @input="updateTyping"
  @blur="setTyping(false)"
  ...
/>

Script logic:

import { set, ref as dbRef } from 'firebase/database'

let typingTimer: any = null

const updateTyping = () => {
  setTyping(true)
  clearTimeout(typingTimer)
  typingTimer = setTimeout(() => setTyping(false), 2000)
}

const setTyping = (isTyping: boolean) => {
  if (uid)
    set(dbRef(db, `chats/${uid}/typing`), isTyping)
}

### **C. Admin Side – Listen to Typing**

In `AdminChatPanel.vue`, add:

import { onValue, ref as dbRef } from 'firebase/database'

const isTyping = ref(false)

watch(selectedUid, (uid) => {
  if (!uid) return
  const typingRef = dbRef(db, `chats/${uid}/typing`)
  onValue(typingRef, snapshot => {
    isTyping.value = snapshot.val() === true
  })
})

Then show this in the UI:

<v-chip v-if="isTyping" color="grey">User is typing...</v-chip>

## **2. Notification Sound on New Message**

### **A. Add Sound File**

Put a `ping.mp3` file in your `public` folder:

/public/sounds/ping.mp3

### **B. In Chat Component (UserChat.vue or AdminChat.vue)**

const audio = new Audio('/sounds/ping.mp3')

const playSound = () => {
  audio.currentTime = 0
  audio.play()
}

### **C. Trigger on New Message**

In your `onValue()` Firebase listener, play sound **only for messages from the other side**:

Example in user chat:

onValue(chatRef, (snapshot) => {
  const msgs: any[] = []
  snapshot.forEach((child) => {
    const msg = { id: child.key, ...child.val() }
    msgs.push(msg)

    if (msg.sender === 'admin') playSound()
  })
  chat.messages = msgs
})

Do similarly on the **admin side**, triggering only when `msg.sender === 'user'`.

Would you also like a **"user is online" indicator** or to show when the **last message was seen**?

User
With Anonmous auth for user also require
To enter name and email
Assistant
Great — here’s how to enhance your **Vue + Vuetify + Firebase chat system** with:

– **Anonymous auth**
– **Ask name and email before starting chat**
– **Typing indicator**
– **Notification sound**

## **1. Ask Name + Email Before Chat Starts**

### **A. Form Component (UserLoginForm.vue)**

<template>
  <v-container>
    <v-card class="pa-4" max-width="400" elevation="3">
      <v-text-field v-model="name" label="Your Name" required />
      <v-text-field v-model="email" label="Email" type="email" required />
      <v-btn @click="startChat" color="primary" :disabled="!name || !email">Start Chat</v-btn>
    </v-card>
  </v-container>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { signInAnonymously } from 'firebase/auth'
import { db, auth } from '@/firebase'
import { ref as dbRef, set } from 'firebase/database'

const name = ref('')
const email = ref('')
const router = useRouter()

const startChat = async () => {
  const { user } = await signInAnonymously(auth)
  await set(dbRef(db, `chats/${user.uid}/userInfo`), {
    name: name.value,
    email: email.value,
    created_at: new Date().toISOString()
  })
  router.push({ name: 'chat' })
}
</script>

## **2. Store User Info in Firebase**

Stored at:

chats/{uid}/userInfo = { name, email, created_at }

## **3. Admin Panel – Show Name + Email**

In `AdminChatPanel.vue` (or wherever you list users):

<v-list-item-title>{{ chat.userInfo?.name || uid }}</v-list-item-title>
<v-list-item-subtitle>{{ chat.userInfo?.email }}</v-list-item-subtitle>

## **4. Chat View for User (ChatBox.vue)**

Same as before, with the typing indicator and sound features added (see previous response).

## **5. Full Routing Example**

In your `router/index.ts`:

{
  path: '/',
  name: 'login',
  component: () => import('@/components/UserLoginForm.vue')
},
{
  path: '/chat',
  name: 'chat',
  component: () => import('@/components/ChatBox.vue')
}

## **6. Add Global Typing + Sound Logic**

As shown earlier:
– Add `typing` node updates on user side
– Admin listens for it and shows "User is typing…"
– Add sound file to `/public/sounds/ping.mp3`
– Play `new Audio('/sounds/ping.mp3')` when message received from the opposite side

Let me know if you'd like:
– Message read receipts
– Online/offline status
– Export chat history as PDF

Want a GitHub-ready version of this codebase?

User
Okay first create an admin account in firebase and login so that user can see that admin is online
Assistant
Perfect! Here's how to create and use an **admin account in Firebase** and show users when the **admin is online**:

## **1. Create Admin Account in Firebase**

### **A. Enable Email/Password Auth**

– Go to Firebase Console > **Authentication** > **Sign-in Method**
– Enable **Email/Password**

### **B. Create Admin User**

– Go to Firebase Console > **Authentication** > **Users**
– Click **Add User**
– Email: `admin@example.com`
– Password: `admin123` (use secure password in production)

### **C. (Optional but Recommended) Add Custom Claims for Admin**

If you're using Firebase Admin SDK (Node.js backend or Cloud Functions), set custom claims:

// Example in Node.js
admin.auth().setCustomUserClaims(uid, { admin: true })

But for now, you can skip this if you're just using email to identify the admin.

## **2. Admin Presence Status with Realtime DB**

### **A. Database Path**

Use:

presence/admin/status = "online" / "offline"

## **3. Set Admin Online Status**

### **AdminLogin.vue** (basic login + status update)

<script setup lang="ts">
import { ref } from 'vue'
import { signInWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth'
import { auth, db } from '@/firebase'
import { ref as dbRef, set, onDisconnect } from 'firebase/database'
import { useRouter } from 'vue-router'

const email = ref('')
const password = ref('')
const router = useRouter()

const login = async () => {
  const { user } = await signInWithEmailAndPassword(auth, email.value, password.value)
  const statusRef = dbRef(db, `presence/admin`)
  await set(statusRef, { status: 'online', last_active: new Date().toISOString() })

  onDisconnect(statusRef).set({ status: 'offline', last_active: new Date().toISOString() })

  router.push({ name: 'admin-panel' })
}
</script>

## **4. Show Admin Status to Users**

### **UserChat.vue**

import { db } from '@/firebase'
import { ref as dbRef, onValue } from 'firebase/database'
import { ref, onMounted } from 'vue'

const adminStatus = ref('offline')

onMounted(() => {
  const statusRef = dbRef(db, 'presence/admin')
  onValue(statusRef, (snapshot) => {
    const data = snapshot.val()
    adminStatus.value = data?.status || 'offline'
  })
})

### **In Template**

<v-alert
  :type="adminStatus === 'online' ? 'success' : 'warning'"
  dense
  class="mb-2"
>
  Admin is {{ adminStatus }}
</v-alert>

## **5. Optional Enhancements**

– Track admin `last_active`
– Use `onDisconnect()` to auto-update status on close
– Add `typing` and `isOnline` together under `chats/{uid}/status`

Would you like a **global Vue store (Pinia)** to manage admin status across components?

User
Yes
Assistant
Awesome! Here's how to manage **admin online status globally using Pinia** so it’s accessible across all components (like `UserChat.vue`, `ChatBox.vue`, `Header.vue`, etc.).

## ✅ 1. **Create Pinia Store for Admin Status**

### **`src/stores/adminStatusStore.ts`**

import { defineStore } from 'pinia'
import { db } from '@/firebase'
import { ref as dbRef, onValue } from 'firebase/database'
import { ref } from 'vue'

export const useAdminStatusStore = defineStore('adminStatus', () => {
  const status = ref<'online' | 'offline'>('offline')
  const lastActive = ref<string | null>(null)

  const listenToAdminStatus = () => {
    const adminStatusRef = dbRef(db, 'presence/admin')
    onValue(adminStatusRef, (snapshot) => {
      const data = snapshot.val()
      status.value = data?.status || 'offline'
      lastActive.value = data?.last_active || null
    })
  }

  return {
    status,
    lastActive,
    listenToAdminStatus
  }
})

## ✅ 2. **Initialize in User Components**

### **In `UserChat.vue` or main layout component**

import { useAdminStatusStore } from '@/stores/adminStatusStore'
import { onMounted } from 'vue'

const adminStatusStore = useAdminStatusStore()

onMounted(() => {
  adminStatusStore.listenToAdminStatus()
})

## ✅ 3. **Use in Template Anywhere**

<v-alert
  :type="adminStatusStore.status === 'online' ? 'success' : 'warning'"
  icon="mdi-account"
  class="mb-3"
>
  Admin is currently <strong>{{ adminStatusStore.status }}</strong>
</v-alert>

You can also show **last seen**:

<v-chip v-if="adminStatusStore.status === 'offline'" class="ml-2" size="small" color="grey">
  Last seen: {{ new Date(adminStatusStore.lastActive).toLocaleString() }}
</v-chip>

## ✅ 4. **Bonus: Display in Header or Nav**

Anywhere in your layout or header:

<v-badge :content="adminStatusStore.status === 'online' ? '●' : ''" color="green">
  <span>Admin</span>
</v-badge>

Would you like me to combine **admin login, presence handling**, and **this Pinia store** into a single working GitHub example repo or zip?

User
Create separate admin panel to view all online users and a button to open chat and reply on same chat to user
Assistant
Great — here's how to build a **separate admin panel** with:

1. **List of online users**
2. **Button to open a chat**
3. **Realtime reply/chat UI on the right**

All built using **Vue 3 + Vuetify + Firebase + Pinia**.

## ✅ 1. Firebase Realtime DB Structure

We'll track online users under:

chats/{uid}/status: {
  online: true,
  last_active: timestamp,
  name: 'User Name',
  email: 'user@email.com'
}

And store chat messages under:

chats/{uid}/messages

## ✅ 2. Track User Online Status

In `UserChat.vue`, after anonymous login:

import { db } from '@/firebase'
import { ref as dbRef, set, onDisconnect } from 'firebase/database'

await set(dbRef(db, `chats/${uid}/status`), {
  online: true,
  last_active: new Date().toISOString(),
  name: 'User Name',
  email: 'email@example.com'
})

onDisconnect(dbRef(db, `chats/${uid}/status`)).set({
  online: false,
  last_active: new Date().toISOString()
})

## ✅ 3. Pinia Store for Admin Panel Users

### `src/stores/onlineUsersStore.ts`

import { defineStore } from 'pinia'
import { db } from '@/firebase'
import { ref as dbRef, onValue } from 'firebase/database'
import { ref } from 'vue'

export const useOnlineUsersStore = defineStore('onlineUsers', () => {
  const users = ref<Record<string, any>>({})

  const loadOnlineUsers = () => {
    const chatRef = dbRef(db, 'chats')
    onValue(chatRef, (snapshot) => {
      const data: Record<string, any> = {}
      snapshot.forEach((child) => {
        const status = child.val()?.status
        if (status?.online) {
          data[child.key!] = {
            uid: child.key!,
            ...status
          }
        }
      })
      users.value = data
    })
  }

  return {
    users,
    loadOnlineUsers
  }
})

## ✅ 4. Admin Panel Layout (`AdminPanel.vue`)

<template>
  <v-container fluid>
    <v-row>
      <!-- Sidebar - Online Users -->
      <v-col cols="4">
        <v-card>
          <v-card-title>Online Users</v-card-title>
          <v-divider></v-divider>
          <v-list>
            <v-list-item
              v-for="(user, uid) in userStore.users"
              :key="uid"
              @click="selectUser(uid)"
              :active="uid === selectedUid"
            >
              <v-list-item-title>{{ user.name }}</v-list-item-title>
              <v-list-item-subtitle>{{ user.email }}</v-list-item-subtitle>
            </v-list-item>
          </v-list>
        </v-card>
      </v-col>

      <!-- Chat Panel -->
      <v-col cols="8" v-if="selectedUid">
        <v-card>
          <v-card-title>Chat with {{ userStore.users[selectedUid].name }}</v-card-title>
          <v-divider></v-divider>
          <v-card-text>
            <v-list max-height="400" class="overflow-y-auto">
              <v-list-item
                v-for="msg in chatMessages"
                :key="msg.id"
                :class="msg.sender === 'admin' ? 'text-left' : 'text-right'"
              >
                <v-chip :color="msg.sender === 'admin' ? 'blue' : 'green'" text-color="white">
                  {{ msg.text }}
                </v-chip>
              </v-list-item>
            </v-list>
            <v-text-field
              v-model="input"
              label="Type message"
              append-icon="mdi-send"
              @click:append="sendMessage"
              @keyup.enter="sendMessage"
            />
          </v-card-text>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { db } from '@/firebase'
import {
  ref as dbRef,
  onValue,
  push,
  serverTimestamp
} from 'firebase/database'
import { useOnlineUsersStore } from '@/stores/onlineUsersStore'

const userStore = useOnlineUsersStore()
userStore.loadOnlineUsers()

const selectedUid = ref('')
const input = ref('')
const chatMessages = ref<any[]>([])

watch(selectedUid, (uid) => {
  if (!uid) return
  const msgRef = dbRef(db, `chats/${uid}/messages`)
  onValue(msgRef, (snapshot) => {
    const msgs: any[] = []
    snapshot.forEach((child) => {
      msgs.push({ id: child.key, ...child.val() })
    })
    chatMessages.value = msgs
  })
})

const selectUser = (uid: string) => {
  selectedUid.value = uid
}

const sendMessage = () => {
  if (!input.value.trim() || !selectedUid.value) return
  const refToMsg = dbRef(db, `chats/${selectedUid.value}/messages`)
  push(refToMsg, {
    sender: 'admin',
    text: input.value.trim(),
    timestamp: serverTimestamp()
  })
  input.value = ''
}
</script>

## ✅ 5. Optional Enhancements

– Show last message in sidebar
– Add search bar for users
– Load chat history on demand (pagination)
– Add avatar/icon per user

Would you like this exported into a GitHub repo or a zip structure to test locally?

Leave a Comment

Your email address will not be published. Required fields are marked *