diff --git a/frontend/app/product/books/[bookId]/page.tsx b/frontend/app/product/books/[bookId]/page.tsx
index 2541987..205c766 100644
--- a/frontend/app/product/books/[bookId]/page.tsx
+++ b/frontend/app/product/books/[bookId]/page.tsx
@@ -15,7 +15,12 @@ import { Label } from "@/components/ui/label";
import { useToast } from "@/components/ui/use-toast";
import Link from "next/link";
import { useEffect, useState } from "react";
-import { SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
+import {
+ Controller,
+ SubmitErrorHandler,
+ SubmitHandler,
+ useForm,
+} from "react-hook-form";
import { LuCheck } from "react-icons/lu";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
@@ -38,6 +43,7 @@ import { BookTitle } from "@/types";
import BookTitleSelectEdit from "@/components/book-manage/book-title-select-edit";
import { useLoading } from "@/hooks/loading-context";
import BookDetailSkeleton from "@/components/skeleton/book-detail-skeleton";
+import { NumericFormat } from "react-number-format";
const FormSchema = z.object({
bookTitleId: z.string().min(1, "Vui lòng chọn một đầu sách"),
@@ -194,7 +200,7 @@ const EditBook = ({ params }: { params: { bookId: string } }) => {
const { currentUser } = useCurrentUser();
if (!currentUser || isLoading) {
- return ;
+ return ;
} else if (
currentUser &&
!includesRoles({
@@ -335,10 +341,26 @@ const EditBook = ({ params }: { params: { bookId: string } }) => {
- (
+ {
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+
+ field.onChange(numericValue);
+ }}
+ readOnly={readOnly}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
/>
{errors.listedPrice && (
@@ -348,10 +370,25 @@ const EditBook = ({ params }: { params: { bookId: string } }) => {
-
(
+
{
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+ field.onChange(numericValue);
+ }}
+ readOnly={readOnly}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
/>
{errors.sellPrice && (
diff --git a/frontend/app/product/books/add/page.tsx b/frontend/app/product/books/add/page.tsx
index 8d7d0ff..6c68f7e 100644
--- a/frontend/app/product/books/add/page.tsx
+++ b/frontend/app/product/books/add/page.tsx
@@ -15,7 +15,12 @@ import { Label } from "@/components/ui/label";
import { useToast } from "@/components/ui/use-toast";
import Link from "next/link";
import { useState } from "react";
-import { SubmitHandler, useFieldArray, useForm } from "react-hook-form";
+import {
+ Controller,
+ SubmitHandler,
+ useFieldArray,
+ useForm,
+} from "react-hook-form";
import { LuCheck } from "react-icons/lu";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
@@ -32,6 +37,7 @@ import { includesRoles } from "@/lib/utils";
import NoRole from "@/components/no-role";
import { useLoading } from "@/hooks/loading-context";
import BookAddSkeleton from "@/components/skeleton/book-add-skeleton";
+import { NumericFormat } from "react-number-format";
const FormSchema = z.object({
bookTitleId: z.string().min(1, "Vui lòng chọn một đầu sách"),
@@ -80,6 +86,7 @@ const InsertNewBook = () => {
setValue,
reset,
trigger,
+ clearErrors,
formState: { errors },
} = form;
@@ -139,20 +146,23 @@ const InsertNewBook = () => {
title: "Thành công",
description: "Thêm mới sách thành công",
});
+ router.refresh();
+
+ setPublisherId("");
+
reset({
bookTitleId: data.bookTitleId,
idBook: "",
edition: 1,
publisherId: "",
- listedPrice: 0,
- sellPrice: 0,
+ listedPrice: 1,
+ sellPrice: 1,
image: "/no-image.jpg",
});
- setPublisherId("");
+ clearErrors();
setImage(null);
mutate(`${endPoint}/v1/books/all`);
- router.refresh();
}
};
@@ -264,7 +274,25 @@ const InsertNewBook = () => {
-
+ (
+ {
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+ field.onChange(numericValue);
+ }}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
+ />
{errors.listedPrice && (
{errors.listedPrice.message}
@@ -273,7 +301,25 @@ const InsertNewBook = () => {
-
+
(
+ {
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+ field.onChange(numericValue);
+ }}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
+ />
{errors.sellPrice && (
{errors.sellPrice.message}
diff --git a/frontend/app/staff/[staffId]/detail-layout.tsx b/frontend/app/staff/[staffId]/detail-layout.tsx
index ab5696b..db2bcd3 100644
--- a/frontend/app/staff/[staffId]/detail-layout.tsx
+++ b/frontend/app/staff/[staffId]/detail-layout.tsx
@@ -39,6 +39,7 @@ import { useCurrentUser } from "@/hooks/use-user";
import { includesRoles, isAdmin } from "@/lib/utils";
import { useLoading } from "@/hooks/loading-context";
import StaffDetailSkeleton from "@/components/skeleton/staff-detail";
+import { useRouter } from "next/navigation";
const FormSchema = z.object({
name: required,
@@ -50,6 +51,7 @@ const PasswordSchema = z.object({
});
const EditStaff = ({ params }: { params: { staffId: string } }) => {
+ const router = useRouter();
const [role, setRole] = useState("");
const { showLoading, hideLoading } = useLoading();
@@ -134,6 +136,7 @@ const EditStaff = ({ params }: { params: { staffId: string } }) => {
description: "Đặt lại mật khẩu nhân viên thành công",
});
setOpen(false);
+ router.refresh();
}
};
const onSubmit: SubmitHandler> = async (data) => {
@@ -160,6 +163,7 @@ const EditStaff = ({ params }: { params: { staffId: string } }) => {
description: "Chỉnh sửa thông tin nhân viên thành công",
});
mutate();
+ router.refresh();
}
};
const handleOpen = (value: boolean) => {
@@ -207,6 +211,7 @@ const EditStaff = ({ params }: { params: { staffId: string } }) => {
description: "Thay đổi ảnh nhân viên thành công",
});
mutate();
+ router.refresh();
}
}
};
@@ -248,6 +253,7 @@ const EditStaff = ({ params }: { params: { staffId: string } }) => {
});
setOpen(false);
mutate();
+ router.refresh();
}
};
@@ -272,6 +278,7 @@ const EditStaff = ({ params }: { params: { staffId: string } }) => {
});
setOpen(false);
mutate();
+ router.refresh();
}
};
const { currentUser } = useCurrentUser();
diff --git a/frontend/app/stockmanage/import/[importId]/page.tsx b/frontend/app/stockmanage/import/[importId]/page.tsx
index 69281f4..45d5385 100644
--- a/frontend/app/stockmanage/import/[importId]/page.tsx
+++ b/frontend/app/stockmanage/import/[importId]/page.tsx
@@ -46,6 +46,7 @@ const ImportDetail = ({ params }: { params: { importId: string } }) => {
description: "Chuyển trạng thái thành công",
});
mutate();
+ router.refresh();
}
};
const { currentUser } = useCurrentUser();
diff --git a/frontend/app/supplier/[supplierId]/detail-layout.tsx b/frontend/app/supplier/[supplierId]/detail-layout.tsx
index f469803..bb38dd2 100644
--- a/frontend/app/supplier/[supplierId]/detail-layout.tsx
+++ b/frontend/app/supplier/[supplierId]/detail-layout.tsx
@@ -3,7 +3,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
-import { useState } from "react";
+import { ChangeEvent, forwardRef, useEffect, useRef, useState } from "react";
import { ImportTable } from "@/components/supplier-manage/import-table";
import { DebtTable } from "@/components/supplier-manage/debt-table";
import { Button } from "@/components/ui/button";
@@ -17,7 +17,7 @@ import {
import getSupplier from "@/lib/supplier/getSupplier";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
-import { SubmitHandler, useForm } from "react-hook-form";
+import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { toast } from "@/components/ui/use-toast";
import paySupplier from "@/lib/supplier/paySupplier";
import EditDialog from "@/components/supplier-manage/edit";
@@ -25,22 +25,33 @@ import { useSWRConfig } from "swr";
import { endPoint } from "@/constants";
import { withAuth } from "@/lib/role/withAuth";
import { useCurrentUser } from "@/hooks/use-user";
-import { includesRoles } from "@/lib/utils";
-import NoRole from "@/components/no-role";
+import { includesRoles, toVND } from "@/lib/utils";
import { useLoading } from "@/hooks/loading-context";
import { Skeleton } from "@/components/ui/skeleton";
import DropdownSkeleton from "@/components/skeleton/dropdown-skeleton";
+import { useRouter } from "next/navigation";
+import { NumericFormat } from "react-number-format";
+import React from "react";
+
const FormSchema = z.object({
- quantity: z.coerce.number().gte(1, "Giá trị phải lớn hơn 0"), // Force it to be a number
+ quantity: z.coerce
+ .number({ invalid_type_error: "Giá trị phải là một số" })
+ .gte(1, "Giá trị phải lớn hơn 0")
+ .refine((value) => Number.isInteger(value), {
+ message: "Giá trị phải là số nguyên",
+ }), // Force it to be a number
});
+
const SupplierDetail = ({ params }: { params: { supplierId: string } }) => {
const form = useForm>({
resolver: zodResolver(FormSchema),
+ defaultValues: { quantity: 0 },
});
const {
register,
handleSubmit,
control,
+ reset,
formState: { errors },
} = form;
const {
@@ -49,7 +60,9 @@ const SupplierDetail = ({ params }: { params: { supplierId: string } }) => {
isError,
mutate: mutateSupplier,
} = getSupplier(params.supplierId);
+ const inputRef = useRef(null);
+ const router = useRouter();
const { mutate } = useSWRConfig();
const { showLoading, hideLoading } = useLoading();
const onSubmit: SubmitHandler> = async (
@@ -76,6 +89,7 @@ const SupplierDetail = ({ params }: { params: { supplierId: string } }) => {
});
mutate(`${endPoint}/v1/suppliers/${data.id}/debts?page=${pageIndex}`);
mutateSupplier();
+ router.refresh();
}
setOpenDialog(false);
};
@@ -169,7 +183,15 @@ const SupplierDetail = ({ params }: { params: { supplierId: string } }) => {
/>
) : null}
-
diff --git a/frontend/components/staff/create-staff-dialog.tsx b/frontend/components/staff/create-staff-dialog.tsx
index d717e1a..a35f75b 100644
--- a/frontend/components/staff/create-staff-dialog.tsx
+++ b/frontend/components/staff/create-staff-dialog.tsx
@@ -28,7 +28,7 @@ const StaffSchema = z.object({
name: required,
email: z.string().email("Email không hợp lệ"),
phone: z.string().regex(phoneRegex, "Số điện thoại không hợp lệ"),
- address: required,
+ address: z.string(),
roleId: z.string().min(1, "Không để trống trường này"),
img: z.string(),
});
@@ -56,26 +56,25 @@ const CreateStaffDialog = () => {
const { showLoading, hideLoading } = useLoading();
const router = useRouter();
const onSubmit: SubmitHandler
> = async (data) => {
- setOpen(false);
- if (!image) {
- return;
- }
- let formData = new FormData();
+ if (image) {
+ let formData = new FormData();
- formData.append("file", image);
- formData.append("folderName", "avatars");
- showLoading();
- const imgRes = await imageUpload(formData);
- if (imgRes.hasOwnProperty("errorKey")) {
- toast({
- variant: "destructive",
- title: "Có lỗi",
- description: imgRes.message,
- });
- return;
+ formData.append("file", image);
+ formData.append("folderName", "avatars");
+ showLoading();
+ const imgRes = await imageUpload(formData);
+ if (imgRes.hasOwnProperty("errorKey")) {
+ toast({
+ variant: "destructive",
+ title: "Có lỗi",
+ description: imgRes.message,
+ });
+ return;
+ } else {
+ data.img = imgRes.data;
+ }
}
- data.img = imgRes.data;
const response: Promise = createStaff({ staff: data });
const responseData = await response;
hideLoading();
@@ -91,6 +90,8 @@ const CreateStaffDialog = () => {
title: "Thành công",
description: "Thêm nhân viên thành công",
});
+ setOpen(false);
+
router.refresh();
}
};
diff --git a/frontend/components/stock-manage/book-insert.tsx b/frontend/components/stock-manage/book-insert.tsx
index bc65d20..01aa386 100644
--- a/frontend/components/stock-manage/book-insert.tsx
+++ b/frontend/components/stock-manage/book-insert.tsx
@@ -4,6 +4,7 @@ import { Input } from "../ui/input";
import { Button } from "../ui/button";
import {
Control,
+ Controller,
UseFormReturn,
useFieldArray,
useWatch,
@@ -20,6 +21,7 @@ import { toVND } from "@/lib/utils";
import { AutoComplete } from "../ui/autocomplete";
import getAllBookForSale from "@/lib/book/getAllBookForSale";
import DropdownSkeleton from "../skeleton/dropdown-skeleton";
+import { NumericFormat } from "react-number-format";
const Total = ({
control,
}: {
@@ -135,10 +137,25 @@ const BookInsert = ({
({value.id})
-
+ (
+ {
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+ field.onChange(numericValue);
+ }}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
+ />
{errors &&
errors.details &&
errors.details[index] &&
@@ -163,10 +180,27 @@ const BookInsert = ({
-
+ (
+ {
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+
+ field.onChange(numericValue);
+ }}
+ defaultValue={book.qtyImport}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
+ />
{errors &&
errors.details &&
errors.details[index] &&
diff --git a/frontend/components/supplier-manage/create.tsx b/frontend/components/supplier-manage/create.tsx
index 095be3c..d84983e 100644
--- a/frontend/components/supplier-manage/create.tsx
+++ b/frontend/components/supplier-manage/create.tsx
@@ -1,5 +1,5 @@
"use client";
-import { SubmitHandler, useForm } from "react-hook-form";
+import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -20,11 +20,20 @@ import { phoneRegex, required } from "@/constants";
import { useCurrentUser } from "@/hooks/use-user";
import { includesRoles } from "@/lib/utils";
import { useLoading } from "@/hooks/loading-context";
+import { NumericFormat } from "react-number-format";
const SupplierSchema = z.object({
id: z.string().max(12, "Tối đa 12 ký tự"),
name: required,
- email: z.string().email("Email không hợp lệ"),
+ email: z
+ .string()
+ .refine(
+ (value) =>
+ value === "" || /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value),
+ {
+ message: "Email không hợp lệ",
+ }
+ ),
phone: z.string().regex(phoneRegex, "Số điện thoại không hợp lệ"),
debt: z.coerce
.number({ invalid_type_error: "Nợ ban đầu phải là một số" })
@@ -46,6 +55,7 @@ const CreateDialog = ({
register,
handleSubmit,
reset,
+ control,
formState: { errors },
} = useForm>({
resolver: zodResolver(SupplierSchema),
@@ -157,7 +167,25 @@ const CreateDialog = ({
-
+
(
+ {
+ const numericValue = parseFloat(
+ values.value.replace(/,/g, "")
+ );
+ field.onChange(numericValue);
+ }}
+ thousandSeparator="."
+ decimalSeparator=","
+ valueIsNumericString
+ customInput={Input}
+ />
+ )}
+ />
{errors.debt && (
{errors.debt.message}
diff --git a/frontend/components/supplier-manage/export-dialog.tsx b/frontend/components/supplier-manage/export-dialog.tsx
index d90eba4..8118e82 100644
--- a/frontend/components/supplier-manage/export-dialog.tsx
+++ b/frontend/components/supplier-manage/export-dialog.tsx
@@ -27,7 +27,7 @@ const ExportDialog = ({
Xuất file
-
+
Xuất danh sách
@@ -44,7 +44,7 @@ const ExportDialog = ({
diff --git a/frontend/components/ui/dialog.tsx b/frontend/components/ui/dialog.tsx
index bc61bf6..46a6dc9 100644
--- a/frontend/components/ui/dialog.tsx
+++ b/frontend/components/ui/dialog.tsx
@@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<