chore: update app version and enhance UI components

- Bumped NEXT_PUBLIC_APP_VERSION to 2.0.3 for the latest release.
- Improved Cypress test for customer feedback page to handle delayed route transitions.
- Refactored CompanyDetailsPage to enhance layout and added new components for better structure.
- Updated TenderDetails to conditionally render location and currency information based on validity checks.
- Added data-cy attributes to various form elements and tables for improved testing capabilities.
This commit is contained in:
AmirReza Jamali
2026-05-05 15:20:55 +03:30
parent 4b425d6a46
commit f48cb7e249
16 changed files with 713 additions and 113 deletions
@@ -2,20 +2,56 @@
import Loading from "@/components/loading";
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
import IsVisible from "@/components/ui/IsVisible";
import Status from "@/components/ui/Status";
import { useCompanyDetails } from "@/hooks/queries";
import { cn } from "@/lib/utils";
import { BreadcrumbItem } from "@/types/shared";
import { formatPhoneNumber, unixToDate } from "@/utils/shared";
import getSymbolFromCurrency from "currency-symbol-map";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { use } from "react";
interface IProps {
params: Promise<{ id: string }>;
}
function SectionHeading({ children }: { children: React.ReactNode }) {
return (
<div className="mb-4 flex items-center gap-3">
<span className="h-px flex-1 bg-gradient-to-r from-stroke/0 via-stroke/80 to-stroke/0 dark:from-dark-3/0 dark:via-dark-3 dark:to-dark-3/0" />
<h2 className="shrink-0 text-[11px] font-semibold uppercase tracking-[0.2em] text-dark-5 dark:text-dark-6">
{children}
</h2>
<span className="h-px flex-1 bg-gradient-to-l from-stroke/0 via-stroke/80 to-stroke/0 dark:from-dark-3/0 dark:via-dark-3 dark:to-dark-3/0" />
</div>
);
}
function ExternalLinkIcon({ className }: { className?: string }) {
return (
<svg
className={cn("h-4 w-4 shrink-0", className)}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
aria-hidden
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
);
}
const SHOWCASE_CENTERED_BODY = {
rootClassName: "flex h-full min-h-0 flex-col",
className:
"flex min-h-0 flex-1 flex-col items-center justify-center text-center",
} as const;
const CompanyDetailsPage = ({ params }: IProps) => {
const { id } = use(params);
const breadcrumbItems: BreadcrumbItem[] = [
@@ -33,8 +69,9 @@ const CompanyDetailsPage = ({ params }: IProps) => {
},
];
const { data, isLoading } = useCompanyDetails(id);
const router = useRouter();
if (isLoading) return <Loading />;
const company = data.data;
const website = company.website?.trim();
return (
<>
@@ -42,71 +79,142 @@ const CompanyDetailsPage = ({ params }: IProps) => {
<ShowcaseSection
title={
<div className="flex justify-between">
<strong>{data.data.name}</strong>
<Status status={data?.data?.status}>{data.data.status}</Status>
<div className="flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between">
<div className="min-w-0 space-y-2">
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-primary">
Company
</p>
<h1 className="text-balance text-xl font-bold leading-tight text-dark dark:text-white sm:text-2xl lg:text-[1.65rem]">
{company.name}
</h1>
</div>
<div className="flex shrink-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-end">
<Status status={company.status}>{company.status}</Status>
{website && (
<Link
href={website}
target="_blank"
rel="noopener noreferrer"
className="shadow-theme-xs inline-flex items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:bg-primary/90 hover:shadow-md"
>
<span>Visit website</span>
<ExternalLinkIcon className="opacity-90" />
</Link>
)}
</div>
</div>
}
className="!p-0"
>
<div className="grid grid-cols-4 gap-4">
<ShowcaseSection title={"Currency"}>
<strong>
{`${data?.data?.currency} ${getSymbolFromCurrency(data?.data.currency ?? "USD")}`}
</strong>
</ShowcaseSection>
<ShowcaseSection title={"Language"}>
<strong className="uppercase">{data?.data?.language}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Created At"}>
<strong>{unixToDate({ unix: data?.data?.created_at })}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Annual revenue"}>
<strong>{data?.data?.annual_revenue}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Email"}>
<strong>{data?.data?.email}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Employee count"}>
<strong>{data?.data?.employee_count}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Founded Year"}>
<strong>{data?.data?.founded_year}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Industry"}>
<strong>{data?.data?.industry}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Is Compliant"}>
<strong>{data?.data?.is_compliant ? "Yes" : "No"}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Is Verified"}>
<strong>{data?.data?.is_verified ? "Yes" : "No"}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Phone"}>
<strong>{formatPhoneNumber(data?.data?.phone ?? 0)}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Registration Number"}>
<strong>{data?.data?.registration_number}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Tax id"}>
<strong>{data?.data?.tax_id}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Type"}>
<strong>{data?.data?.type}</strong>
</ShowcaseSection>
<ShowcaseSection title={"Updated at"}>
<strong>{unixToDate({ unix: data?.data?.updated_at ?? 0 })}</strong>
</ShowcaseSection>
<IsVisible condition={!!data.data.website}>
<ShowcaseSection title={"Website"}>
<Link
href={data.data.website}
className="font-bold"
target="_blank"
<div className="space-y-10 px-4 pb-8 pt-2 sm:px-6 sm:pb-10 xl:px-10">
<div>
<SectionHeading>Profile</SectionHeading>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<ShowcaseSection title="Industry" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.industry || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Type" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.type || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Language" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium uppercase text-dark dark:text-white">
{company.language || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Founded year" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.founded_year || "—"}
</span>
</ShowcaseSection>
</div>
</div>
<div>
<SectionHeading>Business</SectionHeading>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<ShowcaseSection title="Currency" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{`${company.currency || "—"} ${getSymbolFromCurrency(company.currency ?? "USD")}`}
</span>
</ShowcaseSection>
<ShowcaseSection title="Annual revenue" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.annual_revenue || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Employee count" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.employee_count || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection
title="Registration number"
{...SHOWCASE_CENTERED_BODY}
>
{data.data.website}
</Link>
</ShowcaseSection>
</IsVisible>
<span className="font-medium text-dark dark:text-white">
{company.registration_number || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Tax ID" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.tax_id || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Compliant" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.is_compliant ? "Yes" : "No"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Verified" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{company.is_verified ? "Yes" : "No"}
</span>
</ShowcaseSection>
</div>
</div>
<div>
<SectionHeading>Contact & timeline</SectionHeading>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<ShowcaseSection title="Email" {...SHOWCASE_CENTERED_BODY}>
<span className="break-all font-medium text-dark dark:text-white">
{company.email || "—"}
</span>
</ShowcaseSection>
<ShowcaseSection title="Phone" {...SHOWCASE_CENTERED_BODY}>
<span className="font-medium text-dark dark:text-white">
{formatPhoneNumber(company.phone ?? 0)}
</span>
</ShowcaseSection>
<ShowcaseSection title="Created at" {...SHOWCASE_CENTERED_BODY}>
<time className="font-medium tabular-nums text-dark dark:text-white">
{unixToDate({ unix: company.created_at })}
</time>
</ShowcaseSection>
<ShowcaseSection title="Updated at" {...SHOWCASE_CENTERED_BODY}>
<time className="font-medium tabular-nums text-dark dark:text-white">
{unixToDate({ unix: company.updated_at ?? 0 })}
</time>
</ShowcaseSection>
{website && (
<ShowcaseSection title="Website" {...SHOWCASE_CENTERED_BODY}>
<Link
href={website}
target="_blank"
rel="noopener noreferrer"
className="inline-flex max-w-full items-center gap-1.5 break-all font-medium text-primary underline-offset-4 hover:underline"
>
<ExternalLinkIcon className="shrink-0" />
{website}
</Link>
</ShowcaseSection>
)}
</div>
</div>
</div>
</ShowcaseSection>
</>
+77 -42
View File
@@ -22,6 +22,8 @@ interface IProps {
}>;
}
const isValidDisplayCountryCode = (code: string) => /^[A-Za-z]{2,3}$/.test(code);
function SectionHeading({ children }: { children: React.ReactNode }) {
return (
<div className="mb-4 flex items-center gap-3">
@@ -65,9 +67,11 @@ const TenderDetails = ({ params }: IProps) => {
const { data, isLoading, isError } = useTenderDetailQuery(details);
const countryCodeNormalized =
data?.data.country_code?.trim().toUpperCase() ?? "";
const countryCodeValid = isValidAlpha2CountryCode(countryCodeNormalized);
const hasValidCountryCodeForDisplay =
isValidDisplayCountryCode(countryCodeNormalized);
const canLoadCountryFlag = isValidAlpha2CountryCode(countryCodeNormalized);
const { data: flagData } = useGetFlagQuery(countryCodeNormalized, {
enabled: countryCodeValid,
enabled: canLoadCountryFlag,
});
const breadcrumbItems = [
{ name: "Dashboard", href: "/" },
@@ -103,6 +107,14 @@ const TenderDetails = ({ params }: IProps) => {
const tender = data!.data;
const submissionUrl = tender.submission_url?.trim();
const currencyCode = tender.currency?.trim().toUpperCase() ?? "";
const hasValidCurrency = Boolean(currencyCode);
const hasValidEstimatedValue =
typeof tender.estimated_value === "number" &&
Number.isFinite(tender.estimated_value) &&
tender.estimated_value > 0;
const hasValidLocationCurrencyOrValue =
hasValidCountryCodeForDisplay || hasValidCurrency || hasValidEstimatedValue;
return (
<>
@@ -252,50 +264,73 @@ const TenderDetails = ({ params }: IProps) => {
</div>
</div>
<div>
<SectionHeading>Location & currency</SectionHeading>
<div className="grid gap-4 sm:grid-cols-2 lg:max-w-3xl">
<div className="shadow-theme-xs relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-gray-2/30 to-primary/[0.06] p-5 dark:border-dark-3 dark:from-gray-dark dark:via-gray-dark dark:to-primary/[0.12]">
<div className="pointer-events-none absolute -right-8 top-0 h-24 w-24 rounded-full bg-primary/15 blur-2xl dark:bg-primary/25" />
<h3 className="mb-4 text-xs font-semibold uppercase tracking-wide text-dark-5 dark:text-dark-6">
Country
</h3>
<div className="flex items-center gap-3">
<span className="flex h-11 w-11 items-center justify-center rounded-xl bg-white/80 text-primary shadow-inner dark:bg-dark-2/80">
<LocationIcon />
</span>
<div className="flex flex-wrap items-center gap-2">
<span className="text-sm font-semibold uppercase tracking-wide text-dark dark:text-white">
{tender.country_code}
</span>
{flagData && (
<div
dangerouslySetInnerHTML={{ __html: flagData }}
className="flex h-8 w-11 items-center justify-center overflow-hidden rounded-lg border border-stroke/60 shadow-sm dark:border-dark-3"
/>
)}
{hasValidLocationCurrencyOrValue && (
<div>
<SectionHeading>Location & currency</SectionHeading>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 lg:max-w-5xl">
{hasValidCountryCodeForDisplay && (
<div className="shadow-theme-xs relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-gray-2/30 to-primary/[0.06] p-5 dark:border-dark-3 dark:from-gray-dark dark:via-gray-dark dark:to-primary/[0.12]">
<div className="pointer-events-none absolute -right-8 top-0 h-24 w-24 rounded-full bg-primary/15 blur-2xl dark:bg-primary/25" />
<h3 className="mb-4 text-xs font-semibold uppercase tracking-wide text-dark-5 dark:text-dark-6">
Country
</h3>
<div className="flex items-center gap-3">
<span className="flex h-11 w-11 items-center justify-center rounded-xl bg-white/80 text-primary shadow-inner dark:bg-dark-2/80">
<LocationIcon />
</span>
<div className="flex flex-wrap items-center gap-2">
<span className="text-sm font-semibold uppercase tracking-wide text-dark dark:text-white">
{countryCodeNormalized}
</span>
{flagData && (
<div
dangerouslySetInnerHTML={{ __html: flagData }}
className="flex h-8 w-11 items-center justify-center overflow-hidden rounded-lg border border-stroke/60 shadow-sm dark:border-dark-3"
/>
)}
</div>
</div>
</div>
</div>
</div>
)}
{tender.currency && (
<div className="shadow-theme-xs relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-gray-2/30 to-purple-500/[0.06] p-5 dark:border-dark-3 dark:from-gray-dark dark:via-gray-dark dark:to-purple-500/[0.12]">
<div className="pointer-events-none absolute -left-6 bottom-0 h-20 w-20 rounded-full bg-purple-500/15 blur-2xl dark:bg-purple-500/25" />
<h3 className="mb-4 text-xs font-semibold uppercase tracking-wide text-dark-5 dark:text-dark-6">
Currency
</h3>
<div className="flex items-baseline gap-2">
<span className="text-2xl font-bold tabular-nums text-dark dark:text-white">
{tender.currency}
</span>
<span className="text-lg font-medium text-dark-5 dark:text-dark-6">
{getSymbolFromCurrency(tender.currency ?? "USD")}
</span>
{hasValidCurrency && (
<div className="shadow-theme-xs relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-gray-2/30 to-blue/[0.06] p-5 dark:border-dark-3 dark:from-gray-dark dark:via-gray-dark dark:to-blue/[0.12]">
<div className="pointer-events-none absolute -left-6 bottom-0 h-20 w-20 rounded-full bg-blue/15 blur-2xl dark:bg-blue/25" />
<h3 className="mb-4 text-xs font-semibold uppercase tracking-wide text-dark-5 dark:text-dark-6">
Currency
</h3>
<div className="flex items-baseline gap-2">
<span className="text-2xl font-bold tabular-nums text-dark dark:text-white">
{currencyCode}
</span>
<span className="text-lg font-medium text-dark-5 dark:text-dark-6">
{getSymbolFromCurrency(currencyCode || "USD")}
</span>
</div>
</div>
</div>
)}
)}
{hasValidEstimatedValue && (
<div className="shadow-theme-xs relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-gray-2/30 to-green/[0.06] p-5 dark:border-dark-3 dark:from-gray-dark dark:via-gray-dark dark:to-green/[0.12]">
<div className="pointer-events-none absolute -right-6 bottom-0 h-20 w-20 rounded-full bg-green/15 blur-2xl dark:bg-green/25" />
<h3 className="mb-4 text-xs font-semibold uppercase tracking-wide text-dark-5 dark:text-dark-6">
Estimated value
</h3>
<div className="flex items-baseline gap-2">
<span className="text-2xl font-bold tabular-nums text-dark dark:text-white">
{new Intl.NumberFormat().format(tender.estimated_value)}
</span>
{hasValidCurrency && (
<span className="text-sm font-medium uppercase text-dark-5 dark:text-dark-6">
{currencyCode}
</span>
)}
</div>
</div>
)}
</div>
</div>
</div>
)}
<div>
<SectionHeading>Description</SectionHeading>
@@ -18,6 +18,7 @@ type PropsType<T extends FieldValues> = {
className?: string;
icon?: React.ReactNode;
defaultValue?: string;
dataCy?: string;
handleChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
register?: UseFormRegister<T>;
errors?: FieldErrors<T>;
@@ -33,6 +34,7 @@ export function TextAreaGroup<T extends FieldValues>({
className,
icon,
defaultValue,
dataCy,
handleChange,
register,
errors,
@@ -77,6 +79,7 @@ export function TextAreaGroup<T extends FieldValues>({
required={required}
disabled={disabled}
data-active={active}
data-cy={dataCy}
/>
{icon}
+3
View File
@@ -8,6 +8,7 @@ type PropsType = {
backgroundSize?: "sm" | "default";
name?: string;
checked?: boolean;
dataCy?: string;
onToggle: (e: ToggleEvent<HTMLInputElement>) => void;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
};
@@ -18,6 +19,7 @@ export function Switch({
backgroundSize,
name,
checked = false,
dataCy,
onToggle,
onChange,
}: PropsType) {
@@ -37,6 +39,7 @@ export function Switch({
id={id}
className="peer sr-only"
checked={checked}
data-cy={dataCy}
/>
<div
className={cn("h-8 w-14 rounded-full bg-gray-3 dark:bg-[#5A616B]", {
@@ -30,6 +30,7 @@ const CompanyCategoryListFilters = ({
return (
<form
className="grid !min-w-full grid-cols-1 gap-4 xl:grid-cols-2"
data-cy="company-categories-filter-form"
onSubmit={handleFilterSubmit(search)}
>
<InputGroup
@@ -38,6 +39,7 @@ const CompanyCategoryListFilters = ({
label="search"
placeholder="Search"
type="search"
dataCy="company-categories-filter-search-input"
/>
<Select
{...filterRegister("published")}
@@ -51,11 +53,13 @@ const CompanyCategoryListFilters = ({
clearable
value={watch("published")}
onClear={() => setValue("published", undefined)}
dataCy="company-categories-filter-published-select"
/>
<div className="flex gap-4">
<button
type="submit"
data-cy="company-categories-filter-submit-button"
className="mt-6 flex w-fit justify-center rounded-lg bg-primary p-[13px] font-medium capitalize text-white hover:bg-opacity-90"
>
{isMutating ? (
@@ -66,6 +70,7 @@ const CompanyCategoryListFilters = ({
</button>
<button
type="button"
data-cy="company-categories-filter-close-button"
className="mt-6 flex w-fit justify-center rounded-lg bg-error p-[13px] font-medium capitalize text-white hover:bg-opacity-90"
onClick={() => setIsFilterModalOpen(false)}
>
@@ -47,12 +47,13 @@ const CompanyCategoriesTable = () => {
} = useCompanyCategoriesPresenter();
return (
<ListWrapper>
<ListWrapper data-cy="company-categories-page">
<ListHeader
onFilter={() => setIsFilterModalOpen(true)}
createButtonText="Create Category"
dataCyPrefix="company-categories"
/>
<Table>
<Table data-cy="company-categories-table">
<TableHeader>
<TableRow className="border-none uppercase [&>th]:text-start">
<TableHead colSpan={100}>Row</TableHead>
@@ -73,6 +74,7 @@ const CompanyCategoriesTable = () => {
{optimisticCategories.map((category, index) => (
<TableRow
key={category.id}
data-cy={`company-categories-table-row-${category.id}`}
className="odd:bg-gray-2 dark:odd:bg-gray-7"
>
<TableCell colSpan={100}>
@@ -84,8 +86,10 @@ const CompanyCategoriesTable = () => {
{unixToDate({ unix: category.created_at, hasTime: true })}
</TableCell>
<TableCell colSpan={100}>
{/* Published: flip UI immediately via useOptimistic; if togglePublished rejects, React rolls the optimistic row back. */}
<Switch
withIcon
dataCy={`company-categories-published-toggle-${category.id}`}
onToggle={() => {}}
onChange={() => {
const nextPublished = !category.published;
@@ -94,7 +98,11 @@ const CompanyCategoriesTable = () => {
id: category.id,
published: nextPublished,
});
await togglePublished(category.id);
try {
await togglePublished(category.id);
} catch {
// useOptimistic automatically reverts when the transition ends
}
});
}}
checked={category.published}
@@ -103,6 +111,8 @@ const CompanyCategoriesTable = () => {
<TableCell className="text-start xl:pr-7.5">
<div className="flex items-center justify-start gap-x-3.5">
<button
type="button"
data-cy={`company-categories-row-delete-${category.id}`}
data-tooltip-id="delete"
data-tooltip-content="Delete"
data-tooltip-place="top"
@@ -117,6 +127,8 @@ const CompanyCategoriesTable = () => {
<TrashIcon />
</button>
<button
type="button"
data-cy={`company-categories-row-edit-${category.id}`}
data-tooltip-id="edit"
data-tooltip-content="Edit"
data-tooltip-place="top"
+6
View File
@@ -87,6 +87,12 @@ const TendersTable = () => {
<TableCell className="text-start" colSpan={100}>
{item.country_code}
</TableCell>
<TableCell className="text-start" colSpan={100}>
{unixToDate({
unix: item.publication_date,
hasTime: true,
})}
</TableCell>
<TableCell className="text-start" colSpan={100}>
{unixToDate({
unix: item.submission_deadline,
@@ -57,6 +57,7 @@ const useTenderListPresenter = () => {
"row",
"title",
"country code",
"publication date",
"submission deadline",
"tender deadline",
"created at",
@@ -17,15 +17,20 @@ const CreateCompanyCategoryForm = ({ defaultValues, editMode, id }: IProps) => {
const { errors, handleSubmit, onSubmit, register } =
useCreateCompanyCategoryPresenter({ defaultValues, editMode, id });
return (
<form className="grid grid-cols-1 gap-9" onSubmit={handleSubmit(onSubmit)}>
<form
className="grid grid-cols-1 gap-9"
data-cy="company-category-form"
onSubmit={handleSubmit(onSubmit)}
>
<ShowcaseSection
title="Create Company Category"
title={editMode ? "Edit Company Category" : "Create Company Category"}
className="grid grid-cols-1 gap-5 md:grid-cols-2"
>
<div className="flex flex-col gap-2 md:col-span-1">
<InputGroup
label="Name"
required
dataCy="company-category-form-name-input"
{...register("name", {
required: {
value: true,
@@ -51,6 +56,7 @@ const CreateCompanyCategoryForm = ({ defaultValues, editMode, id }: IProps) => {
<div className="flex flex-col gap-2 md:col-span-1">
<TextAreaGroup
label="Description"
dataCy="company-category-form-description-input"
{...register("description", {
minLength: {
value: 10,