updated Api Defaults
This commit is contained in:
@@ -1,24 +1,17 @@
|
||||
"use server";
|
||||
|
||||
import { fetchData, fetchDataWithToken } from "../api-fetcher";
|
||||
import { fetchDataWithToken } from "../api-fetcher";
|
||||
import { baseUrlApplication } from "../basics";
|
||||
import { PageListOptions, PaginateOnly } from "../schemas/list";
|
||||
import { PaginationParams } from "../schemas/list";
|
||||
import type { PaginatedApiResponse } from "@/app/api/utils/types";
|
||||
|
||||
const applicationListEndpoint = `${baseUrlApplication}/application/list`;
|
||||
const applicationUpdateEndpoint = `${baseUrlApplication}/application/update`;
|
||||
const applicationCreateEndpoint = `${baseUrlApplication}/application/create`;
|
||||
const applicationDeleteEndpoint = `${baseUrlApplication}/application/delete`;
|
||||
|
||||
interface RequestApplication {
|
||||
name: string;
|
||||
application_code: string;
|
||||
site_url: string;
|
||||
application_type: string;
|
||||
application_for: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
async function listApplications(payload: PageListOptions) {
|
||||
async function listApplications(payload: PaginationParams): Promise<PaginatedApiResponse<any>> {
|
||||
try {
|
||||
const response = await fetchDataWithToken(
|
||||
applicationListEndpoint,
|
||||
@@ -32,12 +25,62 @@ async function listApplications(payload: PageListOptions) {
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
return response?.status === 200 || response?.status === 202
|
||||
? response.data
|
||||
: null;
|
||||
|
||||
// Format the response to match the expected PaginatedApiResponse format
|
||||
if (response?.status === 200 || response?.status === 202) {
|
||||
const responseData = response.data as any;
|
||||
return {
|
||||
data: responseData.data || [],
|
||||
pagination: {
|
||||
page: responseData.pagination?.page || 1,
|
||||
size: responseData.pagination?.size || 10,
|
||||
totalCount: responseData.pagination?.totalCount || 0,
|
||||
totalItems: responseData.pagination?.totalItems || 0,
|
||||
totalPages: responseData.pagination?.totalPages || 0,
|
||||
pageCount: responseData.pagination?.pageCount || 0,
|
||||
orderField: responseData.pagination?.orderField || ['name'],
|
||||
orderType: responseData.pagination?.orderType || ['asc'],
|
||||
query: responseData.pagination?.query || {},
|
||||
next: responseData.pagination?.next || false,
|
||||
back: responseData.pagination?.back || false
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
data: [],
|
||||
pagination: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
totalCount: 0,
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
pageCount: 0,
|
||||
orderField: ['name'],
|
||||
orderType: ['asc'],
|
||||
query: {},
|
||||
next: false,
|
||||
back: false
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching application list:", error);
|
||||
return null;
|
||||
// Return a default empty response instead of null to match the expected return type
|
||||
return {
|
||||
data: [],
|
||||
pagination: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
totalCount: 0,
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
pageCount: 0,
|
||||
orderField: ['name'],
|
||||
orderType: ['asc'],
|
||||
query: {},
|
||||
next: false,
|
||||
back: false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,54 +39,6 @@ const cookieObject: any = {
|
||||
priority: "high",
|
||||
};
|
||||
|
||||
interface FilterListInterface {
|
||||
page?: number | null | undefined;
|
||||
size?: number | null | undefined;
|
||||
orderField?: string | null | undefined;
|
||||
orderType?: string | null | undefined;
|
||||
includeJoins?: any[] | null | undefined;
|
||||
query?: any | null | undefined;
|
||||
}
|
||||
|
||||
class FilterList {
|
||||
page: number;
|
||||
size: number;
|
||||
orderField: string;
|
||||
orderType: string;
|
||||
includeJoins: any[];
|
||||
query: any;
|
||||
|
||||
constructor({
|
||||
page = 1,
|
||||
size = 5,
|
||||
orderField = "id",
|
||||
orderType = "asc",
|
||||
includeJoins = [],
|
||||
query = {},
|
||||
}: FilterListInterface = {}) {
|
||||
this.page = page ?? 1;
|
||||
this.size = size ?? 5;
|
||||
this.orderField = orderField ?? "uu_id";
|
||||
this.orderType = orderType ?? "asc";
|
||||
this.orderType = this.orderType.startsWith("a") ? "asc" : "desc";
|
||||
this.includeJoins = includeJoins ?? [];
|
||||
this.query = query ?? {};
|
||||
}
|
||||
|
||||
filter() {
|
||||
return {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
orderField: this.orderField,
|
||||
orderType: this.orderType,
|
||||
includeJoins: this.includeJoins,
|
||||
query: this.query,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const defaultFilterList = new FilterList({});
|
||||
|
||||
// Constants
|
||||
const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
||||
const defaultHeaders = {
|
||||
@@ -102,18 +54,11 @@ const DEFAULT_RESPONSE: ApiResponse = {
|
||||
data: {},
|
||||
};
|
||||
|
||||
export type {
|
||||
FilterList,
|
||||
FilterListInterface,
|
||||
HttpMethod,
|
||||
ApiResponse,
|
||||
FetchOptions,
|
||||
};
|
||||
export type { HttpMethod, ApiResponse, FetchOptions };
|
||||
export {
|
||||
DEFAULT_TIMEOUT,
|
||||
DEFAULT_RESPONSE,
|
||||
defaultHeaders,
|
||||
defaultFilterList,
|
||||
baseUrlAuth,
|
||||
baseUrlPeople,
|
||||
baseUrlApplication,
|
||||
|
||||
@@ -5,7 +5,7 @@ export interface PaginateOnly {
|
||||
orderType?: string[];
|
||||
}
|
||||
|
||||
export interface PageListOptions {
|
||||
export interface PaginationParams {
|
||||
page?: number;
|
||||
size?: number;
|
||||
orderField?: string[];
|
||||
|
||||
@@ -18,7 +18,7 @@ export default async function DashLayout({
|
||||
}
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="h-full w-full overflow-y-auto">{children}</div>
|
||||
<div className="h-full w-full">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { NextRequest } from "next/server";
|
||||
// Import the createApplication function when it's available
|
||||
// import { createApplication } from "@/apicalls/application/application";
|
||||
import { createCreateHandler } from "../../utils";
|
||||
import { createCreateHandler } from "@/app/api/utils";
|
||||
import { createApplication } from "@/apicalls/application/application";
|
||||
|
||||
// Create a handler for creating applications using our utility function
|
||||
// When createApplication is available, pass it as the first argument
|
||||
// No need for field validation as it's already handled by Zod at the page level
|
||||
export const POST = createCreateHandler();
|
||||
export const POST = createCreateHandler(createApplication);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { listApplications } from "@/apicalls/application/application";
|
||||
import { createListHandler } from "../../utils";
|
||||
import { createListHandler } from "@/app/api/utils";
|
||||
|
||||
// Create a handler for listing applications using our utility function
|
||||
export const POST = createListHandler(listApplications);
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import { NextRequest } from "next/server";
|
||||
// Import the updateApplication function when it's available
|
||||
// import { updateApplication } from "@/apicalls/application/application";
|
||||
import { createUpdateHandler } from "../../utils";
|
||||
import { updateApplication } from "@/apicalls/application/application";
|
||||
|
||||
// Create a handler for updating applications using our utility function
|
||||
// When updateApplication is available, pass it as the first argument
|
||||
// No need for field validation as it's already handled by Zod at the page level
|
||||
// We only need to ensure 'id' is present for updates
|
||||
export const POST = createUpdateHandler();
|
||||
export const POST = createUpdateHandler(updateApplication);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
successResponse,
|
||||
errorResponse,
|
||||
paginationResponse,
|
||||
createResponse,
|
||||
updateResponse,
|
||||
deleteResponse
|
||||
import {
|
||||
successResponse,
|
||||
errorResponse,
|
||||
paginationResponse,
|
||||
createResponse,
|
||||
updateResponse,
|
||||
deleteResponse,
|
||||
} from "./responseHandlers";
|
||||
import { withErrorHandling, validateRequiredFields } from "./requestHandlers";
|
||||
import {
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
ListFunction,
|
||||
CreateFunction,
|
||||
UpdateFunction,
|
||||
DeleteFunction
|
||||
DeleteFunction,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
* @param body Request body
|
||||
* @param listFunction The function to call to get the list data
|
||||
*/
|
||||
export async function handleListOperation<T>(
|
||||
export async function handleListOperation(
|
||||
request: NextRequest,
|
||||
body: any,
|
||||
listFunction: ListFunction
|
||||
@@ -31,11 +31,11 @@ export async function handleListOperation<T>(
|
||||
// Extract pagination parameters with defaults
|
||||
const page = body.page || 1;
|
||||
const size = body.size || 10;
|
||||
|
||||
|
||||
// Extract sorting parameters with defaults
|
||||
const orderField = body.orderField || ["name"];
|
||||
const orderType = body.orderType || ["asc"];
|
||||
|
||||
|
||||
// Extract query filters
|
||||
const query = body.query || {};
|
||||
|
||||
@@ -59,8 +59,7 @@ export async function handleListOperation<T>(
|
||||
* @param createFunction The function to call to create the item
|
||||
* @param requiredFields Array of required field names
|
||||
*/
|
||||
export async function handleCreateOperation<T>(
|
||||
request: NextRequest,
|
||||
export async function handleCreateOperation(
|
||||
body: any,
|
||||
createFunction?: CreateFunction,
|
||||
requiredFields: string[] = []
|
||||
@@ -91,23 +90,28 @@ export async function handleCreateOperation<T>(
|
||||
* @param request NextRequest object
|
||||
* @param body Request body
|
||||
* @param updateFunction The function to call to update the item
|
||||
* @param requiredFields Array of required field names
|
||||
*/
|
||||
export async function handleUpdateOperation<T>(
|
||||
export async function handleUpdateOperation(
|
||||
request: NextRequest,
|
||||
body: any,
|
||||
updateFunction?: UpdateFunction,
|
||||
requiredFields: string[] = ['id']
|
||||
updateFunction?: UpdateFunction
|
||||
) {
|
||||
// Validate required fields
|
||||
const validation = validateRequiredFields(body, requiredFields);
|
||||
if (!validation.valid) {
|
||||
return errorResponse(validation.error as string, 400);
|
||||
// Skip validation as it's handled at the page level with Yup
|
||||
// and IDs are extracted from URL paths for update/delete operations
|
||||
|
||||
// Get UUID from query string. This is the value of the "uuid" query
|
||||
// string parameter, e.g. if the URL is "?uuid=SOMEUUID", this will
|
||||
// be "SOMEUUID".
|
||||
const uuid = request.nextUrl.searchParams.get("uuid");
|
||||
console.log("UUID:", uuid);
|
||||
|
||||
if (!uuid) {
|
||||
return errorResponse("UUID not found", 400);
|
||||
}
|
||||
|
||||
// If an update function is provided, call it
|
||||
if (updateFunction) {
|
||||
const result = await updateFunction(body.id, body);
|
||||
console.log("Body:", body);
|
||||
const result = await updateFunction(body, uuid);
|
||||
return updateResponse(result);
|
||||
}
|
||||
|
||||
@@ -118,23 +122,26 @@ export async function handleUpdateOperation<T>(
|
||||
/**
|
||||
* Generic delete operation handler
|
||||
* @param request NextRequest object
|
||||
* @param body Request body
|
||||
* @param deleteFunction The function to call to delete the item
|
||||
*/
|
||||
export async function handleDeleteOperation(
|
||||
request: NextRequest,
|
||||
body: any,
|
||||
deleteFunction?: DeleteFunction
|
||||
) {
|
||||
// Validate that ID is provided
|
||||
const validation = validateRequiredFields(body, ['id']);
|
||||
if (!validation.valid) {
|
||||
return errorResponse(validation.error as string, 400);
|
||||
// Skip ID validation as it's handled at the page level
|
||||
// and IDs are typically extracted from URL paths
|
||||
|
||||
// Get UUID from query string. This is the value of the "uuid" query
|
||||
// string parameter, e.g. if the URL is "?uuid=SOMEUUID", this will
|
||||
// be "SOMEUUID".
|
||||
const uuid = request.nextUrl.searchParams.get("uuid");
|
||||
|
||||
if (!uuid) {
|
||||
return errorResponse("UUID not found", 400);
|
||||
}
|
||||
|
||||
// If a delete function is provided, call it
|
||||
if (deleteFunction) {
|
||||
await deleteFunction(body.id);
|
||||
await deleteFunction(uuid);
|
||||
}
|
||||
|
||||
// Return a success response
|
||||
@@ -146,7 +153,7 @@ export async function handleDeleteOperation(
|
||||
* @param listFunction The function to call to get the list data
|
||||
*/
|
||||
export function createListHandler(listFunction: ListFunction) {
|
||||
return withErrorHandling((request, body) =>
|
||||
return withErrorHandling((request: NextRequest, body: any) =>
|
||||
handleListOperation(request, body, listFunction)
|
||||
);
|
||||
}
|
||||
@@ -160,22 +167,19 @@ export function createCreateHandler(
|
||||
createFunction?: CreateFunction,
|
||||
requiredFields: string[] = []
|
||||
) {
|
||||
return withErrorHandling((request, body) =>
|
||||
handleCreateOperation(request, body, createFunction, requiredFields)
|
||||
console.log("Required fields:", requiredFields);
|
||||
return withErrorHandling((body: any) =>
|
||||
handleCreateOperation(body, createFunction, requiredFields)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped update handler with error handling
|
||||
* @param updateFunction The function to call to update the item
|
||||
* @param requiredFields Array of required field names
|
||||
*/
|
||||
export function createUpdateHandler(
|
||||
updateFunction?: UpdateFunction,
|
||||
requiredFields: string[] = ['id']
|
||||
) {
|
||||
return withErrorHandling((request, body) =>
|
||||
handleUpdateOperation(request, body, updateFunction, requiredFields)
|
||||
export function createUpdateHandler(updateFunction?: UpdateFunction) {
|
||||
return withErrorHandling((request: NextRequest, body: any) =>
|
||||
handleUpdateOperation(request, body, updateFunction)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,10 +187,8 @@ export function createUpdateHandler(
|
||||
* Create a wrapped delete handler with error handling
|
||||
* @param deleteFunction The function to call to delete the item
|
||||
*/
|
||||
export function createDeleteHandler(
|
||||
deleteFunction?: DeleteFunction
|
||||
) {
|
||||
return withErrorHandling((request, body) =>
|
||||
handleDeleteOperation(request, body, deleteFunction)
|
||||
export function createDeleteHandler(deleteFunction?: DeleteFunction) {
|
||||
return withErrorHandling((request: NextRequest) =>
|
||||
handleDeleteOperation(request, deleteFunction)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ export function CreateComponent<T>({
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
reset,
|
||||
} = useForm<Record<string, any>>({
|
||||
defaultValues,
|
||||
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
||||
@@ -71,14 +72,37 @@ export function CreateComponent<T>({
|
||||
|
||||
const formValues = watch();
|
||||
|
||||
// Get language-specific validation schema if available
|
||||
useEffect(() => {
|
||||
if (formProps.schemaPath) {
|
||||
const loadLanguageValidationSchema = async () => {
|
||||
try {
|
||||
// Dynamic import of the schema module
|
||||
const schemaModule = await import(formProps.schemaPath);
|
||||
|
||||
// Check if language-specific schema functions are available
|
||||
if (schemaModule.getCreateApplicationSchema) {
|
||||
const langValidationSchema = schemaModule.getCreateApplicationSchema(lang as "en" | "tr");
|
||||
|
||||
// Reset the form with the current values
|
||||
reset(defaultValues);
|
||||
|
||||
// Update the validation schema in formProps for future validations
|
||||
formProps.validationSchema = langValidationSchema;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading language-specific validation schema:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadLanguageValidationSchema();
|
||||
}
|
||||
}, [lang, formProps.schemaPath, reset, defaultValues]);
|
||||
|
||||
// Handle form submission
|
||||
const onSubmit: SubmitHandler<Record<string, any>> = async (data) => {
|
||||
try {
|
||||
console.log("Form data to save:", data);
|
||||
|
||||
// Make an API call to save the data if apiUrl is provided
|
||||
if (apiUrl) {
|
||||
// Use the create endpoint by appending '/create' to the apiUrl
|
||||
const createUrl = `${apiUrl}/create`;
|
||||
const response = await fetch(createUrl, {
|
||||
method: 'POST',
|
||||
@@ -87,13 +111,13 @@ export function CreateComponent<T>({
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
console.log("Response:", response.ok);
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`);
|
||||
}
|
||||
|
||||
// Optional: get the created item from response
|
||||
// const createdItem = await response.json();
|
||||
|
||||
const createdItem = await response.json();
|
||||
console.log("Created item:", createdItem);
|
||||
}
|
||||
|
||||
if (refetch) refetch();
|
||||
@@ -116,23 +140,17 @@ export function CreateComponent<T>({
|
||||
|
||||
// Translate group names for display dynamically
|
||||
const getGroupTitle = (groupName: string) => {
|
||||
// First check if there's a translation for the exact group key
|
||||
// Check if we have a translation for this group name
|
||||
if (t[groupName]) {
|
||||
return t[groupName];
|
||||
}
|
||||
|
||||
// Try to format the group name in a more readable way if no translation exists
|
||||
// Convert camelCase or snake_case to Title Case with spaces
|
||||
// If no translation is found, just format the name as a fallback
|
||||
const formattedName = groupName
|
||||
// Insert space before capital letters and uppercase the first letter
|
||||
.replace(/([A-Z])/g, ' $1')
|
||||
// Replace underscores with spaces
|
||||
.replace(/_/g, ' ')
|
||||
// Capitalize first letter
|
||||
.replace(/^./, (str) => str.toUpperCase())
|
||||
// Capitalize each word
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
|
||||
return formattedName;
|
||||
};
|
||||
|
||||
@@ -234,9 +252,17 @@ export function CreateComponent<T>({
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="grid grid-cols-2 gap-4 pt-6 my-4">
|
||||
<Button type="button" variant="outline" onClick={onCancel} className="w-full">
|
||||
{t.cancel || "Cancel"}
|
||||
</Button>
|
||||
<Button type="submit" className="w-full">
|
||||
{t.save || "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
<Card className="w-full mb-6">
|
||||
<CardHeader>
|
||||
<CardTitle>{t.create || "Create"}</CardTitle>
|
||||
@@ -269,14 +295,7 @@ export function CreateComponent<T>({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-2 pt-6">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
{t.cancel || "Cancel"}
|
||||
</Button>
|
||||
<Button type="submit">
|
||||
{t.save || "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</CardContent>
|
||||
</Card>
|
||||
</form>
|
||||
|
||||
@@ -18,16 +18,16 @@ export function FormDisplay<T>({
|
||||
apiUrl,
|
||||
}: FormDisplayProps<T>) {
|
||||
const [enhancedFormProps, setEnhancedFormProps] = useState(formProps);
|
||||
|
||||
// Dynamically import schema definitions if provided in formProps
|
||||
|
||||
// Update form props when language or mode changes
|
||||
useEffect(() => {
|
||||
const loadSchemaDefinitions = async () => {
|
||||
const updateFormProps = async () => {
|
||||
try {
|
||||
// Check if schemaPath is provided in formProps
|
||||
if (formProps.schemaPath) {
|
||||
// Dynamic import of the schema module
|
||||
const schemaModule = await import(formProps.schemaPath);
|
||||
|
||||
|
||||
// Get the appropriate field definitions based on mode
|
||||
let fieldDefs;
|
||||
if (schemaModule.fieldDefinitions?.getDefinitionsByMode) {
|
||||
@@ -39,39 +39,59 @@ export function FormDisplay<T>({
|
||||
} else if (mode === "view" && schemaModule.viewFieldDefinitions) {
|
||||
fieldDefs = schemaModule.viewFieldDefinitions;
|
||||
}
|
||||
|
||||
// Get the appropriate validation schema based on mode
|
||||
|
||||
// Get the appropriate validation schema based on mode and language
|
||||
let validationSchema;
|
||||
if (mode === "create" && schemaModule.CreateApplicationSchema) {
|
||||
validationSchema = schemaModule.CreateApplicationSchema;
|
||||
} else if (mode === "update" && schemaModule.UpdateApplicationSchema) {
|
||||
validationSchema = schemaModule.UpdateApplicationSchema;
|
||||
if (mode === "create" && schemaModule.getCreateApplicationSchema) {
|
||||
// Use language-aware schema factory function
|
||||
validationSchema = schemaModule.getCreateApplicationSchema(lang as "en" | "tr");
|
||||
} else if (mode === "update" && schemaModule.getUpdateApplicationSchema) {
|
||||
// Use language-aware schema factory function
|
||||
validationSchema = schemaModule.getUpdateApplicationSchema(lang as "en" | "tr");
|
||||
} else if (mode === "view" && schemaModule.ViewApplicationSchema) {
|
||||
validationSchema = schemaModule.ViewApplicationSchema;
|
||||
} else if (schemaModule.ApplicationSchema) {
|
||||
validationSchema = schemaModule.ApplicationSchema;
|
||||
}
|
||||
|
||||
|
||||
// Get the grouped field definitions structure if available
|
||||
const groupedFieldDefs = schemaModule.baseFieldDefinitions || {};
|
||||
|
||||
// Update form props with schema information
|
||||
|
||||
// Update form props with schema information and current language
|
||||
setEnhancedFormProps({
|
||||
...formProps,
|
||||
fieldDefinitions: fieldDefs || {},
|
||||
validationSchema,
|
||||
fieldsByMode: schemaModule.fieldsByMode || {},
|
||||
groupedFieldDefinitions: groupedFieldDefs,
|
||||
// Add current language to force child components to recognize changes
|
||||
currentLang: lang,
|
||||
// Add schema path for dynamic imports in child components
|
||||
schemaPath: formProps.schemaPath
|
||||
});
|
||||
} else {
|
||||
// If no schema path, just update with current language
|
||||
setEnhancedFormProps({
|
||||
...formProps,
|
||||
currentLang: lang
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading schema definitions:", error);
|
||||
// Even on error, update the language
|
||||
setEnhancedFormProps({
|
||||
...formProps,
|
||||
currentLang: lang
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
loadSchemaDefinitions();
|
||||
}, [formProps, mode, lang]); // Added lang as a dependency to ensure re-fetch when language changes
|
||||
|
||||
|
||||
updateFormProps();
|
||||
}, [formProps, mode, lang]); // Lang dependency ensures re-fetch when language changes
|
||||
|
||||
// Debug the props received by FormDisplay
|
||||
// FormDisplay component renders different form modes based on the mode prop
|
||||
|
||||
// Render the appropriate component based on the mode
|
||||
switch (mode) {
|
||||
case "create":
|
||||
@@ -89,9 +109,12 @@ export function FormDisplay<T>({
|
||||
/>
|
||||
);
|
||||
case "update":
|
||||
// Create a stable key for the component to ensure proper re-rendering
|
||||
const updateKey = `update-${lang}-${(initialData as any)?.uu_id || 'new'}`;
|
||||
|
||||
return initialData ? (
|
||||
<UpdateComponent<T>
|
||||
key={`update-${lang}`} // Add key with lang to force re-render on language change
|
||||
key={updateKey} // Add key with lang and item ID to force re-render
|
||||
initialData={initialData}
|
||||
refetch={refetch}
|
||||
setMode={setMode}
|
||||
@@ -100,6 +123,7 @@ export function FormDisplay<T>({
|
||||
lang={lang}
|
||||
translations={translations}
|
||||
formProps={enhancedFormProps}
|
||||
apiUrl={apiUrl}
|
||||
/>
|
||||
) : null;
|
||||
case "view":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { UpdateComponentProps, FieldDefinition } from "./types";
|
||||
@@ -21,6 +21,7 @@ export function UpdateComponent<T>({
|
||||
onCancel,
|
||||
lang,
|
||||
translations,
|
||||
apiUrl,
|
||||
formProps = {},
|
||||
}: UpdateComponentProps<T>) {
|
||||
const t = translations[lang as keyof typeof translations] || {};
|
||||
@@ -29,75 +30,179 @@ export function UpdateComponent<T>({
|
||||
const fieldDefinitions = formProps.fieldDefinitions || {};
|
||||
const validationSchema = formProps.validationSchema;
|
||||
|
||||
// Group fields by their group property
|
||||
// Ensure field definitions are processed only once
|
||||
const processedFieldDefinitions = useMemo(() => {
|
||||
const processed = { ...fieldDefinitions };
|
||||
// Make all fields editable except system fields
|
||||
Object.entries(processed).forEach(([fieldName, definition]) => {
|
||||
if (fieldName !== 'uu_id' && fieldName !== 'created_at' && fieldName !== 'updated_at') {
|
||||
(processed[fieldName] as FieldDefinition).readOnly = false;
|
||||
}
|
||||
});
|
||||
return processed;
|
||||
}, [fieldDefinitions]);
|
||||
|
||||
const [groupedFields, setGroupedFields] = useState<Record<string, FieldDefinition[]>>({});
|
||||
|
||||
// Process field definitions to group them
|
||||
useEffect(() => {
|
||||
if (Object.keys(fieldDefinitions).length > 0) {
|
||||
if (Object.keys(processedFieldDefinitions).length > 0) {
|
||||
const groups: Record<string, FieldDefinition[]> = {};
|
||||
|
||||
// Group fields by their group property
|
||||
Object.entries(fieldDefinitions).forEach(([fieldName, definition]) => {
|
||||
// Group the processed field definitions
|
||||
Object.entries(processedFieldDefinitions).forEach(([fieldName, definition]) => {
|
||||
// Convert to FieldDefinition type
|
||||
const def = definition as FieldDefinition;
|
||||
|
||||
// Add the field name to the definition
|
||||
const fieldDef = { ...def, name: fieldName };
|
||||
|
||||
// Add to the appropriate group
|
||||
if (!groups[def.group]) {
|
||||
groups[def.group] = [];
|
||||
}
|
||||
groups[def.group].push({ ...def, name: fieldName });
|
||||
groups[def.group].push(fieldDef);
|
||||
});
|
||||
|
||||
setGroupedFields(groups);
|
||||
}
|
||||
}, [fieldDefinitions]);
|
||||
}, [processedFieldDefinitions]);
|
||||
|
||||
// Initialize form with default values from field definitions and initialData
|
||||
const defaultValues: Record<string, any> = {};
|
||||
Object.entries(fieldDefinitions).forEach(([key, def]) => {
|
||||
Object.entries(processedFieldDefinitions).forEach(([key, def]) => {
|
||||
const fieldDef = def as FieldDefinition;
|
||||
defaultValues[key] = fieldDef.defaultValue !== undefined ? fieldDef.defaultValue : "";
|
||||
});
|
||||
|
||||
// Merge initialData with default values
|
||||
if (initialData) {
|
||||
Object.assign(defaultValues, initialData as Record<string, any>);
|
||||
}
|
||||
|
||||
// Setup form with validation schema if available
|
||||
// Track the current language to detect changes
|
||||
const [currentLang, setCurrentLang] = useState(lang);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
formState: { errors, isSubmitting, isValid },
|
||||
setValue,
|
||||
watch,
|
||||
reset,
|
||||
trigger,
|
||||
} = useForm({
|
||||
defaultValues,
|
||||
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
||||
mode: "onChange",
|
||||
});
|
||||
|
||||
// Reset form when initialData changes
|
||||
useEffect(() => {
|
||||
if (Object.keys(errors).length > 0) {
|
||||
console.log("Form errors:", errors);
|
||||
}
|
||||
}, [errors]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
reset({ ...initialData as Record<string, any> });
|
||||
}
|
||||
}, [initialData, reset]);
|
||||
|
||||
// Detect language changes and update validation schema
|
||||
useEffect(() => {
|
||||
// If language has changed, update the form
|
||||
if (currentLang !== lang || formProps.currentLang !== lang) {
|
||||
const updateValidationForLanguage = async () => {
|
||||
try {
|
||||
// If we have a schema path, dynamically load the schema for the current language
|
||||
if (formProps.schemaPath) {
|
||||
// Dynamic import of the schema module
|
||||
const schemaModule = await import(formProps.schemaPath);
|
||||
|
||||
// Check if language-specific schema functions are available
|
||||
if (schemaModule.getUpdateApplicationSchema) {
|
||||
// Get the schema for the current language
|
||||
const langValidationSchema = schemaModule.getUpdateApplicationSchema(lang as "en" | "tr");
|
||||
|
||||
// Save current form values
|
||||
const formValues = watch();
|
||||
|
||||
// Reset the form with current values but clear errors
|
||||
reset(formValues, {
|
||||
keepDirty: true,
|
||||
keepValues: true,
|
||||
keepErrors: false
|
||||
});
|
||||
|
||||
// Manually trigger validation after reset
|
||||
setTimeout(() => {
|
||||
// Trigger validation for all fields to show updated error messages
|
||||
Object.keys(formValues).forEach(fieldName => {
|
||||
trigger(fieldName);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
// Update our tracked language
|
||||
setCurrentLang(lang);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating validation schema for language:", error);
|
||||
}
|
||||
};
|
||||
|
||||
updateValidationForLanguage();
|
||||
}
|
||||
}, [lang, formProps.currentLang, currentLang, formProps.schemaPath, reset, watch, trigger]);
|
||||
|
||||
const formValues = watch();
|
||||
|
||||
// Handle form submission
|
||||
const onSubmit = async (data: any) => {
|
||||
try {
|
||||
console.log("Form data to update:", data);
|
||||
|
||||
// Here you would make an API call to update the data
|
||||
// For example: await updateApplication(data);
|
||||
const isFormValid = await trigger();
|
||||
if (!isFormValid) {
|
||||
console.error("Form validation failed - stopping submission");
|
||||
return; // Stop submission if validation fails
|
||||
}
|
||||
if (!apiUrl) {
|
||||
console.error("API URL is missing or undefined");
|
||||
return;
|
||||
}
|
||||
const uuid = initialData ? (initialData as any).uuid || (initialData as any).uu_id : null;
|
||||
if (!uuid) {
|
||||
console.error("UUID not found in initialData");
|
||||
throw new Error("UUID is required for update operations");
|
||||
}
|
||||
const dataToSend = { ...data };
|
||||
Object.entries(fieldDefinitions).forEach(([key, def]) => {
|
||||
const fieldDef = def as FieldDefinition;
|
||||
if (fieldDef.readOnly) {
|
||||
delete dataToSend[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Mock API call success
|
||||
if (refetch) refetch();
|
||||
setMode("list");
|
||||
setSelectedItem(null);
|
||||
const updateUrl = `${apiUrl}/update?uuid=${uuid}`;
|
||||
try {
|
||||
let response = await fetch(updateUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(dataToSend),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error("API error response:", errorText);
|
||||
throw new Error(`API error: ${response.status}`);
|
||||
}
|
||||
if (refetch) refetch();
|
||||
setMode("list");
|
||||
setSelectedItem(null);
|
||||
} catch (fetchError) {
|
||||
console.error("Error during fetch:", fetchError);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating form:", error);
|
||||
console.error("Error details:", error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,43 +218,55 @@ export function UpdateComponent<T>({
|
||||
|
||||
// Translate group names for display dynamically
|
||||
const getGroupTitle = (groupName: string) => {
|
||||
// First check if there's a translation for the exact group key
|
||||
// Check if we have a translation for this group name
|
||||
if (t[groupName]) {
|
||||
return t[groupName];
|
||||
}
|
||||
|
||||
// Try to format the group name in a more readable way if no translation exists
|
||||
// Convert camelCase or snake_case to Title Case with spaces
|
||||
// If no translation is found, just format the name as a fallback
|
||||
const formattedName = groupName
|
||||
// Insert space before capital letters and uppercase the first letter
|
||||
.replace(/([A-Z])/g, ' $1')
|
||||
// Replace underscores with spaces
|
||||
.replace(/_/g, ' ')
|
||||
// Capitalize first letter
|
||||
.replace(/^./, (str) => str.toUpperCase())
|
||||
// Capitalize each word
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
|
||||
return formattedName;
|
||||
};
|
||||
|
||||
// Render a field based on its type
|
||||
const renderField = (fieldName: string, field: FieldDefinition) => {
|
||||
const errorMessage = errors[fieldName]?.message as string;
|
||||
const fieldValue = formValues[fieldName];
|
||||
|
||||
const renderLabel = () => (
|
||||
<Label htmlFor={fieldName}>
|
||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
);
|
||||
|
||||
if (field.readOnly) {
|
||||
return (
|
||||
<div className="space-y-2" key={fieldName}>
|
||||
{renderLabel()}
|
||||
<div className="p-2 bg-gray-50 rounded border border-gray-200">
|
||||
{field.type === "checkbox" ?
|
||||
(fieldValue ? "Yes" : "No") :
|
||||
(fieldValue || "-")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// For editable fields, render the appropriate input type
|
||||
switch (field.type) {
|
||||
case "text":
|
||||
return (
|
||||
<div className="space-y-2" key={fieldName}>
|
||||
<Label htmlFor={fieldName}>
|
||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
{renderLabel()}
|
||||
<Input
|
||||
id={fieldName}
|
||||
{...register(fieldName)}
|
||||
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
disabled={field.readOnly}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||
@@ -160,16 +277,12 @@ export function UpdateComponent<T>({
|
||||
case "textarea":
|
||||
return (
|
||||
<div className="space-y-2" key={fieldName}>
|
||||
<Label htmlFor={fieldName}>
|
||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
{renderLabel()}
|
||||
<Textarea
|
||||
id={fieldName}
|
||||
{...register(fieldName)}
|
||||
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
rows={3}
|
||||
disabled={field.readOnly}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||
@@ -180,14 +293,10 @@ export function UpdateComponent<T>({
|
||||
case "select":
|
||||
return (
|
||||
<div className="space-y-2" key={fieldName}>
|
||||
<Label htmlFor={fieldName}>
|
||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
{renderLabel()}
|
||||
<Select
|
||||
value={formValues[fieldName]}
|
||||
value={fieldValue}
|
||||
onValueChange={(value) => handleSelectChange(fieldName, value)}
|
||||
disabled={field.readOnly}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t[fieldName] || field.label[lang as "en" | "tr"]} />
|
||||
@@ -211,16 +320,12 @@ export function UpdateComponent<T>({
|
||||
<div className="flex items-center space-x-2" key={fieldName}>
|
||||
<Checkbox
|
||||
id={fieldName}
|
||||
checked={formValues[fieldName]}
|
||||
checked={fieldValue}
|
||||
onCheckedChange={(checked) =>
|
||||
handleCheckboxChange(fieldName, checked as boolean)
|
||||
}
|
||||
disabled={field.readOnly}
|
||||
/>
|
||||
<Label htmlFor={fieldName}>
|
||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
{renderLabel()}
|
||||
{errorMessage && (
|
||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||
)}
|
||||
@@ -230,15 +335,11 @@ export function UpdateComponent<T>({
|
||||
case "date":
|
||||
return (
|
||||
<div className="space-y-2" key={fieldName}>
|
||||
<Label htmlFor={fieldName}>
|
||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
{renderLabel()}
|
||||
<Input
|
||||
id={fieldName}
|
||||
type="date"
|
||||
{...register(fieldName)}
|
||||
disabled={field.readOnly}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||
@@ -259,16 +360,33 @@ export function UpdateComponent<T>({
|
||||
<CardDescription>{t.updateDescription || "Update existing item"}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
||||
{/* Display validation errors summary if any */}
|
||||
{Object.keys(errors).length > 0 && (
|
||||
<Alert variant="destructive" className="mb-4">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
{t.formErrors || "Please correct the errors in the form"}
|
||||
<ul className="mt-2 list-disc pl-5">
|
||||
{Object.entries(errors).map(([field, error]) => (
|
||||
<li key={field}>
|
||||
{t[field] || field}: {(error as any)?.message || 'Invalid value'}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 pt-6 my-4">
|
||||
<Button type="button" variant="outline" onClick={onCancel} className="w-full">
|
||||
{t.cancel || "Cancel"}
|
||||
</Button>
|
||||
<Button type="submit" className="w-full">
|
||||
{t.save || "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Render fields grouped by their group property */}
|
||||
<div className="space-y-6">
|
||||
{Object.entries(groupedFields).map(([groupName, fields]) => (
|
||||
@@ -285,14 +403,7 @@ export function UpdateComponent<T>({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-2 pt-6">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
{t.cancel || "Cancel"}
|
||||
</Button>
|
||||
<Button type="submit">
|
||||
{t.save || "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</CardContent>
|
||||
</Card>
|
||||
</form>
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface CreateComponentProps<T> extends BaseFormProps<T> {}
|
||||
|
||||
export interface UpdateComponentProps<T> extends BaseFormProps<T> {
|
||||
initialData: T; // Required for update
|
||||
apiUrl: string;
|
||||
}
|
||||
|
||||
export interface ViewComponentProps<T> extends BaseFormProps<T> {
|
||||
@@ -47,5 +48,5 @@ export interface FormDisplayProps<T> {
|
||||
lang: string;
|
||||
translations: Record<string, Record<string, string>>;
|
||||
formProps?: Record<string, any>;
|
||||
apiUrl?: string;
|
||||
apiUrl: string;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
export type LanguageKey = "en" | "tr";
|
||||
|
||||
export interface TranslationSet {
|
||||
showing: string;
|
||||
of: string;
|
||||
items: string;
|
||||
total: string;
|
||||
filtered: string;
|
||||
page: string;
|
||||
previous: string;
|
||||
next: string;
|
||||
itemsPerPage: string;
|
||||
}
|
||||
import {
|
||||
LanguageKey,
|
||||
TranslationSet,
|
||||
} from "@/validations/translations/translation";
|
||||
|
||||
export const fieldLanguageTranslation = {
|
||||
tr: {
|
||||
@@ -53,6 +44,8 @@ export const translations = {
|
||||
update: "Update",
|
||||
delete: "Delete",
|
||||
view: "View",
|
||||
save: "Save",
|
||||
cancel: "Cancel",
|
||||
|
||||
// Search and filters
|
||||
search: "Search",
|
||||
@@ -104,6 +97,14 @@ export const translations = {
|
||||
availableApplications: "Available Applications",
|
||||
code: "Code",
|
||||
sortBy: "Sort by:",
|
||||
formErrors: "Please correct the errors in the form",
|
||||
|
||||
// Form group titles
|
||||
identificationInfo: "Identification Information",
|
||||
applicationDetails: "Application Details",
|
||||
statusInfo: "Status Information",
|
||||
systemInfo: "System Information",
|
||||
createDescription: "Create Application",
|
||||
},
|
||||
tr: {
|
||||
// Page title
|
||||
@@ -115,7 +116,8 @@ export const translations = {
|
||||
update: "Güncelle",
|
||||
delete: "Sil",
|
||||
view: "Görüntüle",
|
||||
|
||||
save: "Kaydet",
|
||||
cancel: "İptal",
|
||||
// Search and filters
|
||||
search: "Ara",
|
||||
typeSelection: "Tür Seçimi",
|
||||
@@ -166,6 +168,14 @@ export const translations = {
|
||||
availableApplications: "Mevcut Uygulamalar",
|
||||
code: "Kod",
|
||||
sortBy: "Sırala:",
|
||||
formErrors: "Lütfen formdaki hataları düzeltiniz",
|
||||
|
||||
// Form group titles
|
||||
identificationInfo: "Kimlik Bilgileri",
|
||||
applicationDetails: "Uygulama Detayları",
|
||||
statusInfo: "Durum Bilgileri",
|
||||
systemInfo: "Sistem Bilgileri",
|
||||
createDescription: "Yeni bir uygulama oluşturun",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import * as schema from "./schema";
|
||||
import { translations } from "./language";
|
||||
import type { FormMode } from "@/components/common/FormDisplay/types";
|
||||
|
||||
import { PageProps } from "@/validations/translations/translation";
|
||||
import { useApiData } from "@/components/common/hooks/useApiData";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Building, Filter, User } from "lucide-react";
|
||||
import { TextQueryModifier, SelectQueryModifier, TypeQueryModifier } from "@/components/common/QueryModifiers";
|
||||
import { TypeQueryModifier } from "@/components/common/QueryModifiers/TypeQueryModifier";
|
||||
import { TextQueryModifier } from "@/components/common/QueryModifiers/TextQueryModifier";
|
||||
import { SelectQueryModifier } from "@/components/common/QueryModifiers/SelectQueryModifier";
|
||||
import { CreateButton } from "@/components/common/ActionButtonsDisplay/CreateButton";
|
||||
import { PaginationToolsComponent } from "@/components/common/PaginationModifiers/PaginationToolsComponent";
|
||||
import { CardDisplay } from "@/components/common/CardDisplay";
|
||||
import { CardDisplay } from "@/components/common/CardDisplay/CardDisplay";
|
||||
import { FormMode } from "@/components/common/FormDisplay/types";
|
||||
import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay";
|
||||
import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
|
||||
import { LanguageSelectionComponent, Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
|
||||
import { getCreateApplicationSchema, getUpdateApplicationSchema } from "./schema";
|
||||
import { translations } from "./language";
|
||||
import { PageProps } from "@/validations/translations/translation";
|
||||
import { useApiData } from "@/components/common";
|
||||
|
||||
const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||
// Add local state for language to ensure it persists when changed
|
||||
@@ -35,6 +37,39 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||
const [selectedItem, setSelectedItem] = useState<schema.ApplicationData | null>(null);
|
||||
const [gridCols, setGridCols] = useState<GridSize>(3);
|
||||
|
||||
// Use state to store the field definitions and validation schema
|
||||
const [fieldDefinitions, setFieldDefinitions] = useState(() =>
|
||||
mode === 'create' ? schema.createFieldDefinitions :
|
||||
mode === 'update' ? schema.updateFieldDefinitions :
|
||||
schema.viewFieldDefinitions
|
||||
);
|
||||
|
||||
const [validationSchema, setValidationSchema] = useState(() =>
|
||||
mode === 'create' ? getCreateApplicationSchema(lang as "en" | "tr") :
|
||||
mode === 'update' ? getUpdateApplicationSchema(lang as "en" | "tr") :
|
||||
schema.ViewApplicationSchema
|
||||
);
|
||||
|
||||
// Update field definitions when mode changes
|
||||
useEffect(() => {
|
||||
// Select the appropriate field definitions based on the current mode
|
||||
const newFieldDefs = mode === 'create' ? schema.createFieldDefinitions :
|
||||
mode === 'update' ? schema.updateFieldDefinitions :
|
||||
schema.viewFieldDefinitions;
|
||||
|
||||
setFieldDefinitions(newFieldDefs);
|
||||
}, [mode]);
|
||||
|
||||
// Update validation schema when mode or language changes
|
||||
useEffect(() => {
|
||||
setValidationSchema(
|
||||
mode === 'create' ? getCreateApplicationSchema(lang as "en" | "tr") :
|
||||
mode === 'update' ? getUpdateApplicationSchema(lang as "en" | "tr") :
|
||||
schema.ViewApplicationSchema
|
||||
);
|
||||
}, [mode, lang]);
|
||||
|
||||
|
||||
// Fields to display in the cards
|
||||
const showFields = ["application_code", "site_url", "application_type"];
|
||||
|
||||
@@ -95,13 +130,6 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Reset all filters
|
||||
const handleResetAllFilters = () => {
|
||||
updatePagination({
|
||||
page: 1,
|
||||
query: {},
|
||||
});
|
||||
};
|
||||
|
||||
// Handle card actions
|
||||
const handleCardClick = (item: schema.ApplicationData) => {
|
||||
@@ -115,7 +143,9 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||
|
||||
const handleUpdateClick = (item: schema.ApplicationData) => {
|
||||
setSelectedItem(item);
|
||||
setMode("update");
|
||||
setTimeout(() => {
|
||||
setMode("update");
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// Handle create button click
|
||||
@@ -282,12 +312,8 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||
translations={translations}
|
||||
apiUrl='/api/applications'
|
||||
formProps={{
|
||||
fieldDefinitions: mode === 'create' ? schema.createFieldDefinitions :
|
||||
mode === 'update' ? schema.updateFieldDefinitions :
|
||||
schema.viewFieldDefinitions,
|
||||
validationSchema: mode === 'create' ? schema.CreateApplicationSchema :
|
||||
mode === 'update' ? schema.UpdateApplicationSchema :
|
||||
schema.ViewApplicationSchema,
|
||||
fieldDefinitions,
|
||||
validationSchema,
|
||||
fieldsByMode: schema.fieldsByMode
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -15,16 +15,32 @@ export interface ApplicationData {
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
// Base schema with all possible fields
|
||||
const ApplicationBaseSchema = z.object({
|
||||
// Validation error messages by language
|
||||
const errorMessages = {
|
||||
en: {
|
||||
nameRequired: "Name is required",
|
||||
applicationCodeRequired: "Application code is required",
|
||||
siteUrlRequired: "Site URL is required",
|
||||
applicationTypeRequired: "Application type is required"
|
||||
},
|
||||
tr: {
|
||||
nameRequired: "İsim gereklidir",
|
||||
applicationCodeRequired: "Uygulama kodu gereklidir",
|
||||
siteUrlRequired: "Site URL'si gereklidir",
|
||||
applicationTypeRequired: "Uygulama tipi gereklidir"
|
||||
}
|
||||
};
|
||||
|
||||
// Function to get schema with language-specific validation messages
|
||||
const getApplicationBaseSchema = (lang: "en" | "tr" = "en") => z.object({
|
||||
// Identification fields
|
||||
uu_id: z.number().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
application_code: z.string().min(1, "Application code is required"),
|
||||
uu_id: z.string().optional(),
|
||||
name: z.string().min(1, errorMessages[lang].nameRequired),
|
||||
application_code: z.string().min(1, errorMessages[lang].applicationCodeRequired),
|
||||
|
||||
// Application details
|
||||
site_url: z.string().min(1, "Site URL is required"),
|
||||
application_type: z.string().min(1, "Application type is required"),
|
||||
site_url: z.string().min(1, errorMessages[lang].siteUrlRequired),
|
||||
application_type: z.string().min(1, errorMessages[lang].applicationTypeRequired),
|
||||
application_for: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
|
||||
@@ -37,6 +53,9 @@ const ApplicationBaseSchema = z.object({
|
||||
updated_at: z.string().optional(),
|
||||
});
|
||||
|
||||
// For backward compatibility
|
||||
const ApplicationBaseSchema = getApplicationBaseSchema("en");
|
||||
|
||||
const ApplicationBaseTranslationTr = {
|
||||
uu_id: "UUID",
|
||||
name: "Name",
|
||||
@@ -65,28 +84,46 @@ const ApplicationBaseTranslationEn = {
|
||||
updated_at: "Updated At",
|
||||
};
|
||||
|
||||
// Schema for creating a new application
|
||||
export const CreateApplicationSchema = ApplicationBaseSchema.omit({
|
||||
uu_id: true,
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
deleted: true,
|
||||
});
|
||||
// Create schema for creating new applications with language support
|
||||
const getCreateApplicationSchema = (lang: "en" | "tr" = "en") =>
|
||||
getApplicationBaseSchema(lang).omit({
|
||||
uu_id: true,
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
deleted: true,
|
||||
});
|
||||
|
||||
// Schema for updating an existing application
|
||||
export const UpdateApplicationSchema = ApplicationBaseSchema.omit({
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
deleted: true,
|
||||
}).required({
|
||||
uu_id: true,
|
||||
});
|
||||
// Update schema for updating existing applications with language support
|
||||
const getUpdateApplicationSchema = (lang: "en" | "tr" = "en") =>
|
||||
getApplicationBaseSchema(lang).omit({
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
deleted: true,
|
||||
}).required({
|
||||
uu_id: true,
|
||||
});
|
||||
|
||||
// For backward compatibility
|
||||
const CreateApplicationSchema = getCreateApplicationSchema("en");
|
||||
const UpdateApplicationSchema = getUpdateApplicationSchema("en");
|
||||
|
||||
// Schema for viewing an application (all fields)
|
||||
export const ViewApplicationSchema = ApplicationBaseSchema;
|
||||
const ViewApplicationSchema = ApplicationBaseSchema;
|
||||
|
||||
// Default schema (used for validation)
|
||||
export const ApplicationSchema = ApplicationBaseSchema;
|
||||
const ApplicationSchema = ApplicationBaseSchema;
|
||||
|
||||
// Export all schemas and schema generators
|
||||
export {
|
||||
ApplicationBaseSchema,
|
||||
ApplicationSchema,
|
||||
CreateApplicationSchema,
|
||||
UpdateApplicationSchema,
|
||||
ViewApplicationSchema,
|
||||
getApplicationBaseSchema,
|
||||
getCreateApplicationSchema,
|
||||
getUpdateApplicationSchema,
|
||||
};
|
||||
|
||||
export type ApplicationFormData = z.infer<typeof ApplicationSchema>;
|
||||
export type CreateApplicationFormData = z.infer<typeof CreateApplicationSchema>;
|
||||
|
||||
@@ -48,6 +48,21 @@ interface PageProps {
|
||||
|
||||
type PageComponent = React.ComponentType<PageProps>;
|
||||
|
||||
export type LanguageKey = "en" | "tr";
|
||||
|
||||
export interface TranslationSet {
|
||||
showing: string;
|
||||
of: string;
|
||||
items: string;
|
||||
total: string;
|
||||
filtered: string;
|
||||
page: string;
|
||||
previous: string;
|
||||
next: string;
|
||||
itemsPerPage: string;
|
||||
}
|
||||
|
||||
|
||||
export type {
|
||||
PageComponent,
|
||||
PageProps,
|
||||
|
||||
Reference in New Issue
Block a user