[fixed]
This commit is contained in:
parent
80ef365a6b
commit
f642d61d68
|
|
@ -1,7 +1,8 @@
|
||||||
'use client'
|
'use client'
|
||||||
import { Breadcrumbs, IBreadcrumbProps } from '@/components/Breadcrumbs';
|
import { Breadcrumbs, IBreadcrumbProps } from '@/components/Breadcrumbs';
|
||||||
|
import { Input } from '@/components/Input';
|
||||||
import { PageLayout } from '@/components/PageLayout';
|
import { PageLayout } from '@/components/PageLayout';
|
||||||
import { useState } from 'react';
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
const BREADCRUMBS: IBreadcrumbProps[] = [
|
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() {
|
export default function CreateContractor() {
|
||||||
const [formData, setFormData] = useState({
|
const { register, handleSubmit } = useForm<ICreateContractorFormProps>({ defaultValues: CREATE_CONTRACTOR_FORM_INIT })
|
||||||
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 handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
const onSubmit: SubmitHandler<ICreateContractorFormProps> = (form) => {
|
||||||
const { name, value } = e.target;
|
console.log("form", form)
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout pageName='Добавление контрагента'>
|
<PageLayout pageName="Добавление контрагента">
|
||||||
<Breadcrumbs breadcrumbs={BREADCRUMBS} />
|
<Breadcrumbs breadcrumbs={BREADCRUMBS} />
|
||||||
<div className="bg-white shadow rounded-lg p-6">
|
<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(onSubmit)} className="space-y-6">
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{/* Основная информация */}
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-xl font-semibold">Основная информация</h2>
|
<h2 className="text-xl font-semibold">Основная информация</h2>
|
||||||
|
|
||||||
<div>
|
<Input id="name" type="text" label="Наименование *" {...register("name")} />
|
||||||
<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>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
|
|
@ -142,233 +94,46 @@ export default function CreateContractor() {
|
||||||
<select
|
<select
|
||||||
id="type"
|
id="type"
|
||||||
name="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"
|
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="legal">Юридическое лицо</option>
|
||||||
<option value="individual">Физическое лицо/ИП</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<Input id="inn" type="text" label="ИНН *" {...register("inn")} />
|
||||||
<label
|
<Input id="kpp" type="text" label="КПП" {...register("kpp")} />
|
||||||
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>
|
|
||||||
|
|
||||||
{formData.type === "legal" && (
|
<Input id="ogrn" type="text" label="ОГРН" {...register("ogrn")} />
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-xl font-semibold">Контактная информация</h2>
|
<h2 className="text-xl font-semibold">Контактная информация</h2>
|
||||||
|
|
||||||
<div>
|
<Input id="address" type="text" label="Адрес" {...register("address")} />
|
||||||
<label
|
<Input id="phone" type="tel" label='Телефон' {...register("phone")} />
|
||||||
htmlFor="address"
|
<Input id="email" type="email" label='Эл. почта' {...register("email")} />
|
||||||
className="block text-sm font-medium text-gray-700"
|
<Input id="contactPerson" type="text" label='Контактное лицо' {...register("contactPerson")} />
|
||||||
>
|
|
||||||
Адрес
|
|
||||||
</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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Банковские реквизиты */}
|
|
||||||
<div className="space-y-4 md:col-span-2">
|
<div className="space-y-4 md:col-span-2">
|
||||||
<h2 className="text-xl font-semibold">Банковские реквизиты</h2>
|
<h2 className="text-xl font-semibold">Банковские реквизиты</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<Input id="account" label='Расчетный счет' type="text" {...register("account")} />
|
||||||
<label
|
<Input id="bank" type="text" label="Банк" {...register("bank")} />
|
||||||
htmlFor="bankDetails.account"
|
<Input id="bik" type="text" label='БИК' {...register("bik")} />
|
||||||
className="block text-sm font-medium text-gray-700"
|
<Input id="correspondentAccount" type="text" label='Корр. счет' {...register("correspondentAccount")} />
|
||||||
>
|
|
||||||
Расчетный счет
|
|
||||||
</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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end space-x-3">
|
<div className="flex justify-end space-x-3">
|
||||||
<button
|
<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">
|
||||||
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>
|
</button>
|
||||||
<button
|
<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">
|
||||||
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ type IInputProps = {
|
||||||
|
|
||||||
export const Input = ({ label, isError, errorText, className, id, ...props }: IInputProps) => {
|
export const Input = ({ label, isError, errorText, className, id, ...props }: IInputProps) => {
|
||||||
const inputClsx = twMerge(
|
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-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,
|
"border-gray-300 focus:ring-blue-500 focus:border-blue-500": !isError,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
flexRender,
|
flexRender,
|
||||||
RowData,
|
RowData,
|
||||||
} from '@tanstack/react-table'
|
} from '@tanstack/react-table'
|
||||||
|
import { Input } from '../Input';
|
||||||
|
|
||||||
type TableProps<T extends RowData> = {
|
type TableProps<T extends RowData> = {
|
||||||
tableData: T[];
|
tableData: T[];
|
||||||
|
|
@ -162,16 +163,8 @@ export function Table<T extends RowData>({ tableData, columns }: TableProps<T>)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Filter({
|
function Filter({ column, table }: { column: Column<any, any>, table: TanStackTable<any> }) {
|
||||||
column,
|
const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);
|
||||||
table,
|
|
||||||
}: {
|
|
||||||
column: Column<any, any>;
|
|
||||||
table: TanStackTable<any>;
|
|
||||||
}) {
|
|
||||||
const firstValue = table
|
|
||||||
.getPreFilteredRowModel()
|
|
||||||
.flatRows[0]?.getValue(column.id);
|
|
||||||
|
|
||||||
const columnFilterValue = column.getFilterValue();
|
const columnFilterValue = column.getFilterValue();
|
||||||
|
|
||||||
|
|
@ -192,18 +185,13 @@ function Filter({
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={(columnFilterValue as [number, number])?.[1] ?? ""}
|
value={(columnFilterValue as [number, number])?.[1] ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => column.setFilterValue((old: [number, number]) => [old?.[0], e.target.value])}
|
||||||
column.setFilterValue((old: [number, number]) => [
|
|
||||||
old?.[0],
|
|
||||||
e.target.value,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
placeholder={`Max`}
|
placeholder={`Max`}
|
||||||
className="w-24 border shadow rounded"
|
className="w-24 border shadow rounded"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={(columnFilterValue ?? "") as string}
|
value={(columnFilterValue ?? "") as string}
|
||||||
onChange={(e) => column.setFilterValue(e.target.value)}
|
onChange={(e) => column.setFilterValue(e.target.value)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user