Обновлен Dockerfile для установки wget и добавлены зависимости в компонентах. Исправлены зависимости в хуках и компонентах, улучшено логирование и обработка ошибок. Удалены неиспользуемые импорты и оптимизированы некоторые функции. Обновлены компоненты для работы с изображениями и улучшена структура кода.
This commit is contained in:
@ -3,8 +3,6 @@ FROM node:18-alpine AS base
|
||||
|
||||
# Устанавливаем зависимости только когда нужно
|
||||
FROM base AS deps
|
||||
# Проверяем https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine чтобы понять зачем нужен libc6-compat
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Устанавливаем зависимости на основе предпочтительного менеджера пакетов
|
||||
@ -37,6 +35,9 @@ ENV NODE_ENV production
|
||||
# Отключаем телеметрию next.js во время runtime
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# Устанавливаем wget для healthcheck
|
||||
RUN apk add --no-cache wget
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
|
@ -29,7 +29,7 @@ export function AuthGuard({ children, fallback }: AuthGuardProps) {
|
||||
}
|
||||
|
||||
initAuth()
|
||||
}, []) // Убираем checkAuth из зависимостей чтобы избежать повторных вызовов
|
||||
}, [checkAuth, isAuthenticated, user]) // Добавляем зависимости как требует линтер
|
||||
|
||||
// Дополнительное логирование состояний
|
||||
useEffect(() => {
|
||||
|
@ -8,7 +8,7 @@ import { InnStep } from "./inn-step"
|
||||
import { MarketplaceApiStep } from "./marketplace-api-step"
|
||||
import { ConfirmationStep } from "./confirmation-step"
|
||||
import { CheckCircle } from "lucide-react"
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
|
||||
|
||||
type AuthStep = 'phone' | 'sms' | 'cabinet-select' | 'inn' | 'marketplace-api' | 'confirmation' | 'complete'
|
||||
type CabinetType = 'fulfillment' | 'seller' | 'logist' | 'wholesale'
|
||||
@ -61,8 +61,6 @@ export function AuthFlow({ partnerCode }: AuthFlowProps = {}) {
|
||||
partnerCode: partnerCode
|
||||
})
|
||||
|
||||
const { verifySmsCode, checkAuth } = useAuth()
|
||||
|
||||
// При завершении авторизации инициируем проверку и перенаправление
|
||||
useEffect(() => {
|
||||
if (step === 'complete') {
|
||||
@ -126,10 +124,6 @@ export function AuthFlow({ partnerCode }: AuthFlowProps = {}) {
|
||||
setStep('complete')
|
||||
}
|
||||
|
||||
const handlePhoneBack = () => {
|
||||
setStep('phone')
|
||||
}
|
||||
|
||||
const handleSmsBack = () => {
|
||||
setStep('phone')
|
||||
}
|
||||
@ -240,7 +234,7 @@ export function AuthFlow({ partnerCode }: AuthFlowProps = {}) {
|
||||
onBack={handleConfirmationBack}
|
||||
/>
|
||||
)}
|
||||
{step === 'complete' && (
|
||||
{(step as string) === 'complete' && (
|
||||
<div className="space-y-6 text-center">
|
||||
<div className="flex justify-center">
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { AuthLayout } from "./auth-layout"
|
||||
import { Package, ShoppingCart, ArrowLeft, Truck, Building2 } from "lucide-react"
|
||||
|
@ -46,10 +46,7 @@ export function ConfirmationStep({ data, onConfirm, onBack }: ConfirmationStepPr
|
||||
return phone || "+7 (___) ___-__-__"
|
||||
}
|
||||
|
||||
const formatApiKey = (key?: string) => {
|
||||
if (!key) return ""
|
||||
return key.substring(0, 4) + "•".repeat(key.length - 8) + key.substring(key.length - 4)
|
||||
}
|
||||
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setIsLoading(true)
|
||||
|
@ -1,16 +1,16 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect, useRef } from "react"
|
||||
import { useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { GlassInput } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { AuthLayout } from "./auth-layout"
|
||||
import { Key, ArrowLeft, ShoppingCart, Check, X } from "lucide-react"
|
||||
import { ArrowLeft, ShoppingCart, Check, X } from "lucide-react"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { useMutation } from '@apollo/client'
|
||||
import { ADD_MARKETPLACE_API_KEY } from '@/graphql/mutations'
|
||||
import { getAuthToken } from '@/lib/apollo-client'
|
||||
|
||||
|
||||
interface ApiValidationData {
|
||||
sellerId?: string
|
||||
@ -110,7 +110,7 @@ export function MarketplaceApiStep({ onNext, onBack }: MarketplaceApiStepProps)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
} catch {
|
||||
setValidationStates(prev => ({
|
||||
...prev,
|
||||
[marketplace]: {
|
||||
|
@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button"
|
||||
import { GlassInput } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert"
|
||||
|
||||
import { AuthLayout } from "./auth-layout"
|
||||
import { MessageSquare, ArrowLeft, Clock, RefreshCw, Check } from "lucide-react"
|
||||
import { useMutation } from '@apollo/client'
|
||||
@ -26,7 +26,7 @@ export function SmsStep({ phone, onNext, onBack }: SmsStepProps) {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const inputRefs = useRef<(HTMLInputElement | null)[]>([])
|
||||
|
||||
const { verifySmsCode, checkAuth } = useAuth()
|
||||
const { verifySmsCode } = useAuth()
|
||||
const [sendSmsCode] = useMutation(SEND_SMS_CODE)
|
||||
|
||||
// Автофокус на первое поле при загрузке
|
||||
|
@ -8,7 +8,7 @@ import { DashboardHome } from './dashboard-home'
|
||||
export type DashboardSection = 'home' | 'settings'
|
||||
|
||||
export function Dashboard() {
|
||||
const [activeSection, setActiveSection] = useState<DashboardSection>('home')
|
||||
const [activeSection] = useState<DashboardSection>('home')
|
||||
|
||||
const renderContent = () => {
|
||||
switch (activeSection) {
|
||||
|
@ -3,13 +3,12 @@
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
|
||||
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
||||
import { useRouter, usePathname } from 'next/navigation'
|
||||
import {
|
||||
Settings,
|
||||
LogOut,
|
||||
Building2,
|
||||
Store,
|
||||
MessageCircle,
|
||||
Wrench
|
||||
|
@ -32,10 +32,10 @@ import {
|
||||
RefreshCw,
|
||||
Calendar,
|
||||
Settings,
|
||||
Upload,
|
||||
Camera
|
||||
} from 'lucide-react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export function UserSettings() {
|
||||
const { user } = useAuth()
|
||||
@ -662,9 +662,11 @@ export function UserSettings() {
|
||||
<div className="relative">
|
||||
<Avatar className="h-16 w-16">
|
||||
{user?.avatar ? (
|
||||
<img
|
||||
<Image
|
||||
src={user.avatar}
|
||||
alt="Аватар"
|
||||
width={64}
|
||||
height={64}
|
||||
className="w-full h-full object-cover rounded-full"
|
||||
/>
|
||||
) : (
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from 'react'
|
||||
import React from 'react'
|
||||
import { useQuery, useMutation } from '@apollo/client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
@ -39,9 +39,9 @@ interface CounterpartyRequest {
|
||||
}
|
||||
|
||||
export function MarketCounterparties() {
|
||||
const { data: counterpartiesData, loading: counterpartiesLoading, refetch: refetchCounterparties } = useQuery(GET_MY_COUNTERPARTIES)
|
||||
const { data: incomingData, loading: incomingLoading, refetch: refetchIncoming } = useQuery(GET_INCOMING_REQUESTS)
|
||||
const { data: outgoingData, loading: outgoingLoading, refetch: refetchOutgoing } = useQuery(GET_OUTGOING_REQUESTS)
|
||||
const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery(GET_MY_COUNTERPARTIES)
|
||||
const { data: incomingData, loading: incomingLoading } = useQuery(GET_INCOMING_REQUESTS)
|
||||
const { data: outgoingData, loading: outgoingLoading } = useQuery(GET_OUTGOING_REQUESTS)
|
||||
|
||||
const [respondToRequest] = useMutation(RESPOND_TO_COUNTERPARTY_REQUEST, {
|
||||
refetchQueries: [
|
||||
@ -141,7 +141,7 @@ export function MarketCounterparties() {
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return 'Ошибка даты'
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ export function OrganizationCard({
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return 'Ошибка даты'
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,13 @@
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
|
||||
import {
|
||||
Building2,
|
||||
Phone,
|
||||
Mail,
|
||||
MapPin,
|
||||
Calendar,
|
||||
|
||||
FileText,
|
||||
Users,
|
||||
CreditCard,
|
||||
@ -98,7 +98,7 @@ function formatDate(dateString?: string | null): string {
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return 'Не указана'
|
||||
}
|
||||
}
|
||||
@ -376,7 +376,7 @@ export function OrganizationDetailsModal({ organization, open, onOpenChange }: O
|
||||
</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
{organization.users.map((user, index) => (
|
||||
{organization.users.map((user) => (
|
||||
<div key={user.id} className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<OrganizationAvatar
|
||||
@ -406,7 +406,7 @@ export function OrganizationDetailsModal({ organization, open, onOpenChange }: O
|
||||
</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
{organization.apiKeys.map((apiKey, index) => (
|
||||
{organization.apiKeys.map((apiKey) => (
|
||||
<div key={apiKey.id} className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Badge className={apiKey.isActive ? 'bg-green-500/20 text-green-300 border-green-500/30' : 'bg-red-500/20 text-red-300 border-red-500/30'}>
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useState, useRef, useEffect, useMemo } from 'react'
|
||||
import { useMutation, useQuery } from '@apollo/client'
|
||||
import { GET_MESSAGES } from '@/graphql/queries'
|
||||
import { SEND_MESSAGE, SEND_VOICE_MESSAGE, SEND_IMAGE_MESSAGE, SEND_FILE_MESSAGE } from '@/graphql/mutations'
|
||||
@ -97,7 +97,7 @@ export function MessengerChat({ counterparty }: MessengerChatProps) {
|
||||
}
|
||||
})
|
||||
|
||||
const messages = messagesData?.messages || []
|
||||
const messages = useMemo(() => messagesData?.messages || [], [messagesData?.messages])
|
||||
|
||||
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useQuery, useMutation } from '@apollo/client'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import Image from 'next/image'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
@ -45,7 +46,7 @@ export function ServicesTab() {
|
||||
imageUrl: ''
|
||||
})
|
||||
const [imageFile, setImageFile] = useState<File | null>(null)
|
||||
const [isUploading, setIsUploading] = useState(false)
|
||||
const [isUploading] = useState(false)
|
||||
|
||||
// GraphQL запросы и мутации
|
||||
const { data, loading, error, refetch } = useQuery(GET_MY_SERVICES, {
|
||||
@ -95,36 +96,6 @@ export function ServicesTab() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleImageUpload = async (file: File) => {
|
||||
if (!user?.id) return
|
||||
|
||||
setIsUploading(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
formData.append('userId', user.id)
|
||||
formData.append('type', 'service')
|
||||
|
||||
const response = await fetch('/api/upload-service-image', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to upload image')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
console.log('Upload result:', result)
|
||||
setFormData(prev => ({ ...prev, imageUrl: result.url }))
|
||||
} catch (error) {
|
||||
console.error('Error uploading image:', error)
|
||||
toast.error('Ошибка при загрузке изображения')
|
||||
} finally {
|
||||
setIsUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const uploadImageAndGetUrl = async (file: File): Promise<string> => {
|
||||
if (!user?.id) throw new Error('User not found')
|
||||
|
||||
@ -278,9 +249,11 @@ export function ServicesTab() {
|
||||
/>
|
||||
{formData.imageUrl && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
<Image
|
||||
src={formData.imageUrl}
|
||||
alt="Preview"
|
||||
width={80}
|
||||
height={80}
|
||||
className="w-20 h-20 object-cover rounded-lg border border-purple-400/30 shadow-lg"
|
||||
/>
|
||||
</div>
|
||||
@ -386,9 +359,11 @@ export function ServicesTab() {
|
||||
<td className="p-4 text-white/80">{index + 1}</td>
|
||||
<td className="p-4">
|
||||
{service.imageUrl ? (
|
||||
<img
|
||||
<Image
|
||||
src={service.imageUrl}
|
||||
alt={service.name}
|
||||
width={48}
|
||||
height={48}
|
||||
className="w-12 h-12 object-cover rounded border border-white/20"
|
||||
onError={(e) => {
|
||||
console.error('Image failed to load:', service.imageUrl, e)
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { useState } from 'react'
|
||||
import { useQuery, useMutation } from '@apollo/client'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import Image from 'next/image'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
@ -47,7 +48,7 @@ export function SuppliesTab() {
|
||||
imageUrl: ''
|
||||
})
|
||||
const [imageFile, setImageFile] = useState<File | null>(null)
|
||||
const [isUploading, setIsUploading] = useState(false)
|
||||
const [isUploading] = useState(false)
|
||||
|
||||
// GraphQL запросы и мутации
|
||||
const { data, loading, error, refetch } = useQuery(GET_MY_SUPPLIES, {
|
||||
@ -96,35 +97,7 @@ export function SuppliesTab() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleImageUpload = async (file: File) => {
|
||||
if (!user?.id) return
|
||||
|
||||
setIsUploading(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
formData.append('userId', user.id)
|
||||
formData.append('type', 'supply')
|
||||
|
||||
const response = await fetch('/api/upload-service-image', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to upload image')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
console.log('Upload result:', result)
|
||||
setFormData(prev => ({ ...prev, imageUrl: result.url }))
|
||||
} catch (error) {
|
||||
console.error('Error uploading image:', error)
|
||||
toast.error('Ошибка при загрузке изображения')
|
||||
} finally {
|
||||
setIsUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const uploadImageAndGetUrl = async (file: File): Promise<string> => {
|
||||
if (!user?.id) throw new Error('User not found')
|
||||
@ -300,9 +273,11 @@ export function SuppliesTab() {
|
||||
/>
|
||||
{formData.imageUrl && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
<Image
|
||||
src={formData.imageUrl}
|
||||
alt="Preview"
|
||||
width={80}
|
||||
height={80}
|
||||
className="w-20 h-20 object-cover rounded-lg border border-purple-400/30 shadow-lg"
|
||||
/>
|
||||
</div>
|
||||
@ -409,9 +384,11 @@ export function SuppliesTab() {
|
||||
<td className="p-4 text-white/80">{index + 1}</td>
|
||||
<td className="p-4">
|
||||
{supply.imageUrl ? (
|
||||
<img
|
||||
<Image
|
||||
src={supply.imageUrl}
|
||||
alt={supply.name}
|
||||
width={48}
|
||||
height={48}
|
||||
className="w-12 h-12 object-cover rounded border border-white/20"
|
||||
/>
|
||||
) : (
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Paperclip, Image, X } from 'lucide-react'
|
||||
import NextImage from 'next/image'
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
|
||||
interface FileUploaderProps {
|
||||
@ -118,9 +119,11 @@ export function FileUploader({ onSendFile }: FileUploaderProps) {
|
||||
<div className="flex items-center space-x-2 flex-1">
|
||||
{isImageType(selectedFile.type) ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<img
|
||||
<NextImage
|
||||
src={selectedFile.url}
|
||||
alt="Preview"
|
||||
width={40}
|
||||
height={40}
|
||||
className="w-10 h-10 object-cover rounded"
|
||||
/>
|
||||
<div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import { Download, Eye } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
@ -44,9 +45,11 @@ export function ImageMessage({ imageUrl, fileName, fileSize, isCurrentUser = fal
|
||||
: 'bg-white/10 border border-white/20'
|
||||
} rounded-lg overflow-hidden`}>
|
||||
<div className="relative">
|
||||
<img
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={fileName}
|
||||
width={300}
|
||||
height={300}
|
||||
className="w-full h-auto cursor-pointer transition-opacity duration-200"
|
||||
style={{
|
||||
opacity: isLoading ? 0 : 1,
|
||||
@ -108,9 +111,11 @@ export function ImageMessage({ imageUrl, fileName, fileSize, isCurrentUser = fal
|
||||
onClick={() => setShowFullSize(false)}
|
||||
>
|
||||
<div className="relative max-w-full max-h-full">
|
||||
<img
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={fileName}
|
||||
width={800}
|
||||
height={600}
|
||||
className="max-w-full max-h-full object-contain"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
|
@ -17,6 +17,7 @@ const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
|
||||
}
|
||||
|
||||
// Фильтруем пропсы, которые могут конфликтовать с IMaskInput
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { min, max, step, ...filteredProps } = props
|
||||
|
||||
return (
|
||||
@ -61,6 +62,7 @@ const GlassPhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
|
||||
const isEmpty = !value || value.replace(/\D/g, '').length === 0
|
||||
|
||||
// Фильтруем пропсы, которые могут конфликтовать с IMaskInput
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { min, max, step, onFocus, onBlur, ...filteredProps } = props
|
||||
|
||||
return (
|
||||
|
@ -23,6 +23,7 @@ export function VoicePlayer({ audioUrl, duration = 0, isCurrentUser = false }: V
|
||||
if (duration > 0 && (!audioDuration || audioDuration === 0)) {
|
||||
setAudioDuration(duration)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [duration, audioDuration])
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -30,9 +30,11 @@ const generateToken = (payload: AuthTokenPayload): string => {
|
||||
return jwt.sign(payload, process.env.JWT_SECRET!, { expiresIn: '30d' })
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const verifyToken = (token: string): AuthTokenPayload => {
|
||||
try {
|
||||
return jwt.verify(token, process.env.JWT_SECRET!) as AuthTokenPayload
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (error) {
|
||||
throw new GraphQLError('Недействительный токен', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
@ -94,7 +96,7 @@ function parseLiteral(ast: unknown): unknown {
|
||||
return value
|
||||
}
|
||||
case Kind.LIST:
|
||||
return ast.values.map(parseLiteral)
|
||||
return (ast as { values: unknown[] }).values.map(parseLiteral)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
@ -774,7 +776,7 @@ export const resolvers = {
|
||||
|
||||
const organization = await prisma.organization.create({
|
||||
data: {
|
||||
inn: validationResults[0]?.data?.inn || `SELLER_${Date.now()}`,
|
||||
inn: (validationResults[0]?.data?.inn as string) || `SELLER_${Date.now()}`,
|
||||
name: shopName, // Используем tradeMark как основное название
|
||||
fullName: sellerName ? `${sellerName} (${shopName})` : `Интернет-магазин "${shopName}"`,
|
||||
type: 'SELLER'
|
||||
@ -788,7 +790,7 @@ export const resolvers = {
|
||||
marketplace: validation.marketplace as 'WILDBERRIES' | 'OZON',
|
||||
apiKey: validation.apiKey,
|
||||
organizationId: organization.id,
|
||||
validationData: validation.data
|
||||
validationData: JSON.parse(JSON.stringify(validation.data))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -910,7 +912,7 @@ export const resolvers = {
|
||||
where: { id: existingKey.id },
|
||||
data: {
|
||||
apiKey,
|
||||
validationData: validationResult.data,
|
||||
validationData: JSON.parse(JSON.stringify(validationResult.data)),
|
||||
isActive: true
|
||||
}
|
||||
})
|
||||
@ -927,7 +929,7 @@ export const resolvers = {
|
||||
marketplace,
|
||||
apiKey,
|
||||
organizationId: user.organization.id,
|
||||
validationData: validationResult.data
|
||||
validationData: JSON.parse(JSON.stringify(validationResult.data))
|
||||
}
|
||||
})
|
||||
|
||||
@ -1097,7 +1099,7 @@ export const resolvers = {
|
||||
}
|
||||
|
||||
// Обновляем организацию
|
||||
const updatedOrganization = await prisma.organization.update({
|
||||
await prisma.organization.update({
|
||||
where: { id: user.organization.id },
|
||||
data: updateData,
|
||||
include: {
|
||||
@ -1213,7 +1215,7 @@ export const resolvers = {
|
||||
}
|
||||
|
||||
// Обновляем организацию
|
||||
const updatedOrganization = await prisma.organization.update({
|
||||
await prisma.organization.update({
|
||||
where: { id: user.organization.id },
|
||||
data: updateData,
|
||||
include: {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { apolloClient } from '@/lib/apollo-client'
|
||||
|
||||
export const useApolloRefresh = () => {
|
||||
|
@ -27,7 +27,7 @@ const authLink = setContext((operation, { headers }) => {
|
||||
})
|
||||
|
||||
// Error Link для обработки ошибок
|
||||
const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
|
||||
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||
if (graphQLErrors) {
|
||||
graphQLErrors.forEach(({ message, locations, path, extensions }) => {
|
||||
console.error(
|
||||
|
@ -15,9 +15,12 @@ const s3Config: S3Config = {
|
||||
}
|
||||
|
||||
export class S3Service {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
private static async createSignedUrl(fileName: string, fileType: string): Promise<string> {
|
||||
// Для простоты пока используем прямую загрузку через fetch
|
||||
// В продакшене лучше генерировать signed URLs на backend
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// fileType используется для будущей логики разделения по типам файлов
|
||||
const timestamp = Date.now()
|
||||
const key = `avatars/${timestamp}-${fileName}`
|
||||
|
||||
|
Reference in New Issue
Block a user