Compare commits

...

4 Commits

Author SHA1 Message Date
Egor Pozharov
2c59f027ea remove address auto-parsing and always display address detail fields in DeliveryForm
Some checks are pending
Build and Push Docker Images / build-backend (push) Waiting to run
Build and Push Docker Images / build-frontend (push) Waiting to run
2026-04-17 16:43:16 +06:00
Egor Pozharov
b54cdb878d remove address auto-parsing and always display address detail fields in DeliveryForm
Some checks failed
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled
2026-04-17 16:42:37 +06:00
Egor Pozharov
57fd82c6dd update DeliveryCard to pair pickup locations with product names in a single section
Some checks failed
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled
2026-04-16 23:36:55 +06:00
Egor Pozharov
6647379abc fix watchtower config
Some checks failed
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled
2026-04-16 23:29:52 +06:00
4 changed files with 46 additions and 67 deletions

View File

@@ -54,8 +54,6 @@ services:
- WATCHTOWER_REVIVE_STOPPED=false
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# Уберите или исправьте config.json если registry не требует auth
# - /root/.docker/config.json:/config.json:ro
command: delivery-tracker-backend-1 delivery-tracker-frontend-1 --interval 60
networks:
- delivery-network

View File

@@ -1,5 +1,5 @@
import { memo } from 'react';
import { MapPin, Phone, Package, Store, Calendar, MessageSquare, CheckCircle2, Circle, CheckSquare, User, Wrench } from 'lucide-react';
import { MapPin, Phone, Store, Calendar, MessageSquare, CheckCircle2, Circle, CheckSquare, User, Wrench } from 'lucide-react';
import type { Delivery } from '../../types';
import { pickupLocationLabels } from '../../types';
import { StatusBadge } from './StatusBadge';
@@ -58,24 +58,23 @@ export const DeliveryCard = memo(({ delivery, onStatusChange, onEdit, onDelete }
<span className="text-[#1b1b1d] font-medium">{delivery.date}</span>
</div>
{/* Pickup locations */}
<div className="flex items-center gap-2 text-sm">
<Store size={16} className="text-[#75777d]" />
<span className="text-[#1b1b1d]">
{delivery.pickupLocation2
? `${pickupLocationLabels[delivery.pickupLocation]} + ${pickupLocationLabels[delivery.pickupLocation2]}`
: pickupLocationLabels[delivery.pickupLocation]}
</span>
</div>
{delivery.pickupLocation2 && delivery.productName2 && (
<div className="flex items-start gap-2 text-sm pl-6">
<span className="text-[#75777d] text-xs">Со 2-й точки: {delivery.productName2}</span>
{/* Pickup locations paired with products */}
<div className="flex items-start gap-2 text-sm">
<Store size={16} className="text-[#75777d] mt-0.5 shrink-0" />
<div className="flex flex-col gap-1 min-w-0">
<div className="flex items-baseline gap-2 flex-wrap">
<span className="text-[#1b1b1d] font-medium">{pickupLocationLabels[delivery.pickupLocation]}</span>
<span className="text-[#75777d]"></span>
<span className="text-[#1b1b1d]">{delivery.productName}</span>
</div>
{delivery.pickupLocation2 && (
<div className="flex items-baseline gap-2 flex-wrap">
<span className="text-[#1b1b1d] font-medium">{pickupLocationLabels[delivery.pickupLocation2]}</span>
<span className="text-[#75777d]"></span>
<span className="text-[#1b1b1d]">{delivery.productName2 || '—'}</span>
</div>
)}
</div>
)}
<div className="flex items-center gap-2 text-sm">
<Package size={16} className="text-[#75777d]" />
<span className="text-[#1b1b1d]">{delivery.productName}</span>
</div>
<button

View File

@@ -2,7 +2,6 @@ import { useState, useEffect, useCallback } from 'react';
import { Button, Input, Select, Modal } from '../ui';
import { pickupOptions } from '../../constants/pickup';
import { formatDateForInput, parseDateFromInput, getTodayFrontend } from '../../utils/date';
import { parseAddress } from '../../utils/addressParser';
import type { Delivery, PickupLocation, DeliveryStatus } from '../../types';
interface DeliveryFormProps {
@@ -17,6 +16,23 @@ interface DeliveryFormProps {
// Phone validation regex for Kazakhstan numbers
const PHONE_REGEX = /^\+7\s?\(?\d{3}\)?\s?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/;
// City is not shown in UI but is included in the saved address (used for 2GIS search).
const CITY_LABEL = 'Кокшетау';
const buildAddressString = (
street: string,
house: string,
apartment: string,
entrance: string,
): string => {
const parts: string[] = [CITY_LABEL];
if (street) parts.push(`ул. ${street}`);
if (house) parts.push(`д. ${house}`);
if (apartment) parts.push(`кв. ${apartment}`);
if (entrance) parts.push(`подъезд ${entrance}`);
return parts.join(', ');
};
export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDate, isSubmitting }: DeliveryFormProps) => {
const [formData, setFormData] = useState({
date: defaultDate || getTodayFrontend(),
@@ -39,7 +55,6 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa
status: 'new' as DeliveryStatus,
});
const [showSecondPickup, setShowSecondPickup] = useState(false);
const [showAddressDetails, setShowAddressDetails] = useState(false);
useEffect(() => {
if (initialData) {
@@ -76,13 +91,17 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa
const isPhoneValid = !formData.phone || validatePhone(formData.phone);
const isAdditionalPhoneValid = !formData.additionalPhone || validatePhone(formData.additionalPhone);
const isFormValid = formData.productName && formData.address && formData.phone && isPhoneValid && formData.customerName && formData.street && formData.house;
const isFormValid = formData.productName && formData.phone && isPhoneValid && formData.customerName && formData.street && formData.house;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!isFormValid) return;
try {
await onSubmit(formData);
const payload = {
...formData,
address: buildAddressString(formData.street, formData.house, formData.apartment, formData.entrance),
};
await onSubmit(payload);
if (!initialData) {
setFormData({
date: defaultDate || getTodayFrontend(),
@@ -105,7 +124,6 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa
status: 'new',
});
setShowSecondPickup(false);
setShowAddressDetails(false);
}
onClose();
} catch {
@@ -159,44 +177,9 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa
required
/>
{/* Address with auto-parse */}
<div>
<label className="block text-sm font-medium text-[#1b1b1d] mb-1">
Адрес доставки
</label>
<input
type="text"
value={formData.address}
onChange={(e) => {
const newAddress = e.target.value;
const parsed = parseAddress(newAddress);
setFormData({
...formData,
address: newAddress,
street: parsed.street || formData.street,
house: parsed.house || formData.house,
apartment: parsed.apartment || formData.apartment,
entrance: parsed.entrance || formData.entrance,
floor: parsed.floor || formData.floor,
});
if (parsed.street || parsed.house) {
setShowAddressDetails(true);
}
}}
onBlur={() => setShowAddressDetails(true)}
placeholder="ул. Абая, д. 15, кв. 45, подъезд 3, этаж 5"
className="w-full px-3 py-2 bg-[#f5f3f5] border border-[#c5c6cd] rounded-md text-[#1b1b1d] focus:outline-none focus:ring-2 focus:ring-[#1B263B] focus:border-transparent transition-colors"
required
/>
<p className="text-xs text-[#75777d] mt-1">
Улица, дом, квартира, подъезд, этаж
</p>
</div>
{/* Parsed address details */}
{showAddressDetails && (
<div className="bg-[#f5f3f5] rounded-lg p-4 space-y-3">
<p className="text-sm font-medium text-[#1b1b1d]">Проверьте распознанные данные:</p>
{/* Address fields */}
<div className="bg-[#f5f3f5] rounded-lg p-4 space-y-3">
<p className="text-sm font-medium text-[#1b1b1d]">Адрес доставки</p>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs text-[#75777d] mb-1">Улица *</label>
@@ -246,8 +229,7 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa
/>
</div>
</div>
</div>
)}
</div>
<Input
label="ФИО клиента *"
@@ -327,7 +309,7 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa
options={pickupOptions}
/>
<Input
label="Что забрать со второй точки"
label="Название товара 2"
value={formData.productName2}
onChange={(e) => setFormData({ ...formData, productName2: e.target.value })}
placeholder="Название товара со второй точки"

View File

@@ -29,7 +29,7 @@ export default defineConfig({
allowedHosts: ['delivery.loca.lt', '.loca.lt'],
proxy: {
'/api': {
target: 'http://192.168.3.96:8080',
target: 'http://localhost:8080',
changeOrigin: true,
},
},