mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- Remove nested git repository from bugulma/frontend/.git - Add all frontend files to main repository tracking - Convert from separate frontend/backend repos to unified monorepo - Preserve all frontend code and development history as tracked files - Eliminate nested repository complexity for simpler development workflow This creates a proper monorepo structure with frontend and backend coexisting in the same repository for easier development and deployment.
130 lines
4.0 KiB
TypeScript
130 lines
4.0 KiB
TypeScript
import React, { useCallback } from 'react';
|
|
import { useTranslation } from '@/hooks/useI18n.tsx';
|
|
import { Mic, Paperclip, Send, X } from 'lucide-react';
|
|
import Button from '@/components/ui/Button.tsx';
|
|
|
|
interface ChatInputProps {
|
|
inputValue: string;
|
|
onInputChange: (value: string) => void;
|
|
onSendMessage: () => void;
|
|
attachedImage: { previewUrl: string } | null;
|
|
onClearAttachment: () => void;
|
|
onFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
fileInputRef: React.RefObject<HTMLInputElement>;
|
|
inputRef: React.RefObject<HTMLInputElement>;
|
|
isListening: boolean;
|
|
onStartListening: () => void;
|
|
onStopListening: () => void;
|
|
isSpeechSupported: boolean;
|
|
isLoading: boolean;
|
|
}
|
|
|
|
const ChatInput = ({
|
|
inputValue,
|
|
onInputChange,
|
|
onSendMessage,
|
|
attachedImage,
|
|
onClearAttachment,
|
|
onFileChange,
|
|
fileInputRef,
|
|
inputRef,
|
|
isListening,
|
|
onStartListening,
|
|
onStopListening,
|
|
isSpeechSupported,
|
|
isLoading,
|
|
}: ChatInputProps) => {
|
|
const { t } = useTranslation();
|
|
|
|
const handleSubmit = useCallback(
|
|
(e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
onSendMessage();
|
|
},
|
|
[onSendMessage]
|
|
);
|
|
|
|
return (
|
|
<div className="p-3 border-t shrink-0 space-y-2">
|
|
{attachedImage && (
|
|
<div className="relative w-20 h-20 rounded-lg overflow-hidden border">
|
|
<img
|
|
src={attachedImage.previewUrl}
|
|
alt="Preview"
|
|
className="w-full h-full object-cover"
|
|
loading="eager"
|
|
decoding="sync"
|
|
/>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="absolute top-1 right-1 h-6 w-6 p-1 rounded-full bg-background/50 hover:bg-background/80"
|
|
onClick={onClearAttachment}
|
|
aria-label={t('chatbot.removeImageLabel')}
|
|
>
|
|
<X className="h-3 h-4 text-current w-3 w-4" />
|
|
</Button>
|
|
</div>
|
|
)}
|
|
<form onSubmit={handleSubmit} className="relative flex items-center gap-2">
|
|
<input
|
|
ref={fileInputRef}
|
|
type="file"
|
|
id="chatbot-file-input"
|
|
name="chatbot-file-input"
|
|
onChange={onFileChange}
|
|
className="hidden"
|
|
accept="image/*"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
className="p-2 h-9 w-9 shrink-0"
|
|
onClick={() => fileInputRef.current?.click()}
|
|
aria-label={t('chatbot.attachFileLabel')}
|
|
>
|
|
<Paperclip className="h-4 text-current w-4" />
|
|
</Button>
|
|
<input
|
|
ref={inputRef}
|
|
type="text"
|
|
id="chatbot-message-input"
|
|
name="chatbot-message-input"
|
|
value={inputValue}
|
|
onChange={(e) => onInputChange(e.target.value)}
|
|
placeholder={t('chatbot.placeholder')}
|
|
className="w-full h-10 rounded-md border bg-muted pl-4 pr-10 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
disabled={isLoading}
|
|
/>
|
|
<div className="flex items-center absolute right-2 top-1/2 -translate-y-1/2">
|
|
{isSpeechSupported && (
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
className="p-1.5"
|
|
onClick={isListening ? onStopListening : onStartListening}
|
|
aria-label={isListening ? t('chatbot.stopRecordLabel') : t('chatbot.recordLabel')}
|
|
>
|
|
<Mic className={`h-4 w-4 ${isListening ? 'text-destructive animate-pulse' : ''}`} />
|
|
</Button>
|
|
)}
|
|
<Button
|
|
type="submit"
|
|
variant="ghost"
|
|
size="sm"
|
|
disabled={isLoading || (!inputValue.trim() && !attachedImage)}
|
|
className="p-1.5 text-primary disabled:text-muted-foreground"
|
|
aria-label={t('chatbot.sendLabel')}
|
|
>
|
|
<Send className="h-4 text-current w-4" />
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default React.memo(ChatInput);
|