feat(form): add loading state and replace custom switch component

This commit introduces a loading state for form submissions and replaces the custom switch component with a third-party library for improved user experience and maintainability.

- Adds `react-switch` as a dependency to replace the custom-built switch component, providing a more robust and feature-rich solution.
- Implements a loading indicator on form submission buttons and in the confirmation modal by utilizing the `useIsMutating` hook from TanStack Query. This provides clear visual feedback to the user during asynchronous operations.
- Creates a reusable `FormFooter` component to encapsulate the submit and cancel buttons, centralizing the form submission logic and loading state.
- Refactors the company create/edit forms to use the new `FormFooter` and `react-switch` components.
- Moves the `BreadcrumbItem` type to a shared types file for better code organization.
This commit is contained in:
AmirReza Jamali
2025-09-20 16:13:04 +03:30
parent 2c43e945c2
commit e6493f5d83
27 changed files with 782 additions and 84 deletions
+8
View File
@@ -19,6 +19,14 @@ export const API_ENDPOINTS = {
UPDATE: (id: string) => `companies/${id}`,
DELETE: (id: string) => `companies/${id}`,
DETAILS: (id: string) => `companies/${id}`,
CATEGORIES: {
READ_ALL: "company-categories",
CREATE: "company-categories",
UPDATE: (id: string) => `company-categories/${id}`,
DELETE: (id: string) => `company-categories/${id}`,
DETAILS: (id: string) => `company-categories/${id}`,
TOGGLE_PUBLISHED:(id:string) => `company-categories/${id}/publish`,
},
},
TENDERS: {
READ_ALL: "tenders",
+84
View File
@@ -4,6 +4,8 @@ import {
ApiResponse,
CompaniesListResponseSchema,
TCompaniesResponse,
TCompanyCategoryApiResponse,
TCreateCompanyCategoryCredentials,
} from "../types";
import { ICreateCompanyCredentials } from "../types";
@@ -58,3 +60,85 @@ export const companiesService = {
}
},
};
export const companyCategoriesService = {
getCategories: async (
params?: Record<string, any>,
): Promise<TCompanyCategoryApiResponse> => {
try {
const response = await api.get(
API_ENDPOINTS.COMPANIES.CATEGORIES.READ_ALL,
{
params,
},
);
return response.data;
} catch (error) {
console.error("ERROR Caught in company categories service: ", error);
throw error;
}
},
getSingleCategory: async (id: string) => {
try {
return (await api.get(API_ENDPOINTS.COMPANIES.CATEGORIES.DETAILS(id)))
.data;
} catch (error) {
console.error(
"ERROR caught in Company categories service, Get single category",
error,
);
throw error;
}
},
createCategory: async (credentials: TCreateCompanyCategoryCredentials) => {
try {
const response = await api.post(
API_ENDPOINTS.COMPANIES.CATEGORIES.CREATE,
credentials,
);
return response.data;
} catch (error) {
console.error("ERROR Caught in company categories service: ", error);
throw error;
}
},
deleteCategory: async (id: string) => {
try {
return (await api.delete(API_ENDPOINTS.COMPANIES.CATEGORIES.DELETE(id)))
.data;
} catch (error) {
console.error(
"ERROR caught in Company Categories Services Delete:",
error,
);
throw error;
}
},
togglePublished: async (id: string) => {
try {
return (
await api.patch(API_ENDPOINTS.COMPANIES.CATEGORIES.TOGGLE_PUBLISHED(id))
).data;
} catch (error) {
console.error(
"ERROR caught in Company Categories Services Toggle Published:",
error,
);
throw error;
}
},
updateCategories: async ({
id,
credentials,
}: {
id: string;
credentials: TCreateCompanyCategoryCredentials;
}) => {
const response = await api.put(
API_ENDPOINTS.COMPANIES.CATEGORIES.UPDATE(id),
credentials,
);
return response.data;
},
};
+29
View File
@@ -0,0 +1,29 @@
import { z } from "zod";
import { createApiResponseSchema } from "./Factory";
export const companyCategorySchema = z.object({
created_at: z.number(),
description: z.string(),
id: z.string(),
name: z.string(),
published: z.boolean(),
published_at: z.number(),
thumbnail: z.string(),
updated_at: z.number(),
});
export const createCompanyCategoryCredentials = z.object({
name: z.string(),
description: z.string(),
thumbnail: z.string().optional(),
});
export const companyCategoriesResponseSchema = createApiResponseSchema(
z.object({
categories: z.array(companyCategorySchema),
}),
);
export type TCompanyCategory = z.infer<typeof companyCategorySchema>;
export type TCompanyCategoryApiResponse = z.infer<
typeof companyCategoriesResponseSchema
>;
export type TCreateCompanyCategoryCredentials = z.infer<
typeof createCompanyCategoryCredentials
>;
+6 -5
View File
@@ -1,7 +1,8 @@
export * from "./shared";
export * from "./User";
export * from "./Posts";
export * from "./Factory";
export * from "./TCompany";
export * from "./CompanyCategories";
export * from "./Customers";
export * from "./Factory";
export * from "./Posts";
export * from "./Profile";
export * from "./shared";
export * from "./TCompany";
export * from "./User";