Добавлены настройки для удаленных изображений в конфигурацию Next.js. Удалены неиспользуемые заголовки и улучшен интерфейс компонентов панели управления и мессенджера, включая добавление новых заголовков с кнопками и сокращенных названий компаний. Оптимизированы стили и структура кода в компонентах.
This commit is contained in:
37
deploy.sh
Normal file
37
deploy.sh
Normal file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🚀 Starting deployment to new.sferav.ru..."
|
||||
|
||||
# Остановка предыдущей версии
|
||||
echo "⏹️ Stopping previous version..."
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
|
||||
# Очистка неиспользуемых образов
|
||||
echo "🧹 Cleaning up unused images..."
|
||||
docker image prune -f
|
||||
|
||||
# Сборка и запуск новой версии
|
||||
echo "🔨 Building and starting new version..."
|
||||
docker-compose -f docker-compose.prod.yml up -d --build
|
||||
|
||||
# Ожидание запуска
|
||||
echo "⏳ Waiting for application to start..."
|
||||
sleep 10
|
||||
|
||||
# Проверка здоровья
|
||||
echo "🏥 Checking application health..."
|
||||
for i in {1..30}; do
|
||||
if curl -f http://127.0.0.1:3017/api/health > /dev/null 2>&1; then
|
||||
echo "✅ Application is healthy!"
|
||||
break
|
||||
fi
|
||||
echo "⏳ Attempt $i/30 - waiting for health check..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Проверка статуса контейнера
|
||||
echo "📊 Container status:"
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
|
||||
echo "🎉 Deployment completed!"
|
||||
echo "🌐 Application is available at: https://new.sferav.ru"
|
36
docker-compose.prod.yml
Normal file
36
docker-compose.prod.yml
Normal file
@ -0,0 +1,36 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
- SMS_AERO_EMAIL=${SMS_AERO_EMAIL}
|
||||
- SMS_AERO_API_KEY=${SMS_AERO_API_KEY}
|
||||
- SMS_AERO_API_URL=${SMS_AERO_API_URL}
|
||||
- DADATA_API_KEY=${DADATA_API_KEY}
|
||||
- DADATA_API_URL=${DADATA_API_URL}
|
||||
- WILDBERRIES_API_URL=${WILDBERRIES_API_URL}
|
||||
- OZON_API_URL=${OZON_API_URL}
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
- SMS_DEV_MODE=${SMS_DEV_MODE}
|
||||
ports:
|
||||
- "127.0.0.1:3017:3000" # Привязка только к localhost
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
- HOSTNAME=0.0.0.0
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
|
||||
timeout: 10s
|
||||
interval: 30s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
# Логирование
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
@ -2,6 +2,16 @@ import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 's3.twcstorage.ru',
|
||||
port: '',
|
||||
pathname: '/**',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
@ -40,15 +40,6 @@ export function DashboardHome() {
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="p-8">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-white mb-2">
|
||||
Добро пожаловать!
|
||||
</h1>
|
||||
<p className="text-white/80">
|
||||
Главная панель управления {getCabinetType()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Информация об организации */}
|
||||
<Card className="bg-white/10 backdrop-blur border-white/20 p-6">
|
||||
|
@ -554,62 +554,6 @@ export function UserSettings() {
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
|
||||
<div className="h-full w-full flex flex-col">
|
||||
{/* Заголовок - фиксированная высота */}
|
||||
<div className="flex items-center justify-between mb-4 flex-shrink-0">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-white mb-1">Настройки профиля</h1>
|
||||
<p className="text-white/70 text-sm">Управление информацией о профиле и организации</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Компактный индикатор прогресса */}
|
||||
<div className="flex items-center gap-2 mr-2">
|
||||
<div className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center">
|
||||
<span className="text-xs text-white font-medium">{profileStatus.percentage}%</span>
|
||||
</div>
|
||||
<div className="hidden sm:block text-xs text-white/70">
|
||||
{isIncomplete ? (
|
||||
<>Заполнено {profileStatus.percentage}% профиля</>
|
||||
) : (
|
||||
<>Профиль полностью заполнен</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="glass-secondary text-white hover:text-white cursor-pointer"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="glass-button text-white cursor-pointer"
|
||||
>
|
||||
<Edit3 className="h-4 w-4 mr-2" />
|
||||
Редактировать
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Сообщения о сохранении */}
|
||||
{saveMessage && (
|
||||
<Alert className={`mb-4 ${saveMessage.type === 'success' ? 'border-green-500 bg-green-500/10' : 'border-red-500 bg-red-500/10'}`}>
|
||||
@ -658,6 +602,64 @@ export function UserSettings() {
|
||||
{/* Профиль пользователя */}
|
||||
<TabsContent value="profile" className="flex-1 overflow-hidden">
|
||||
<Card className="glass-card p-6 h-full overflow-auto">
|
||||
{/* Заголовок вкладки с прогрессом и кнопками */}
|
||||
<div className="flex items-center justify-between mb-6 pb-4 border-b border-white/10">
|
||||
<div className="flex items-center gap-4">
|
||||
<User className="h-6 w-6 text-purple-400" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-white">Профиль пользователя</h2>
|
||||
<p className="text-white/70 text-sm">Личная информация и контактные данные</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Компактный индикатор прогресса */}
|
||||
<div className="flex items-center gap-2 mr-2">
|
||||
<div className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center">
|
||||
<span className="text-xs text-white font-medium">{profileStatus.percentage}%</span>
|
||||
</div>
|
||||
<div className="hidden sm:block text-xs text-white/70">
|
||||
{isIncomplete ? (
|
||||
<>Заполнено {profileStatus.percentage}% профиля</>
|
||||
) : (
|
||||
<>Профиль полностью заполнен</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="glass-secondary text-white hover:text-white cursor-pointer"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="glass-button text-white cursor-pointer"
|
||||
>
|
||||
<Edit3 className="h-4 w-4 mr-2" />
|
||||
Редактировать
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div className="relative">
|
||||
<Avatar className="h-16 w-16">
|
||||
@ -831,12 +833,56 @@ export function UserSettings() {
|
||||
{/* Организация и юридические данные */}
|
||||
<TabsContent value="organization" className="flex-1 overflow-hidden">
|
||||
<Card className="glass-card p-6 h-full overflow-hidden">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Building2 className="h-5 w-5 text-blue-400" />
|
||||
<h3 className="text-lg font-semibold text-white">Организация и юридические данные</h3>
|
||||
{(formData.inn || user?.organization?.inn) && (
|
||||
<CheckCircle className="h-5 w-5 text-green-400 ml-auto" />
|
||||
)}
|
||||
{/* Заголовок вкладки с кнопками */}
|
||||
<div className="flex items-center justify-between mb-6 pb-4 border-b border-white/10">
|
||||
<div className="flex items-center gap-4">
|
||||
<Building2 className="h-6 w-6 text-blue-400" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-white">Данные организации</h2>
|
||||
<p className="text-white/70 text-sm">Юридическая информация и реквизиты</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{(formData.inn || user?.organization?.inn) && (
|
||||
<div className="flex items-center gap-2 mr-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-400" />
|
||||
<span className="text-green-400 text-sm">Проверено</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="glass-secondary text-white hover:text-white cursor-pointer"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="glass-button text-white cursor-pointer"
|
||||
>
|
||||
<Edit3 className="h-4 w-4 mr-2" />
|
||||
Редактировать
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Общая подпись про реестр */}
|
||||
@ -1010,12 +1056,56 @@ export function UserSettings() {
|
||||
{(user?.organization?.type === 'FULFILLMENT' || user?.organization?.type === 'LOGIST' || user?.organization?.type === 'WHOLESALE' || user?.organization?.type === 'SELLER') && (
|
||||
<TabsContent value="financial" className="flex-1 overflow-hidden">
|
||||
<Card className="glass-card p-6 h-full overflow-auto">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<CreditCard className="h-5 w-5 text-red-400" />
|
||||
<h3 className="text-lg font-semibold text-white">Финансовые данные</h3>
|
||||
{formData.bankName && formData.bik && formData.accountNumber && formData.corrAccount && (
|
||||
<CheckCircle className="h-5 w-5 text-green-400 ml-auto" />
|
||||
)}
|
||||
{/* Заголовок вкладки с кнопками */}
|
||||
<div className="flex items-center justify-between mb-6 pb-4 border-b border-white/10">
|
||||
<div className="flex items-center gap-4">
|
||||
<CreditCard className="h-6 w-6 text-red-400" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-white">Финансовые данные</h2>
|
||||
<p className="text-white/70 text-sm">Банковские реквизиты для расчетов</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{formData.bankName && formData.bik && formData.accountNumber && formData.corrAccount && (
|
||||
<div className="flex items-center gap-2 mr-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-400" />
|
||||
<span className="text-green-400 text-sm">Заполнено</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="glass-secondary text-white hover:text-white cursor-pointer"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="glass-button text-white cursor-pointer"
|
||||
>
|
||||
<Edit3 className="h-4 w-4 mr-2" />
|
||||
Редактировать
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
@ -1073,12 +1163,56 @@ export function UserSettings() {
|
||||
{user?.organization?.type === 'SELLER' && (
|
||||
<TabsContent value="api" className="flex-1 overflow-hidden">
|
||||
<Card className="glass-card p-6 h-full overflow-auto">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Key className="h-5 w-5 text-green-400" />
|
||||
<h3 className="text-lg font-semibold text-white">API ключи маркетплейсов</h3>
|
||||
{user?.organization?.apiKeys?.length > 0 && (
|
||||
<CheckCircle className="h-5 w-5 text-green-400 ml-auto" />
|
||||
)}
|
||||
{/* Заголовок вкладки с кнопками */}
|
||||
<div className="flex items-center justify-between mb-6 pb-4 border-b border-white/10">
|
||||
<div className="flex items-center gap-4">
|
||||
<Key className="h-6 w-6 text-green-400" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-white">API ключи маркетплейсов</h2>
|
||||
<p className="text-white/70 text-sm">Интеграция с торговыми площадками</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{user?.organization?.apiKeys?.length > 0 && (
|
||||
<div className="flex items-center gap-2 mr-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-400" />
|
||||
<span className="text-green-400 text-sm">Настроено</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="glass-secondary text-white hover:text-white cursor-pointer"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="glass-button text-white cursor-pointer"
|
||||
>
|
||||
<Edit3 className="h-4 w-4 mr-2" />
|
||||
Редактировать
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
@ -1123,9 +1257,15 @@ export function UserSettings() {
|
||||
{/* Инструменты */}
|
||||
<TabsContent value="tools" className="flex-1 overflow-hidden">
|
||||
<Card className="glass-card p-6 h-full overflow-auto">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Key className="h-5 w-5 text-green-400" />
|
||||
<h3 className="text-lg font-semibold text-white">Инструменты</h3>
|
||||
{/* Заголовок вкладки */}
|
||||
<div className="flex items-center justify-between mb-6 pb-4 border-b border-white/10">
|
||||
<div className="flex items-center gap-4">
|
||||
<Settings className="h-6 w-6 text-green-400" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-white">Инструменты</h2>
|
||||
<p className="text-white/70 text-sm">Дополнительные возможности для бизнеса</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(user?.organization?.type === 'FULFILLMENT' || user?.organization?.type === 'LOGIST' || user?.organization?.type === 'WHOLESALE') && (
|
||||
|
@ -15,14 +15,6 @@ export function MarketDashboard() {
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
|
||||
<div className="h-full w-full flex flex-col">
|
||||
{/* Заголовок - фиксированная высота */}
|
||||
<div className="flex items-center justify-between mb-4 flex-shrink-0">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-white mb-1">Маркет</h1>
|
||||
<p className="text-white/70 text-sm">Управление контрагентами и поиск партнеров</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Основной контент с табами */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Tabs defaultValue="counterparties" className="h-full flex flex-col">
|
||||
|
@ -122,6 +122,37 @@ export function MessengerChat({ counterparty }: MessengerChatProps) {
|
||||
return name.charAt(0).toUpperCase()
|
||||
}
|
||||
|
||||
const getShortCompanyName = (fullName: string) => {
|
||||
if (!fullName) return 'Полное название не указано'
|
||||
|
||||
// Словарь для замены полных форм на сокращенные
|
||||
const legalFormReplacements: { [key: string]: string } = {
|
||||
'Общество с ограниченной ответственностью': 'ООО',
|
||||
'Открытое акционерное общество': 'ОАО',
|
||||
'Закрытое акционерное общество': 'ЗАО',
|
||||
'Публичное акционерное общество': 'ПАО',
|
||||
'Непубличное акционерное общество': 'НАО',
|
||||
'Акционерное общество': 'АО',
|
||||
'Индивидуальный предприниматель': 'ИП',
|
||||
'Товарищество с ограниченной ответственностью': 'ТОО',
|
||||
'Частное предприятие': 'ЧП',
|
||||
'Субъект предпринимательской деятельности': 'СПД'
|
||||
}
|
||||
|
||||
let result = fullName
|
||||
|
||||
// Заменяем полные формы на сокращенные
|
||||
for (const [fullForm, shortForm] of Object.entries(legalFormReplacements)) {
|
||||
const regex = new RegExp(`^${fullForm}\\s+`, 'i')
|
||||
if (regex.test(result)) {
|
||||
result = result.replace(regex, `${shortForm} `)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const getTypeLabel = (type: string) => {
|
||||
switch (type) {
|
||||
case 'FULFILLMENT':
|
||||
@ -273,17 +304,17 @@ export function MessengerChat({ counterparty }: MessengerChatProps) {
|
||||
</Avatar>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-medium">
|
||||
{getOrganizationName(counterparty)}
|
||||
</h3>
|
||||
<p className="text-white/60 text-sm mb-1">
|
||||
{getManagerName(counterparty)}
|
||||
</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="flex items-center space-x-3">
|
||||
<h3 className="text-white font-medium">
|
||||
{getOrganizationName(counterparty)}
|
||||
</h3>
|
||||
<Badge className={`${getTypeColor(counterparty.type)} text-xs`}>
|
||||
{getTypeLabel(counterparty.type)}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-white/60 text-sm">
|
||||
{getShortCompanyName(counterparty.fullName || '')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -48,6 +48,37 @@ export function MessengerConversations({
|
||||
return name.charAt(0).toUpperCase()
|
||||
}
|
||||
|
||||
const getShortCompanyName = (fullName: string) => {
|
||||
if (!fullName) return 'Полное название не указано'
|
||||
|
||||
// Словарь для замены полных форм на сокращенные
|
||||
const legalFormReplacements: { [key: string]: string } = {
|
||||
'Общество с ограниченной ответственностью': 'ООО',
|
||||
'Открытое акционерное общество': 'ОАО',
|
||||
'Закрытое акционерное общество': 'ЗАО',
|
||||
'Публичное акционерное общество': 'ПАО',
|
||||
'Непубличное акционерное общество': 'НАО',
|
||||
'Акционерное общество': 'АО',
|
||||
'Индивидуальный предприниматель': 'ИП',
|
||||
'Товарищество с ограниченной ответственностью': 'ТОО',
|
||||
'Частное предприятие': 'ЧП',
|
||||
'Субъект предпринимательской деятельности': 'СПД'
|
||||
}
|
||||
|
||||
let result = fullName
|
||||
|
||||
// Заменяем полные формы на сокращенные
|
||||
for (const [fullForm, shortForm] of Object.entries(legalFormReplacements)) {
|
||||
const regex = new RegExp(`^${fullForm}\\s+`, 'i')
|
||||
if (regex.test(result)) {
|
||||
result = result.replace(regex, `${shortForm} `)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const getTypeLabel = (type: string) => {
|
||||
switch (type) {
|
||||
case 'FULFILLMENT':
|
||||
@ -154,18 +185,17 @@ export function MessengerConversations({
|
||||
</Avatar>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-white font-medium text-sm leading-tight truncate mb-1">
|
||||
{getOrganizationName(org)}
|
||||
</h4>
|
||||
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<Badge className={`${getTypeColor(org.type)} text-xs`}>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h4 className="text-white font-medium text-sm leading-tight truncate">
|
||||
{getOrganizationName(org)}
|
||||
</h4>
|
||||
<Badge className={`${getTypeColor(org.type)} text-xs flex-shrink-0 ml-2`}>
|
||||
{getTypeLabel(org.type)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<p className="text-white/60 text-xs truncate">
|
||||
{getManagerName(org)}
|
||||
{getShortCompanyName(org.fullName || '')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,14 +65,6 @@ export function MessengerDashboard() {
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
|
||||
<div className="h-full w-full flex flex-col">
|
||||
{/* Заголовок - фиксированная высота */}
|
||||
<div className="flex items-center justify-between mb-4 flex-shrink-0">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-white mb-1">Мессенджер</h1>
|
||||
<p className="text-white/70 text-sm">Общение с контрагентами</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Основной контент - сетка из 2 колонок */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="grid grid-cols-[350px_1fr] gap-4 h-full">
|
||||
|
@ -12,14 +12,6 @@ export function ServicesDashboard() {
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
|
||||
<div className="h-full w-full flex flex-col">
|
||||
{/* Заголовок - фиксированная высота */}
|
||||
<div className="flex items-center justify-between mb-4 flex-shrink-0">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-white mb-1">Услуги</h1>
|
||||
<p className="text-white/70 text-sm">Управление услугами, расходниками и логистикой</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Основной контент с табами */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Tabs defaultValue="services" className="h-full flex flex-col">
|
||||
|
Reference in New Issue
Block a user