refactor(Breadcrumbs, InputGroup, Layouts, Tables): enhance styling and structure for improved responsiveness
- Adjusted Breadcrumb component styles for better spacing and font size. - Made placeholder in InputGroup optional and refined input styles for consistency. - Updated MainLayout and Header components for improved padding and layout. - Enhanced table components by importing necessary elements and optimizing rendering logic. - Refactored CompanyCategoriesTable to utilize optimistic updates for better user experience.
This commit is contained in:
@@ -9,8 +9,8 @@ const Breadcrumb = ({ items }: BreadcrumbProps) => {
|
|||||||
const currentPage = items[items.length - 1];
|
const currentPage = items[items.length - 1];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div className="mb-3 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<h2 className="text-[26px] font-bold leading-[30px] text-dark dark:text-white">
|
<h2 className="text-[16px] font-bold leading-[30px] text-dark dark:text-white">
|
||||||
{currentPage.name}
|
{currentPage.name}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
@@ -20,14 +20,14 @@ const Breadcrumb = ({ items }: BreadcrumbProps) => {
|
|||||||
const isLast = index === items.length - 1;
|
const isLast = index === items.length - 1;
|
||||||
|
|
||||||
return isLast ? (
|
return isLast ? (
|
||||||
<li key={index} className="font-medium text-primary">
|
<li key={index} className="font-medium text-sm text-primary">
|
||||||
{item.name}
|
{item.name}
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
<li key={index} className="flex items-center gap-2">
|
<li key={index} className="flex items-center gap-2">
|
||||||
<Link
|
<Link
|
||||||
href={item.href}
|
href={item.href}
|
||||||
className="font-medium hover:text-primary"
|
className="font-medium text-sm hover:text-primary"
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type InputGroupProps<T extends FieldValues> = {
|
|||||||
name: Path<T>;
|
name: Path<T>;
|
||||||
label: string;
|
label: string;
|
||||||
type: HTMLInputTypeAttribute;
|
type: HTMLInputTypeAttribute;
|
||||||
placeholder: string;
|
placeholder?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
fileStyleVariant?: "style1" | "style2";
|
fileStyleVariant?: "style1" | "style2";
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@@ -93,7 +93,7 @@ const InputGroup = <T extends FieldValues>({
|
|||||||
defaultValue: props.defaultValue,
|
defaultValue: props.defaultValue,
|
||||||
})}
|
})}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent outline-none transition placeholder:capitalize focus:border-primary disabled:cursor-default disabled:bg-gray-2 data-[active=true]:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary dark:disabled:bg-dark dark:data-[active=true]:border-primary",
|
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent outline-none transition focus:border-primary disabled:cursor-default disabled:bg-gray-2 data-[active=true]:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary dark:disabled:bg-dark dark:data-[active=true]:border-primary",
|
||||||
// Add error styles conditionally
|
// Add error styles conditionally
|
||||||
error && "border-red focus:border-red dark:border-red",
|
error && "border-red focus:border-red dark:border-red",
|
||||||
type === "file"
|
type === "file"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function MainLayout({ children }: PropsWithChildren) {
|
|||||||
{isMobile && <div className="w-[70px] shrink-0" />}
|
{isMobile && <div className="w-[70px] shrink-0" />}
|
||||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col bg-gray-2 dark:bg-[#020d1a]">
|
<div className="flex min-h-0 min-w-0 flex-1 flex-col bg-gray-2 dark:bg-[#020d1a]">
|
||||||
<Header />
|
<Header />
|
||||||
<main className="isolate flex min-h-0 flex-1 flex-col mx-auto w-full max-w-screen-2xl overflow-hidden p-4 md:p-6 2xl:p-10">
|
<main className="isolate flex min-h-0 flex-1 flex-col mx-auto w-full max-w-screen-2xl overflow-hidden p-4 md:p-6 2xl:px-10 2xl:py-4">
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import { UserInfo } from "./user-info";
|
|||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-30 flex shrink-0 items-center justify-between border-b border-stroke bg-white px-4 py-5 shadow-1 dark:border-stroke-dark dark:bg-gray-dark md:px-5 2xl:px-10">
|
<header className="sticky top-0 z-30 flex shrink-0 items-center justify-between border-b border-stroke bg-white px-4 py-2.5 shadow-1 dark:border-stroke-dark dark:bg-gray-dark md:px-5 2xl:px-10">
|
||||||
<div className="max-xl:hidden">
|
<div className="max-xl:hidden">
|
||||||
<h1 className="mb-0.5 text-heading-5 font-bold text-dark dark:text-white">
|
<h1 className="text-heading-6 font-bold leading-tight text-dark dark:text-white">
|
||||||
Opportunity Lens
|
Opportunity Lens
|
||||||
</h1>
|
</h1>
|
||||||
<p className="font-medium">Opportunity Lens management panel</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-1 items-center justify-end gap-2 min-[375px]:gap-4">
|
<div className="flex flex-1 items-center justify-end gap-2 min-[375px]:gap-4">
|
||||||
|
|||||||
@@ -4,12 +4,18 @@ import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
|||||||
import ConfirmationModal from "@/components/ui/ConfirmationModal";
|
import ConfirmationModal from "@/components/ui/ConfirmationModal";
|
||||||
import ListHeader from "@/components/ui/ListHeader";
|
import ListHeader from "@/components/ui/ListHeader";
|
||||||
import Modal from "@/components/ui/modal";
|
import Modal from "@/components/ui/modal";
|
||||||
import { Table, TableBody, TableCell, TableHead } from "@/components/ui/table";
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
import TableSkeleton from "@/components/ui/TableSkeleton";
|
import TableSkeleton from "@/components/ui/TableSkeleton";
|
||||||
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
||||||
import { getPaginatedRowNumber, unixToDate } from "@/utils/shared";
|
import { getPaginatedRowNumber, unixToDate } from "@/utils/shared";
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import { TableHeader, TableRow } from "../../ui/table";
|
|
||||||
import CmsListFilters from "./CmsListFilters";
|
import CmsListFilters from "./CmsListFilters";
|
||||||
import useCmsTablePresenter from "./useCmsTablePresenter";
|
import useCmsTablePresenter from "./useCmsTablePresenter";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { PencilSquareIcon, TrashIcon } from "@/assets/icons";
|
|||||||
import { Switch } from "@/components/FormElements/switch";
|
import { Switch } from "@/components/FormElements/switch";
|
||||||
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
||||||
import ConfirmationModal from "@/components/ui/ConfirmationModal";
|
import ConfirmationModal from "@/components/ui/ConfirmationModal";
|
||||||
import IsVisible from "@/components/ui/IsVisible";
|
import ListHeader from "@/components/ui/ListHeader";
|
||||||
|
import Modal from "@/components/ui/modal";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -15,21 +16,19 @@ import {
|
|||||||
import TableSkeleton from "@/components/ui/TableSkeleton";
|
import TableSkeleton from "@/components/ui/TableSkeleton";
|
||||||
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
||||||
import { getPaginatedRowNumber, unixToDate } from "@/utils/shared";
|
import { getPaginatedRowNumber, unixToDate } from "@/utils/shared";
|
||||||
|
import { startTransition } from "react";
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import { Skeleton } from "../../../ui/skeleton";
|
|
||||||
import useCompanyCategoriesPresenter from "./useCompanyCategoriesPresenter";
|
|
||||||
import ListHeader from "@/components/ui/ListHeader";
|
|
||||||
import CompanyCategoryListFilters from "./CompanyCategoryListFilters";
|
import CompanyCategoryListFilters from "./CompanyCategoryListFilters";
|
||||||
import Modal from "@/components/ui/modal";
|
import useCompanyCategoriesPresenter from "./useCompanyCategoriesPresenter";
|
||||||
|
|
||||||
const CompanyCategoriesTable = () => {
|
const CompanyCategoriesTable = () => {
|
||||||
const {
|
const {
|
||||||
categories,
|
optimisticCategories,
|
||||||
|
addOptimisticPublished,
|
||||||
isLoading,
|
isLoading,
|
||||||
pathName,
|
pathName,
|
||||||
router,
|
router,
|
||||||
currentCategory,
|
currentCategory,
|
||||||
isToggling,
|
|
||||||
togglePublished,
|
togglePublished,
|
||||||
onConfirmDelete,
|
onConfirmDelete,
|
||||||
setCurrentCategory,
|
setCurrentCategory,
|
||||||
@@ -42,18 +41,10 @@ const CompanyCategoriesTable = () => {
|
|||||||
search,
|
search,
|
||||||
watch,
|
watch,
|
||||||
setFilterValue,
|
setFilterValue,
|
||||||
|
columns,
|
||||||
isMutating,
|
isMutating,
|
||||||
} = useCompanyCategoriesPresenter();
|
} = useCompanyCategoriesPresenter();
|
||||||
|
|
||||||
const columns = [
|
|
||||||
"Row",
|
|
||||||
"Title",
|
|
||||||
"Description",
|
|
||||||
"Created at",
|
|
||||||
"Published",
|
|
||||||
"Actions",
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card">
|
<div className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card">
|
||||||
<ListHeader
|
<ListHeader
|
||||||
@@ -74,11 +65,11 @@ const CompanyCategoriesTable = () => {
|
|||||||
{isLoading && <TableSkeleton column={columns.length} />}
|
{isLoading && <TableSkeleton column={columns.length} />}
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<EmptyListWrapper
|
<EmptyListWrapper
|
||||||
list={categories ?? []}
|
list={optimisticCategories}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
emptyMessage="No categories were found"
|
emptyMessage="No categories were found"
|
||||||
>
|
>
|
||||||
{(categories ?? []).map((category, index) => (
|
{optimisticCategories.map((category, index) => (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={category.id}
|
key={category.id}
|
||||||
className="odd:bg-gray-2 dark:odd:bg-gray-7"
|
className="odd:bg-gray-2 dark:odd:bg-gray-7"
|
||||||
@@ -92,21 +83,21 @@ const CompanyCategoriesTable = () => {
|
|||||||
{unixToDate({ unix: category.created_at, hasTime: true })}
|
{unixToDate({ unix: category.created_at, hasTime: true })}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell colSpan={100}>
|
<TableCell colSpan={100}>
|
||||||
<IsVisible condition={isToggling}>
|
<Switch
|
||||||
<Skeleton className="h-8 bg-neutral-400 dark:bg-dark-4" />
|
withIcon
|
||||||
</IsVisible>
|
onToggle={() => {}}
|
||||||
<IsVisible condition={!isToggling}>
|
onChange={() => {
|
||||||
<Switch
|
const nextPublished = !category.published;
|
||||||
withIcon
|
startTransition(async () => {
|
||||||
onChange={(e) => {
|
addOptimisticPublished({
|
||||||
togglePublished(category.id);
|
id: category.id,
|
||||||
}}
|
published: nextPublished,
|
||||||
checked={category.published}
|
});
|
||||||
onToggle={(e) => {
|
await togglePublished(category.id);
|
||||||
console.log(e);
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
checked={category.published}
|
||||||
</IsVisible>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-start xl:pr-7.5">
|
<TableCell className="text-start xl:pr-7.5">
|
||||||
<div className="flex items-center justify-start gap-x-3.5">
|
<div className="flex items-center justify-start gap-x-3.5">
|
||||||
|
|||||||
+23
-4
@@ -9,9 +9,11 @@ import { TCompanyCategory } from "@/lib/api";
|
|||||||
import { deleteEmptyKeys } from "@/utils/shared";
|
import { deleteEmptyKeys } from "@/utils/shared";
|
||||||
import { useIsMutating } from "@tanstack/react-query";
|
import { useIsMutating } from "@tanstack/react-query";
|
||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useOptimistic, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
type TogglePublishedOptimistic = Pick<TCompanyCategory, "id" | "published">;
|
||||||
|
|
||||||
const useCompanyCategoriesPresenter = () => {
|
const useCompanyCategoriesPresenter = () => {
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
|
const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
|
||||||
@@ -42,10 +44,17 @@ const useCompanyCategoriesPresenter = () => {
|
|||||||
}, [searchParams, filterFormReset]);
|
}, [searchParams, filterFormReset]);
|
||||||
|
|
||||||
const { data, isLoading, isError } = useCompanyCategoriesQuery(params);
|
const { data, isLoading, isError } = useCompanyCategoriesQuery(params);
|
||||||
|
const categories = data?.data.categories;
|
||||||
|
const [optimisticCategories, addOptimisticPublished] = useOptimistic<
|
||||||
|
TCompanyCategory[],
|
||||||
|
TogglePublishedOptimistic
|
||||||
|
>(categories ?? [], (state, { id, published }) =>
|
||||||
|
state.map((c) => (c.id === id ? { ...c, published } : c)),
|
||||||
|
);
|
||||||
const { mutate: deleteCategory } = useDeleteCompanyCategoryQuery(() =>
|
const { mutate: deleteCategory } = useDeleteCompanyCategoryQuery(() =>
|
||||||
setIsModalOpen(false),
|
setIsModalOpen(false),
|
||||||
);
|
);
|
||||||
const { mutate: togglePublished, isPending: isToggling } =
|
const { mutateAsync: togglePublished } =
|
||||||
useToggleCompanyCategoryPublished();
|
useToggleCompanyCategoryPublished();
|
||||||
|
|
||||||
const search = (data: any) => {
|
const search = (data: any) => {
|
||||||
@@ -60,19 +69,29 @@ const useCompanyCategoriesPresenter = () => {
|
|||||||
if (!currentCategory) return;
|
if (!currentCategory) return;
|
||||||
deleteCategory(currentCategory?.id);
|
deleteCategory(currentCategory?.id);
|
||||||
};
|
};
|
||||||
|
const columns = [
|
||||||
|
"Row",
|
||||||
|
"Title",
|
||||||
|
"Description",
|
||||||
|
"Created at",
|
||||||
|
"Published",
|
||||||
|
"Actions",
|
||||||
|
];
|
||||||
return {
|
return {
|
||||||
categories: data?.data.categories,
|
categories,
|
||||||
|
optimisticCategories,
|
||||||
|
addOptimisticPublished,
|
||||||
isModalOpen,
|
isModalOpen,
|
||||||
setIsModalOpen,
|
setIsModalOpen,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
pathName,
|
pathName,
|
||||||
router,
|
router,
|
||||||
|
columns,
|
||||||
params,
|
params,
|
||||||
setParams,
|
setParams,
|
||||||
currentCategory,
|
currentCategory,
|
||||||
togglePublished,
|
togglePublished,
|
||||||
isToggling,
|
|
||||||
setCurrentCategory,
|
setCurrentCategory,
|
||||||
onConfirmDelete,
|
onConfirmDelete,
|
||||||
isFilterModalOpen,
|
isFilterModalOpen,
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ interface IProps {
|
|||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
||||||
const { errors, handleSubmit, register, onSubmit } =
|
const { errors, handleSubmit, register, onSubmit } = useCreateAdminPresenter({
|
||||||
useCreateAdminPresenter({
|
editMode,
|
||||||
editMode,
|
defaultValues,
|
||||||
defaultValues,
|
id,
|
||||||
id,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
@@ -30,7 +29,7 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<div className="flex flex-col gap-9">
|
<div className="flex flex-col gap-9">
|
||||||
<ShowcaseSection
|
<ShowcaseSection
|
||||||
title="Input Fields"
|
title="Input Fields"
|
||||||
className="grid grid-cols-1 gap-5 md:grid-cols-2"
|
className="grid grid-cols-1 gap-3 md:grid-cols-2"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
@@ -46,7 +45,7 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
label="Full name"
|
label="Full name"
|
||||||
placeholder="Full name"
|
placeholder="Enter full name (5-100 characters)"
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
name="full_name"
|
name="full_name"
|
||||||
@@ -61,7 +60,7 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<div>
|
<div>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
label="Username"
|
label="Username"
|
||||||
placeholder="Username"
|
placeholder="Letters and numbers only (3-30)"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
@@ -96,7 +95,7 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<InputGroup
|
<InputGroup
|
||||||
label="Email"
|
label="Email"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
placeholder="Email"
|
placeholder="you@example.com"
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
{...register("email", {
|
{...register("email", {
|
||||||
@@ -121,8 +120,8 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<div>
|
<div>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
label="Password"
|
label="Password"
|
||||||
placeholder="Password"
|
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
|
placeholder="At least 8 characters"
|
||||||
required
|
required
|
||||||
type="password"
|
type="password"
|
||||||
{...register("password", {
|
{...register("password", {
|
||||||
@@ -152,7 +151,6 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<div>
|
<div>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
label="Phone"
|
label="Phone"
|
||||||
placeholder="Phone"
|
|
||||||
type="text"
|
type="text"
|
||||||
{...register("phone", {
|
{...register("phone", {
|
||||||
minLength: {
|
minLength: {
|
||||||
@@ -163,9 +161,9 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
value: 20,
|
value: 20,
|
||||||
message: FormErrorMessages.maxLength(20),
|
message: FormErrorMessages.maxLength(20),
|
||||||
},
|
},
|
||||||
pattern:{
|
pattern: {
|
||||||
value:REGEX.PHONE,
|
value: REGEX.PHONE,
|
||||||
message:FormErrorMessages.invalidPattern
|
message: FormErrorMessages.invalidPattern,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
name="phone"
|
name="phone"
|
||||||
@@ -180,7 +178,6 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<div>
|
<div>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
label="Department"
|
label="Department"
|
||||||
placeholder="Department"
|
|
||||||
type="text"
|
type="text"
|
||||||
{...register("department", {
|
{...register("department", {
|
||||||
minLength: {
|
minLength: {
|
||||||
@@ -204,7 +201,6 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
<div>
|
<div>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
label="Position"
|
label="Position"
|
||||||
placeholder="Position"
|
|
||||||
type="text"
|
type="text"
|
||||||
{...register("position", {
|
{...register("position", {
|
||||||
minLength: {
|
minLength: {
|
||||||
@@ -230,7 +226,6 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
|||||||
type="file"
|
type="file"
|
||||||
fileStyleVariant="style1"
|
fileStyleVariant="style1"
|
||||||
label="Attach file"
|
label="Attach file"
|
||||||
placeholder="Attach file"
|
|
||||||
register={register}
|
register={register}
|
||||||
name="profile_image"
|
name="profile_image"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
API_ENDPOINTS,
|
API_ENDPOINTS,
|
||||||
companiesService,
|
companiesService,
|
||||||
companyCategoriesService,
|
companyCategoriesService,
|
||||||
|
type TCompanyCategoryApiResponse,
|
||||||
} from "@/lib/api";
|
} from "@/lib/api";
|
||||||
import {
|
import {
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
@@ -10,7 +11,7 @@ import {
|
|||||||
useQueryClient,
|
useQueryClient,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
export const useCreateCompany = () => {
|
export const useCreateCompany = () => {
|
||||||
@@ -171,6 +172,30 @@ export const useDeleteCompanyCategoryQuery = (successCallback?: () => void) => {
|
|||||||
};
|
};
|
||||||
export const useToggleCompanyCategoryPublished = () => {
|
export const useToggleCompanyCategoryPublished = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const updateOptimisticCategories = useCallback((categoryId: string) => {
|
||||||
|
queryClient.setQueriesData<TCompanyCategoryApiResponse>(
|
||||||
|
{
|
||||||
|
queryKey: [API_ENDPOINTS.COMPANIES.CATEGORIES.READ_ALL],
|
||||||
|
exact: false,
|
||||||
|
},
|
||||||
|
(previousList) => {
|
||||||
|
if (!previousList?.data?.categories) {
|
||||||
|
return previousList;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...previousList,
|
||||||
|
data: {
|
||||||
|
...previousList.data,
|
||||||
|
categories: previousList.data.categories.map((category) =>
|
||||||
|
category.id === categoryId
|
||||||
|
? { ...category, published: !category.published }
|
||||||
|
: category,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}, [queryClient]);
|
||||||
const mutationKey = useMemo(
|
const mutationKey = useMemo(
|
||||||
() => [
|
() => [
|
||||||
API_ENDPOINTS.COMPANIES.CATEGORIES.TOGGLE_PUBLISHED("published"),
|
API_ENDPOINTS.COMPANIES.CATEGORIES.TOGGLE_PUBLISHED("published"),
|
||||||
@@ -181,8 +206,17 @@ export const useToggleCompanyCategoryPublished = () => {
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey,
|
mutationKey,
|
||||||
mutationFn: companyCategoriesService.togglePublished,
|
mutationFn: companyCategoriesService.togglePublished,
|
||||||
onSuccess: ({ data }: { data: { message: string } }) => {
|
onSuccess: (toggleResponse, categoryId: string) => {
|
||||||
toast.success(data.message);
|
const responseBody = toggleResponse as {
|
||||||
|
data?: { message?: string };
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
const toastMessage =
|
||||||
|
responseBody.data?.message ?? responseBody.message;
|
||||||
|
if (toastMessage) {
|
||||||
|
toast.success(toastMessage);
|
||||||
|
}
|
||||||
|
updateOptimisticCategories(categoryId);
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: [API_ENDPOINTS.COMPANIES.CATEGORIES.READ_ALL],
|
queryKey: [API_ENDPOINTS.COMPANIES.CATEGORIES.READ_ALL],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user