feat(call): enhance CallPage component with improved voicemail settings, greeting upload functionality, and UI refinements for better user experience
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="flex flex-col w-full h-full bg-gray-100 dark:bg-zinc-950" :class="{ dark: config?.theme === 'dark' }">
|
||||
<div class="mx-auto w-full max-w-xl p-4 flex-1 flex flex-col">
|
||||
<div class="w-full h-full overflow-y-auto">
|
||||
<div class="mx-auto w-full max-w-xl p-4 flex-1 flex flex-col min-h-full">
|
||||
<!-- Tabs -->
|
||||
<div class="flex border-b border-gray-200 dark:border-zinc-800 mb-6 shrink-0">
|
||||
<button
|
||||
@@ -216,8 +217,12 @@
|
||||
class="mt-4 p-4 text-left bg-gray-200 dark:bg-zinc-800 rounded-lg text-sm text-gray-600 dark:text-zinc-300"
|
||||
>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>TX: {{ activeCall.tx_packets }} ({{ formatBytes(activeCall.tx_bytes) }})</div>
|
||||
<div>RX: {{ activeCall.rx_packets }} ({{ formatBytes(activeCall.rx_bytes) }})</div>
|
||||
<div>
|
||||
TX: {{ activeCall.tx_packets }} ({{ formatBytes(activeCall.tx_bytes) }})
|
||||
</div>
|
||||
<div>
|
||||
RX: {{ activeCall.rx_packets }} ({{ formatBytes(activeCall.rx_bytes) }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -279,7 +284,9 @@
|
||||
<p class="text-sm font-semibold text-gray-900 dark:text-white truncate">
|
||||
{{ entry.remote_identity_name || "Unknown" }}
|
||||
</p>
|
||||
<span class="text-[10px] text-gray-500 dark:text-zinc-500 font-mono ml-2">
|
||||
<span
|
||||
class="text-[10px] text-gray-500 dark:text-zinc-500 font-mono ml-2"
|
||||
>
|
||||
{{ entry.timestamp ? formatDateTime(entry.timestamp * 1000) : "" }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -450,10 +457,12 @@
|
||||
<div class="text-xs text-amber-800 dark:text-amber-200">
|
||||
<p class="font-bold mb-1">Dependencies Missing</p>
|
||||
<p v-if="!voicemailStatus.has_espeak">
|
||||
Voicemail requires `espeak-ng` to generate greetings. Please install it on your system.
|
||||
Voicemail requires `espeak-ng` to generate greetings. Please install it on your
|
||||
system.
|
||||
</p>
|
||||
<p v-if="!voicemailStatus.has_ffmpeg" :class="{ 'mt-1': !voicemailStatus.has_espeak }">
|
||||
Voicemail requires `ffmpeg` to process audio files. Please install it on your system.
|
||||
Voicemail requires `ffmpeg` to process audio files. Please install it on your
|
||||
system.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -462,7 +471,9 @@
|
||||
<!-- Enabled Toggle -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm font-semibold text-gray-900 dark:text-white">Enable Voicemail</div>
|
||||
<div class="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
Enable Voicemail
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-zinc-400">
|
||||
Accept calls automatically and record messages
|
||||
</div>
|
||||
@@ -485,7 +496,8 @@
|
||||
|
||||
<!-- Greeting Text -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-bold text-gray-500 dark:text-zinc-400 uppercase tracking-tighter"
|
||||
<label
|
||||
class="text-xs font-bold text-gray-500 dark:text-zinc-400 uppercase tracking-tighter"
|
||||
>Greeting Message</label
|
||||
>
|
||||
<textarea
|
||||
@@ -498,6 +510,7 @@
|
||||
<p class="text-[10px] text-gray-500 dark:text-zinc-500">
|
||||
This text will be converted to speech using eSpeak NG.
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
:disabled="
|
||||
!voicemailStatus.has_espeak ||
|
||||
@@ -512,18 +525,58 @@
|
||||
>
|
||||
{{ isGeneratingGreeting ? "Generating..." : "Save & Generate" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Greeting Upload -->
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
class="text-xs font-bold text-gray-500 dark:text-zinc-400 uppercase tracking-tighter"
|
||||
>Custom Audio Greeting</label
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<input
|
||||
ref="greetingUpload"
|
||||
type="file"
|
||||
accept="audio/*"
|
||||
class="hidden"
|
||||
@change="uploadGreeting"
|
||||
/>
|
||||
<button
|
||||
v-if="voicemailStatus.has_espeak && voicemailStatus.has_ffmpeg"
|
||||
class="text-[10px] bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 px-3 py-1 rounded-full font-bold hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-colors flex items-center gap-1"
|
||||
:disabled="!voicemailStatus.has_ffmpeg || isUploadingGreeting"
|
||||
class="text-xs bg-gray-100 dark:bg-zinc-800 text-gray-700 dark:text-zinc-300 px-4 py-2 rounded-lg font-bold hover:bg-gray-200 dark:hover:bg-zinc-700 transition-colors disabled:opacity-50 flex items-center gap-2"
|
||||
@click="$refs.greetingUpload.click()"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="upload" class="size-4" />
|
||||
{{ isUploadingGreeting ? "Uploading..." : "Upload Audio File" }}
|
||||
</button>
|
||||
<div v-if="voicemailStatus.has_greeting" class="flex items-center gap-2">
|
||||
<button
|
||||
class="text-xs bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400 px-4 py-2 rounded-lg font-bold hover:bg-red-200 dark:hover:bg-red-900/50 transition-colors flex items-center gap-2"
|
||||
@click="deleteGreeting"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="delete" class="size-4" />
|
||||
Remove Greeting
|
||||
</button>
|
||||
<button
|
||||
class="text-xs bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 px-4 py-2 rounded-lg font-bold hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-colors flex items-center gap-2"
|
||||
@click="playGreeting"
|
||||
>
|
||||
<MaterialDesignIcon
|
||||
:icon-name="isPlayingGreeting ? 'stop' : 'play'"
|
||||
class="size-3"
|
||||
class="size-4"
|
||||
/>
|
||||
{{ isPlayingGreeting ? "Stop Preview" : "Preview Greeting" }}
|
||||
{{ isPlayingGreeting ? "Stop Preview" : "Preview" }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="text-[10px] text-gray-500 dark:text-zinc-500 italic">
|
||||
No custom greeting uploaded (default text will be used)
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-[10px] text-gray-500 dark:text-zinc-500">
|
||||
Supports MP3, OGG, WAV, M4A, FLAC. Will be converted to Opus.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Delays -->
|
||||
@@ -571,6 +624,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -600,8 +654,10 @@ export default {
|
||||
has_espeak: false,
|
||||
has_ffmpeg: false,
|
||||
is_recording: false,
|
||||
has_greeting: false,
|
||||
},
|
||||
isGeneratingGreeting: false,
|
||||
isUploadingGreeting: false,
|
||||
playingVoicemailId: null,
|
||||
audioPlayer: null,
|
||||
isPlayingGreeting: false,
|
||||
@@ -752,12 +808,47 @@ export default {
|
||||
try {
|
||||
await window.axios.post("/api/v1/telephone/voicemail/generate-greeting");
|
||||
ToastUtils.success("Greeting generated successfully");
|
||||
await this.getVoicemailStatus();
|
||||
} catch (e) {
|
||||
ToastUtils.error(e.response?.data?.message || "Failed to generate greeting");
|
||||
} finally {
|
||||
this.isGeneratingGreeting = false;
|
||||
}
|
||||
},
|
||||
async uploadGreeting(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
this.isUploadingGreeting = true;
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
try {
|
||||
await window.axios.post("/api/v1/telephone/voicemail/greeting/upload", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
ToastUtils.success("Greeting uploaded successfully");
|
||||
await this.getVoicemailStatus();
|
||||
} catch (e) {
|
||||
ToastUtils.error(e.response?.data?.message || "Failed to upload greeting");
|
||||
} finally {
|
||||
this.isUploadingGreeting = false;
|
||||
event.target.value = "";
|
||||
}
|
||||
},
|
||||
async deleteGreeting() {
|
||||
if (!confirm("Are you sure you want to delete your custom greeting?")) return;
|
||||
|
||||
try {
|
||||
await window.axios.delete("/api/v1/telephone/voicemail/greeting");
|
||||
ToastUtils.success("Greeting deleted");
|
||||
await this.getVoicemailStatus();
|
||||
} catch (e) {
|
||||
ToastUtils.error("Failed to delete greeting");
|
||||
}
|
||||
},
|
||||
async playVoicemail(voicemail) {
|
||||
if (this.playingVoicemailId === voicemail.id) {
|
||||
this.audioPlayer.pause();
|
||||
|
||||
Reference in New Issue
Block a user