This commit is contained in:
dmisamohin 2025-06-06 18:16:12 +03:00
parent 80ef365a6b
commit f642d61d68
3 changed files with 63 additions and 310 deletions

View File

@ -1,7 +1,8 @@
'use client'
import { Breadcrumbs, IBreadcrumbProps } from '@/components/Breadcrumbs';
import { Input } from '@/components/Input';
import { PageLayout } from '@/components/PageLayout';
import { useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
const BREADCRUMBS: IBreadcrumbProps[] = [
{
@ -33,104 +34,55 @@ const BREADCRUMBS: IBreadcrumbProps[] = [
}
];
type ICreateContractorFormProps = {
name: string;
type: string;
inn: string;
kpp: string;
ogrn: string;
address: string;
phone: string;
email: string;
contactPerson: string;
account: string;
bank: string;
bik: string;
correspondentAccount: string;
};
const CREATE_CONTRACTOR_FORM_INIT: ICreateContractorFormProps = {
name: "",
type: "legal",
inn: "",
kpp: "",
ogrn: "",
address: "",
phone: "",
email: "",
contactPerson: "",
account: "",
bank: "",
bik: "",
correspondentAccount: "",
};
export default function CreateContractor() {
const [formData, setFormData] = useState({
name: '',
type: 'legal', // или 'individual'
inn: '',
kpp: '',
ogrn: '',
address: '',
phone: '',
email: '',
contactPerson: '',
bankDetails: {
account: '',
bank: '',
bik: '',
correspondentAccount: ''
}
});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const { register, handleSubmit } = useForm<ICreateContractorFormProps>({ defaultValues: CREATE_CONTRACTOR_FORM_INIT })
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target;
if (name.includes('.')) {
const [parent, child] = name.split('.');
// setFormData(prev => ({
// ...prev,
// [parent]: {
// ...prev[parent as keyof typeof formData],
// [child]: value
// }
// }));
} else {
setFormData(prev => ({
...prev,
[name]: value
}));
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError('');
try {
// Здесь будет запрос к вашему API
const response = await fetch('/api/contractors', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
if (!response.ok) {
throw new Error('Ошибка при создании контрагента');
}
const data = await response.json();
// router.push(`/contractors/${data.id}`);
} catch (err) {
setError(err instanceof Error ? err.message : 'Неизвестная ошибка');
} finally {
setIsLoading(false);
}
const onSubmit: SubmitHandler<ICreateContractorFormProps> = (form) => {
console.log("form", form)
};
return (
<PageLayout pageName='Добавление контрагента'>
<PageLayout pageName="Добавление контрагента">
<Breadcrumbs breadcrumbs={BREADCRUMBS} />
<div className="bg-white shadow rounded-lg p-6">
{error && <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">{error}</div>}
<form onSubmit={handleSubmit} className="space-y-6">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Основная информация */}
<div className="space-y-4">
<h2 className="text-xl font-semibold">Основная информация</h2>
<div>
<label
htmlFor="name"
className="block text-sm font-medium text-gray-700"
>
Наименование *
</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<Input id="name" type="text" label="Наименование *" {...register("name")} />
<div>
<label
@ -142,233 +94,46 @@ export default function CreateContractor() {
<select
id="type"
name="type"
value={formData.type}
onChange={handleChange}
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
disabled
>
<option value="legal">Юридическое лицо</option>
<option value="individual">Физическое лицо/ИП</option>
</select>
</div>
<div>
<label
htmlFor="inn"
className="block text-sm font-medium text-gray-700"
>
ИНН *
</label>
<input
type="text"
id="inn"
name="inn"
value={formData.inn}
onChange={handleChange}
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<Input id="inn" type="text" label="ИНН *" {...register("inn")} />
<Input id="kpp" type="text" label="КПП" {...register("kpp")} />
{formData.type === "legal" && (
<div>
<label
htmlFor="kpp"
className="block text-sm font-medium text-gray-700"
>
КПП
</label>
<input
type="text"
id="kpp"
name="kpp"
value={formData.kpp}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
)}
<div>
<label
htmlFor="ogrn"
className="block text-sm font-medium text-gray-700"
>
{formData.type === "legal" ? "ОГРН" : "ОГРНИП"}
</label>
<input
type="text"
id="ogrn"
name="ogrn"
value={formData.ogrn}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<Input id="ogrn" type="text" label="ОГРН" {...register("ogrn")} />
</div>
<div className="space-y-4">
<h2 className="text-xl font-semibold">Контактная информация</h2>
<div>
<label
htmlFor="address"
className="block text-sm font-medium text-gray-700"
>
Адрес
</label>
<input
type="text"
id="address"
name="address"
value={formData.address}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<div>
<label
htmlFor="phone"
className="block text-sm font-medium text-gray-700"
>
Телефон
</label>
<input
type="tel"
id="phone"
name="phone"
value={formData.phone}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700"
>
Email
</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<div>
<label
htmlFor="contactPerson"
className="block text-sm font-medium text-gray-700"
>
Контактное лицо
</label>
<input
type="text"
id="contactPerson"
name="contactPerson"
value={formData.contactPerson}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<Input id="address" type="text" label="Адрес" {...register("address")} />
<Input id="phone" type="tel" label='Телефон' {...register("phone")} />
<Input id="email" type="email" label='Эл. почта' {...register("email")} />
<Input id="contactPerson" type="text" label='Контактное лицо' {...register("contactPerson")} />
</div>
{/* Банковские реквизиты */}
<div className="space-y-4 md:col-span-2">
<h2 className="text-xl font-semibold">Банковские реквизиты</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="bankDetails.account"
className="block text-sm font-medium text-gray-700"
>
Расчетный счет
</label>
<input
type="text"
id="bankDetails.account"
name="bankDetails.account"
value={formData.bankDetails.account}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<div>
<label
htmlFor="bankDetails.bank"
className="block text-sm font-medium text-gray-700"
>
Банк
</label>
<input
type="text"
id="bankDetails.bank"
name="bankDetails.bank"
value={formData.bankDetails.bank}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<div>
<label
htmlFor="bankDetails.bik"
className="block text-sm font-medium text-gray-700"
>
БИК
</label>
<input
type="text"
id="bankDetails.bik"
name="bankDetails.bik"
value={formData.bankDetails.bik}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<div>
<label
htmlFor="bankDetails.correspondentAccount"
className="block text-sm font-medium text-gray-700"
>
Корр. счет
</label>
<input
type="text"
id="bankDetails.correspondentAccount"
name="bankDetails.correspondentAccount"
value={formData.bankDetails.correspondentAccount}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2"
/>
</div>
<Input id="account" label='Расчетный счет' type="text" {...register("account")} />
<Input id="bank" type="text" label="Банк" {...register("bank")} />
<Input id="bik" type="text" label='БИК' {...register("bik")} />
<Input id="correspondentAccount" type="text" label='Корр. счет' {...register("correspondentAccount")} />
</div>
</div>
</div>
<div className="flex justify-end space-x-3">
<button
type="button"
// onClick={() => router.push('/contractors')}
className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Отмена
<button type="button" className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Очистить
</button>
<button
type="submit"
disabled={isLoading}
className="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? "Сохранение..." : "Создать контрагента"}
<button type="submit" className="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed">
Создать контрагента
</button>
</div>
</form>

View File

@ -10,7 +10,7 @@ type IInputProps = {
export const Input = ({ label, isError, errorText, className, id, ...props }: IInputProps) => {
const inputClsx = twMerge(
classNames("w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1",
classNames("mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 border p-2",
{
"border-red-300 text-red-900 placeholder-red-300 focus:ring-red-500 focus:border-red-500": isError,
"border-gray-300 focus:ring-blue-500 focus:border-blue-500": !isError,

View File

@ -14,6 +14,7 @@ import {
flexRender,
RowData,
} from '@tanstack/react-table'
import { Input } from '../Input';
type TableProps<T extends RowData> = {
tableData: T[];
@ -162,16 +163,8 @@ export function Table<T extends RowData>({ tableData, columns }: TableProps<T>)
)
}
function Filter({
column,
table,
}: {
column: Column<any, any>;
table: TanStackTable<any>;
}) {
const firstValue = table
.getPreFilteredRowModel()
.flatRows[0]?.getValue(column.id);
function Filter({ column, table }: { column: Column<any, any>, table: TanStackTable<any> }) {
const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);
const columnFilterValue = column.getFilterValue();
@ -192,18 +185,13 @@ function Filter({
<input
type="number"
value={(columnFilterValue as [number, number])?.[1] ?? ""}
onChange={(e) =>
column.setFilterValue((old: [number, number]) => [
old?.[0],
e.target.value,
])
}
onChange={(e) => column.setFilterValue((old: [number, number]) => [old?.[0], e.target.value])}
placeholder={`Max`}
className="w-24 border shadow rounded"
/>
</div>
) : (
<input
<Input
type="text"
value={(columnFilterValue ?? "") as string}
onChange={(e) => column.setFilterValue(e.target.value)}