FriendlyMM/Genset-Maintenance-Assistant
0
1import React, { useRef, useEffect } from 'react';2import { Send, Bot, User, AlertTriangle, Loader2 } from 'lucide-react';3import ReactMarkdown from 'react-markdown';4import { Message, Language } from '../types';5 6interface ChatInterfaceProps {7 messages: Message[];8 inputValue: string;9 setInputValue: (val: string) => void;10 isLoading: boolean;11 onSend: (text?: string) => void;12 hasData: boolean;13 onOpenSidebar: () => void;14 language: Language;15}16 17const QUICK_ACTIONS_EN = [18 "Oil Change Interval",19 "Valve Lash Adjustment Interval",20 "Major Overhaul Interval",21 "Coolant Change Interval",22 "Next Maintenance Service",23 "Recommended Load Factor",24 "Contact Information"25];26 27const QUICK_ACTIONS_MM = [28 "အင်ဂျင်ဝိုင် လဲလှယ်ရန် သတ်မှတ်ချိန်",29 "Valve Lash ချိန်ညှိရန် သတ်မှတ်ချိန်",30 "Major Overhaul ပြုလုပ်ရန်အချိန်",31 "Coolant လဲလှယ်ရန် သတ်မှတ်ချိန်",32 "နောက်ထပ် ပြုလုပ်ရမည့် Maintenance",33 "သတ်မှတ် Load Factor",34 "ဆက်သွယ်ရန် လိပ်စာ"35];36 37export const ChatInterface: React.FC<ChatInterfaceProps> = ({38 messages,39 inputValue,40 setInputValue,41 isLoading,42 onSend,43 hasData,44 onOpenSidebar,45 language46}) => {47 const messagesEndRef = useRef<HTMLDivElement>(null);48 49 const scrollToBottom = () => {50 messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });51 };52 53 useEffect(() => {54 scrollToBottom();55 }, [messages]);56 57 const handleKeyDown = (e: React.KeyboardEvent) => {58 if (e.key === 'Enter' && !e.shiftKey) {59 e.preventDefault();60 onSend();61 }62 };63 64 const quickActions = language === 'mm' ? QUICK_ACTIONS_MM : QUICK_ACTIONS_EN;65 const placeholderText = language === 'mm' 66 ? (!hasData ? "ကျေးဇူးပြု၍ Schedule CSV ဖိုင် အရင်တင်ပါ..." : "ပြုပြင်ထိန်းသိမ်းမှုနှင့် ပတ်သက်၍ မေးမြန်းနိုင်ပါသည်...")67 : (!hasData ? "Upload schedule first..." : "Ask a question...");68 const missingDataText = language === 'mm' ? "အချက်အလက် မရှိပါ" : "Data missing";69 70 return (71 <div className="flex-1 flex flex-col h-full bg-industrial-950 relative w-full">72 {/* Mobile Sidebar Toggle - Only visible when sidebar is hidden on small screens */}73 <div className="lg:hidden p-4 bg-industrial-900 border-b border-industrial-700 flex justify-between items-center shrink-0">74 <h1 className="text-lg font-bold text-white">Genset Assistant</h1>75 <button onClick={onOpenSidebar} className="text-action-500 font-medium text-sm px-2 py-1 hover:bg-industrial-800 rounded">76 Settings77 </button>78 </div>79 80 {/* Messages Area */}81 <div className="flex-1 overflow-y-auto p-4 md:p-6 space-y-6 scroll-smooth">82 {messages.length === 0 && (83 <div className="h-full flex flex-col items-center justify-center text-center opacity-60 p-4">84 <div className="bg-industrial-800 p-6 rounded-full mb-6">85 <Bot className="w-12 h-12 md:w-16 md:h-16 text-action-500" />86 </div>87 <h1 className="text-xl md:text-2xl font-bold text-white mb-2">Maintenance Assistant</h1>88 <p className="text-industrial-400 text-sm md:text-base max-w-md mx-auto">89 {!hasData 90 ? (language === 'mm' ? "ဆက်လက်လုပ်ဆောင်ရန်အတွက် ကျေးဇူးပြု၍ Maintenance Schedule CSV ဖိုင်ကို Settings တွင် Upload ပြုလုပ်ပေးပါ။" : "Please upload your maintenance schedule CSV in the settings to get started.")91 : (language === 'mm' ? "အင်ဂျင်ဝိုင်၊ Overhaul ကာလ သို့မဟုတ် အခြား ပြုပြင်ထိန်းသိမ်းမှုဆိုင်ရာများကို မေးမြန်းနိုင်ပါသည်။" : "Ask me about oil changes, overhaul intervals, or any service requirements for your genset.")}92 </p>93 {!hasData && (94 <button 95 onClick={onOpenSidebar}96 className="mt-6 px-6 py-2 bg-action-600 hover:bg-action-500 text-white rounded-md font-medium transition-colors"97 >98 {language === 'mm' ? "Schedule ဖိုင်တင်မည်" : "Upload Schedule"}99 </button>100 )}101 </div>102 )}103 104 {messages.map((msg) => (105 <div106 key={msg.id}107 className={`flex gap-3 md:gap-4 ${msg.role === 'user' ? 'flex-row-reverse' : 'flex-row'}`}108 >109 <div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${110 msg.role === 'user' ? 'bg-industrial-700' : 'bg-action-600'111 }`}>112 {msg.role === 'user' ? <User className="w-5 h-5 text-white" /> : <Bot className="w-5 h-5 text-white" />}113 </div>114 115 <div className={`flex flex-col max-w-[85%] md:max-w-[75%] ${msg.role === 'user' ? 'items-end' : 'items-start'}`}>116 <div117 className={`px-4 py-3 rounded-2xl text-sm md:text-base leading-relaxed shadow-sm ${118 msg.role === 'user'119 ? 'bg-industrial-700 text-white rounded-tr-none'120 : 'bg-industrial-800 text-industrial-100 rounded-tl-none border border-industrial-700'121 }`}122 >123 {msg.role === 'user' ? (124 msg.text125 ) : (126 <div className="prose prose-invert prose-sm md:prose-base max-w-none prose-a:text-action-500 prose-a:no-underline hover:prose-a:underline font-normal">127 <ReactMarkdown>{msg.text}</ReactMarkdown>128 </div>129 )}130 </div>131 <span className="text-[10px] md:text-xs text-industrial-500 mt-1 px-1">132 {msg.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}133 </span>134 </div>135 </div>136 ))}137 {isLoading && (138 <div className="flex gap-4">139 <div className="w-8 h-8 rounded-full bg-action-600 flex items-center justify-center flex-shrink-0">140 <Bot className="w-5 h-5 text-white" />141 </div>142 <div className="bg-industrial-800 px-5 py-3 rounded-2xl rounded-tl-none border border-industrial-700 flex items-center">143 <Loader2 className="w-5 h-5 text-action-500 animate-spin mr-2" />144 <span className="text-industrial-400 text-sm">145 {language === 'mm' ? "ရှာဖွေနေပါသည်..." : "Analyzing schedule..."}146 </span>147 </div>148 </div>149 )}150 <div ref={messagesEndRef} />151 </div>152 153 {/* Input Area */}154 <div className="p-3 md:p-6 bg-industrial-900 border-t border-industrial-800 shrink-0">155 156 {/* Quick Actions */}157 <div className="max-w-4xl mx-auto mb-3 overflow-x-auto flex gap-2 pb-1 px-1 -mx-1 mask-linear-fade scrollbar-hide">158 {quickActions.map((action) => (159 <button160 key={action}161 onClick={() => onSend(action)}162 disabled={isLoading}163 className="flex-shrink-0 px-3 py-1.5 md:px-4 md:py-2 bg-industrial-800 hover:bg-industrial-700 text-industrial-200 text-xs md:text-sm border border-industrial-700 rounded-full transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed hover:border-action-500/50"164 >165 {action}166 </button>167 ))}168 </div>169 170 <div className="max-w-4xl mx-auto relative flex items-center">171 <input172 type="text"173 value={inputValue}174 onChange={(e) => setInputValue(e.target.value)}175 onKeyDown={handleKeyDown}176 disabled={isLoading}177 placeholder={placeholderText}178 className="w-full bg-industrial-950 text-white placeholder-industrial-500 border border-industrial-700 rounded-xl py-3 pl-4 pr-12 md:py-4 md:pl-5 md:pr-14 text-base md:text-sm focus:outline-none focus:ring-2 focus:ring-action-500 focus:border-transparent transition-all disabled:opacity-50 disabled:cursor-not-allowed"179 />180 <button181 onClick={() => onSend()}182 disabled={!inputValue.trim() || isLoading}183 className="absolute right-2 md:right-3 p-1.5 md:p-2 bg-action-600 hover:bg-action-500 text-white rounded-lg transition-colors disabled:opacity-50 disabled:bg-industrial-700"184 >185 <Send className="w-5 h-5" />186 </button>187 </div>188 {!hasData && (189 <div className="max-w-4xl mx-auto mt-2 flex justify-center">190 <span className="text-xs text-safety-500 flex items-center gap-1">191 <AlertTriangle className="w-3 h-3" />192 {missingDataText}193 </span>194 </div>195 )}196 </div>197 </div>198 );199};