146 lines
4.5 KiB
TypeScript
146 lines
4.5 KiB
TypeScript
/**
|
||
* Модальное окно для создания нового чата
|
||
*/
|
||
|
||
'use client';
|
||
|
||
import React, { useState, useEffect } from 'react';
|
||
import {
|
||
Box,
|
||
Typography,
|
||
TextField,
|
||
List,
|
||
ListItemButton,
|
||
ListItemText,
|
||
Avatar,
|
||
Dialog,
|
||
DialogTitle,
|
||
DialogContent,
|
||
IconButton,
|
||
CircularProgress
|
||
} from '@mui/material';
|
||
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||
import apiClient from '@/lib/api-client';
|
||
import { createChat } from '@/api/chat';
|
||
import { LoadingSpinner } from '../common/LoadingSpinner';
|
||
|
||
interface User {
|
||
id: number;
|
||
email: string;
|
||
first_name?: string;
|
||
last_name?: string;
|
||
avatar_url?: string | null;
|
||
role: string;
|
||
}
|
||
|
||
interface NewChatModalProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
onChatCreated: (chat: any) => void;
|
||
}
|
||
|
||
export function NewChatModal({ isOpen, onClose, onChatCreated }: NewChatModalProps) {
|
||
const [search, setSearch] = useState('');
|
||
const [users, setUsers] = useState<User[]>([]);
|
||
const [loading, setLoading] = useState(false);
|
||
const [creating, setCreating] = useState(false);
|
||
|
||
useEffect(() => {
|
||
if (isOpen) {
|
||
loadContacts();
|
||
}
|
||
}, [isOpen, search]);
|
||
|
||
const loadContacts = async () => {
|
||
setLoading(true);
|
||
try {
|
||
const response = await apiClient.get<any>(`/users/contacts/?search=${encodeURIComponent(search)}`);
|
||
setUsers(response.data.results || response.data || []);
|
||
} catch (error) {
|
||
console.error('Failed to load contacts:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleCreateChat = async (userId: number) => {
|
||
setCreating(userId as any);
|
||
try {
|
||
const response = await apiClient.post<any>('/chat/chats/create_direct/', { user_id: userId });
|
||
const chat = response.data.data || response.data;
|
||
onChatCreated(chat);
|
||
onClose();
|
||
} catch (error) {
|
||
console.error('Failed to create chat:', error);
|
||
} finally {
|
||
setCreating(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<Dialog
|
||
open={isOpen}
|
||
onClose={onClose}
|
||
fullWidth
|
||
maxWidth="xs"
|
||
PaperProps={{
|
||
sx: {
|
||
borderRadius: '24px',
|
||
background: 'var(--md-sys-color-surface)',
|
||
boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
|
||
}
|
||
}}
|
||
>
|
||
<DialogTitle sx={{ m: 0, p: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||
<Typography variant="h6" component="span" sx={{ fontWeight: 700 }}>Новый чат</Typography>
|
||
<IconButton onClick={onClose} size="small">
|
||
<CloseRoundedIcon />
|
||
</IconButton>
|
||
</DialogTitle>
|
||
<DialogContent sx={{ p: 2, pt: 0 }}>
|
||
<TextField
|
||
fullWidth
|
||
placeholder="Поиск контактов..."
|
||
value={search}
|
||
onChange={(e) => setSearch(e.target.value)}
|
||
size="small"
|
||
sx={{ mb: 2 }}
|
||
/>
|
||
|
||
{loading && users.length === 0 ? (
|
||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
||
<CircularProgress size={24} />
|
||
</Box>
|
||
) : users.length === 0 ? (
|
||
<Typography sx={{ textAlign: 'center', py: 4, color: 'text.secondary' }}>
|
||
Контакты не найдены
|
||
</Typography>
|
||
) : (
|
||
<List sx={{ pt: 0 }}>
|
||
{users.map((user) => (
|
||
<ListItemButton
|
||
key={user.id}
|
||
onClick={() => handleCreateChat(user.id)}
|
||
disabled={creating === (user.id as any)}
|
||
sx={{ borderRadius: '12px', mb: 0.5 }}
|
||
>
|
||
<Avatar
|
||
src={user.avatar_url || undefined}
|
||
sx={{ mr: 1.5, bgcolor: 'var(--md-sys-color-primary-container)', color: 'var(--md-sys-color-on-primary-container)' }}
|
||
>
|
||
{user.first_name?.[0] || user.email[0].toUpperCase()}
|
||
</Avatar>
|
||
<ListItemText
|
||
primary={`${user.first_name || ''} ${user.last_name || ''}`.trim() || user.email}
|
||
secondary={user.role === 'mentor' ? 'Ментор' : user.role === 'client' ? 'Студент' : 'Родитель'}
|
||
/>
|
||
{creating === (user.id as any) && <CircularProgress size={16} />}
|
||
</ListItemButton>
|
||
))}
|
||
</List>
|
||
)}
|
||
</DialogContent>
|
||
</Dialog>
|
||
);
|
||
}
|