@@ -7,19 +7,23 @@ import {
77 DropdownMenu ,
88 DropdownMenuContent ,
99 DropdownMenuItem ,
10- DropdownMenuLabel ,
11- DropdownMenuSeparator ,
1210 DropdownMenuTrigger ,
1311} from ' @/components/ui/dropdown-menu' ;
12+ import {
13+ InputGroup ,
14+ InputGroupAddon ,
15+ InputGroupButton ,
16+ InputGroupTextarea ,
17+ } from ' @/components/ui/input-group' ;
1418import { Badge } from ' @/components/ui/badge'
1519import AppLayout from ' @/layouts/AppLayout.vue' ;
16- import { dashboard } from ' @/routes' ;
20+ import { dashboard , chat } from ' @/routes' ;
1721import { show } from ' @/actions/App/Http/Controllers/FileController' ;
1822import { type BreadcrumbItem } from ' @/types' ;
1923import { Check , Circle , Dot } from ' lucide-vue-next' ;
2024import { Button } from ' @/components/ui/button' ;
2125import { Head , Form , Link } from ' @inertiajs/vue3' ;
22- import { Ellipsis , Trash } from " lucide-vue-next" ;
26+ import { Ellipsis , Trash , ArrowUpIcon } from " lucide-vue-next" ;
2327import { ScrollArea } from ' @/components/ui/scroll-area' ;
2428import store from ' @/actions/App/Http/Controllers/FileChatStoreController' ;
2529import chatDetails from ' @/actions/App/Http/Controllers/FileChatDetailsController' ;
@@ -40,11 +44,13 @@ const scrollAreaRef = ref<{ $el: HTMLElement } | null>(null);
4044
4145const chatContainerRef = ref <{ $el: HTMLElement } | null >(null );
4246
47+ const isTyping = ref (false );
48+
4349if (props .messages .data .length > 0 && props .messages .data [props .messages .data .length - 1 ].participant === ' user' ) {
4450 disabledButton .value = true ;
4551}
4652
47- const isReady = ref (false );
53+ const isReady = ref (true );
4854
4955const breadcrumbs: BreadcrumbItem [] = [
5056 {
@@ -64,11 +70,10 @@ const breadcrumbs: BreadcrumbItem[] = [
6470const scrollToBottom = async () => {
6571 await nextTick ();
6672 chatContainerRef .value ?.scrollIntoView ({ behavior: ' smooth' , block: ' end' });
67- isReady .value = true ;
68- console .log (' isready' );
6973}
7074
7175const handleSuccess = () => {
76+ isTyping .value = true ;
7277 scrollToBottom ();
7378}
7479
@@ -82,6 +87,7 @@ onMounted(() => {
8287 window .Echo .private (` message-created.${props .conversation .data .id } ` )
8388 .listen (' MessageCreated' , async (e ) => {
8489 disabledButton .value = false ;
90+ isTyping .value = false ;
8591 props .messages .data .push (e .message );
8692 await nextTick ();
8793 scrollToBottom ();
@@ -90,8 +96,6 @@ onMounted(() => {
9096
9197
9298onMounted (async () => {
93- isReady .value = false ;
94- console .log (' mounted' + isReady .value );
9599 await scrollToBottom ();
96100});
97101
@@ -109,7 +113,7 @@ onMounted(async () => {
109113 <div class =" w-[70%] h-full" >
110114 <div class =" h-full" >
111115 <div class =" relative h-full flex flex-col items-center justify-between" >
112- <ScrollArea v-show =" isReady" ref =" scrollAreaRef" class =" w-full rounded-md h-[400px ] p-2 mb-2" >
116+ <ScrollArea v-show =" isReady" ref =" scrollAreaRef" class =" w-full rounded-md h-[350px ] p-2 mb-2" >
113117 <div class =" space-y-2" >
114118 <div
115119 v-for =" message in messages.data"
@@ -122,6 +126,12 @@ onMounted(async () => {
122126 </p >
123127 </div >
124128 </div >
129+ <div v-if =" isTyping" class =" relative flex items-center space-x-2 px-2" >
130+ <span class =" flex h-3 w-3" >
131+ <span class =" animate-ping absolute inline-flex h-3 w-3 rounded-full bg-blue-400 opacity-75" ></span >
132+ <span class =" relative inline-flex rounded-full h-3 w-3 bg-blue-500" ></span >
133+ </span >
134+ </div >
125135 </div >
126136
127137 <div ref =" chatContainerRef" ></div >
@@ -140,7 +150,22 @@ onMounted(async () => {
140150 reset
141151 }"
142152 >
143- <textarea
153+ <InputGroup class =" w-full rounded-3xl" >
154+ <InputGroupTextarea name =" message" placeholder =" Type your message..." autofocus =" true" required />
155+ <InputGroupAddon align =" block-end" class =" flex justify-end" >
156+ <InputGroupButton
157+ variant =" default"
158+ class =" rounded-full"
159+ size =" icon-sm"
160+ type =" submit"
161+ :disabled =" processing || disabledButton"
162+ >
163+ <ArrowUpIcon class =" size-5" />
164+ <span class =" sr-only" >Send</span >
165+ </InputGroupButton >
166+ </InputGroupAddon >
167+ </InputGroup >
168+ <!-- <textarea
144169 rows="2"
145170 name="message"
146171 class="scrollbar-thin scrollbar-rounded bg-zinc-900 w-full p-4 border border-gray-300 rounded-4xl resize-none focus:outline-none focus:ring-1 focus:ring-gray-500 transition-all"
@@ -166,17 +191,28 @@ onMounted(async () => {
166191 d="M17 8l4 4m0 0l-4 4m4-4H3"
167192 />
168193 </svg>
169- </Button >
194+ </Button> -->
170195 </Form >
171196 </div >
172197 </div >
173198 </div >
174199 <div class =" w-1/3 h-full border rounded-lg py-4 px-2" >
175- <h1 class =" text-lg font-bold text-center" >Chat History</h1 >
200+ <div class =" flex justify-between items-center" >
201+ <h1 class =" text-lg font-bold text-center" >Chat History</h1 >
202+
203+ <div >
204+ <Link
205+ :href =" chat.url(file?.data.uuid)"
206+ class =" w-full"
207+ >
208+ <Button class =" cursor-pointer" >New Chat</Button >
209+ </Link >
210+ </div >
211+ </div >
176212 <ScrollArea class =" h-[90%] w-full rounded-md" >
177213 <div class =" mt-2 space-y-2" >
178214 <Link
179- v-for =" conversationData in conversations.data" :key =" conversation .id"
215+ v-for =" conversationData in conversations.data" :key =" conversationData .id"
180216 :href =" chatDetails.url({ file: file.data.uuid, conversation: conversationData.uuid })"
181217 class =" block mt-3"
182218 >
0 commit comments