updated components common header layouts
This commit is contained in:
parent
1ce28ec5f0
commit
71c808a5c3
|
|
@ -5,8 +5,6 @@ import { CardSkeleton } from "./CardSkeleton";
|
||||||
import { getFieldValue, getGridClasses } from "./utils";
|
import { getFieldValue, getGridClasses } from "./utils";
|
||||||
import { CardDisplayProps } from "./schema";
|
import { CardDisplayProps } from "./schema";
|
||||||
|
|
||||||
// Interface moved to schema.ts
|
|
||||||
|
|
||||||
export function CardDisplay<T>({
|
export function CardDisplay<T>({
|
||||||
showFields,
|
showFields,
|
||||||
data,
|
data,
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ export function CreateComponent<T>({
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
|
reset,
|
||||||
} = useForm<Record<string, any>>({
|
} = useForm<Record<string, any>>({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
||||||
|
|
@ -71,6 +72,33 @@ export function CreateComponent<T>({
|
||||||
|
|
||||||
const formValues = watch();
|
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
|
// Handle form submission
|
||||||
const onSubmit: SubmitHandler<Record<string, any>> = async (data) => {
|
const onSubmit: SubmitHandler<Record<string, any>> = async (data) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -112,23 +140,17 @@ export function CreateComponent<T>({
|
||||||
|
|
||||||
// Translate group names for display dynamically
|
// Translate group names for display dynamically
|
||||||
const getGroupTitle = (groupName: string) => {
|
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]) {
|
if (t[groupName]) {
|
||||||
return t[groupName];
|
return t[groupName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to format the group name in a more readable way if no translation exists
|
// If no translation is found, just format the name as a fallback
|
||||||
// Convert camelCase or snake_case to Title Case with spaces
|
|
||||||
const formattedName = groupName
|
const formattedName = groupName
|
||||||
// Insert space before capital letters and uppercase the first letter
|
|
||||||
.replace(/([A-Z])/g, ' $1')
|
.replace(/([A-Z])/g, ' $1')
|
||||||
// Replace underscores with spaces
|
|
||||||
.replace(/_/g, ' ')
|
.replace(/_/g, ' ')
|
||||||
// Capitalize first letter
|
|
||||||
.replace(/^./, (str) => str.toUpperCase())
|
.replace(/^./, (str) => str.toUpperCase())
|
||||||
// Capitalize each word
|
|
||||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||||
|
|
||||||
return formattedName;
|
return formattedName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -233,6 +255,14 @@ export function CreateComponent<T>({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<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">
|
<Card className="w-full mb-6">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{t.create || "Create"}</CardTitle>
|
<CardTitle>{t.create || "Create"}</CardTitle>
|
||||||
|
|
@ -265,14 +295,7 @@ export function CreateComponent<T>({
|
||||||
))}
|
))}
|
||||||
</div>
|
</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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ export function FormDisplay<T>({
|
||||||
}: FormDisplayProps<T>) {
|
}: FormDisplayProps<T>) {
|
||||||
const [enhancedFormProps, setEnhancedFormProps] = useState(formProps);
|
const [enhancedFormProps, setEnhancedFormProps] = useState(formProps);
|
||||||
|
|
||||||
// Dynamically import schema definitions if provided in formProps
|
// Update form props when language or mode changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSchemaDefinitions = async () => {
|
const updateFormProps = async () => {
|
||||||
try {
|
try {
|
||||||
// Check if schemaPath is provided in formProps
|
// Check if schemaPath is provided in formProps
|
||||||
if (formProps.schemaPath) {
|
if (formProps.schemaPath) {
|
||||||
|
|
@ -40,12 +40,14 @@ export function FormDisplay<T>({
|
||||||
fieldDefs = 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;
|
let validationSchema;
|
||||||
if (mode === "create" && schemaModule.CreateApplicationSchema) {
|
if (mode === "create" && schemaModule.getCreateApplicationSchema) {
|
||||||
validationSchema = schemaModule.CreateApplicationSchema;
|
// Use language-aware schema factory function
|
||||||
} else if (mode === "update" && schemaModule.UpdateApplicationSchema) {
|
validationSchema = schemaModule.getCreateApplicationSchema(lang as "en" | "tr");
|
||||||
validationSchema = schemaModule.UpdateApplicationSchema;
|
} 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) {
|
} else if (mode === "view" && schemaModule.ViewApplicationSchema) {
|
||||||
validationSchema = schemaModule.ViewApplicationSchema;
|
validationSchema = schemaModule.ViewApplicationSchema;
|
||||||
} else if (schemaModule.ApplicationSchema) {
|
} else if (schemaModule.ApplicationSchema) {
|
||||||
|
|
@ -55,22 +57,40 @@ export function FormDisplay<T>({
|
||||||
// Get the grouped field definitions structure if available
|
// Get the grouped field definitions structure if available
|
||||||
const groupedFieldDefs = schemaModule.baseFieldDefinitions || {};
|
const groupedFieldDefs = schemaModule.baseFieldDefinitions || {};
|
||||||
|
|
||||||
// Update form props with schema information
|
// Update form props with schema information and current language
|
||||||
setEnhancedFormProps({
|
setEnhancedFormProps({
|
||||||
...formProps,
|
...formProps,
|
||||||
fieldDefinitions: fieldDefs || {},
|
fieldDefinitions: fieldDefs || {},
|
||||||
validationSchema,
|
validationSchema,
|
||||||
fieldsByMode: schemaModule.fieldsByMode || {},
|
fieldsByMode: schemaModule.fieldsByMode || {},
|
||||||
groupedFieldDefinitions: groupedFieldDefs,
|
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) {
|
} catch (error) {
|
||||||
console.error("Error loading schema definitions:", error);
|
console.error("Error loading schema definitions:", error);
|
||||||
|
// Even on error, update the language
|
||||||
|
setEnhancedFormProps({
|
||||||
|
...formProps,
|
||||||
|
currentLang: lang
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadSchemaDefinitions();
|
updateFormProps();
|
||||||
}, [formProps, mode, lang]); // Added lang as a dependency to ensure re-fetch when language changes
|
}, [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
|
// Render the appropriate component based on the mode
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
|
@ -89,9 +109,12 @@ export function FormDisplay<T>({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "update":
|
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 ? (
|
return initialData ? (
|
||||||
<UpdateComponent<T>
|
<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}
|
initialData={initialData}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
setMode={setMode}
|
setMode={setMode}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useMemo } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||||
import { UpdateComponentProps, FieldDefinition } from "./types";
|
import { UpdateComponentProps, FieldDefinition } from "./types";
|
||||||
|
|
@ -26,29 +26,48 @@ export function UpdateComponent<T>({
|
||||||
}: UpdateComponentProps<T>) {
|
}: UpdateComponentProps<T>) {
|
||||||
const t = translations[lang as keyof typeof translations] || {};
|
const t = translations[lang as keyof typeof translations] || {};
|
||||||
|
|
||||||
|
// Get field definitions from formProps if available
|
||||||
const fieldDefinitions = formProps.fieldDefinitions || {};
|
const fieldDefinitions = formProps.fieldDefinitions || {};
|
||||||
const validationSchema = formProps.validationSchema;
|
const validationSchema = formProps.validationSchema;
|
||||||
|
|
||||||
|
// 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[]>>({});
|
const [groupedFields, setGroupedFields] = useState<Record<string, FieldDefinition[]>>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(fieldDefinitions).length > 0) {
|
if (Object.keys(processedFieldDefinitions).length > 0) {
|
||||||
const groups: Record<string, FieldDefinition[]> = {};
|
const groups: Record<string, FieldDefinition[]> = {};
|
||||||
|
|
||||||
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;
|
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]) {
|
if (!groups[def.group]) {
|
||||||
groups[def.group] = [];
|
groups[def.group] = [];
|
||||||
}
|
}
|
||||||
groups[def.group].push({ ...def, name: fieldName });
|
groups[def.group].push(fieldDef);
|
||||||
});
|
});
|
||||||
|
|
||||||
setGroupedFields(groups);
|
setGroupedFields(groups);
|
||||||
}
|
}
|
||||||
}, [fieldDefinitions]);
|
}, [processedFieldDefinitions]);
|
||||||
|
|
||||||
const defaultValues: Record<string, any> = {};
|
const defaultValues: Record<string, any> = {};
|
||||||
Object.entries(fieldDefinitions).forEach(([key, def]) => {
|
Object.entries(processedFieldDefinitions).forEach(([key, def]) => {
|
||||||
const fieldDef = def as FieldDefinition;
|
const fieldDef = def as FieldDefinition;
|
||||||
defaultValues[key] = fieldDef.defaultValue !== undefined ? fieldDef.defaultValue : "";
|
defaultValues[key] = fieldDef.defaultValue !== undefined ? fieldDef.defaultValue : "";
|
||||||
});
|
});
|
||||||
|
|
@ -57,6 +76,9 @@ export function UpdateComponent<T>({
|
||||||
Object.assign(defaultValues, initialData as Record<string, any>);
|
Object.assign(defaultValues, initialData as Record<string, any>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track the current language to detect changes
|
||||||
|
const [currentLang, setCurrentLang] = useState(lang);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|
@ -83,6 +105,53 @@ export function UpdateComponent<T>({
|
||||||
}
|
}
|
||||||
}, [initialData, reset]);
|
}, [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();
|
const formValues = watch();
|
||||||
|
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
|
|
@ -149,10 +218,12 @@ export function UpdateComponent<T>({
|
||||||
|
|
||||||
// Translate group names for display dynamically
|
// Translate group names for display dynamically
|
||||||
const getGroupTitle = (groupName: string) => {
|
const getGroupTitle = (groupName: string) => {
|
||||||
|
// Check if we have a translation for this group name
|
||||||
if (t[groupName]) {
|
if (t[groupName]) {
|
||||||
return t[groupName];
|
return t[groupName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no translation is found, just format the name as a fallback
|
||||||
const formattedName = groupName
|
const formattedName = groupName
|
||||||
.replace(/([A-Z])/g, ' $1')
|
.replace(/([A-Z])/g, ' $1')
|
||||||
.replace(/_/g, ' ')
|
.replace(/_/g, ' ')
|
||||||
|
|
@ -289,6 +360,7 @@ export function UpdateComponent<T>({
|
||||||
<CardDescription>{t.updateDescription || "Update existing item"}</CardDescription>
|
<CardDescription>{t.updateDescription || "Update existing item"}</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
|
||||||
{/* Display validation errors summary if any */}
|
{/* Display validation errors summary if any */}
|
||||||
{Object.keys(errors).length > 0 && (
|
{Object.keys(errors).length > 0 && (
|
||||||
<Alert variant="destructive" className="mb-4">
|
<Alert variant="destructive" className="mb-4">
|
||||||
|
|
@ -306,6 +378,15 @@ export function UpdateComponent<T>({
|
||||||
</Alert>
|
</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 */}
|
{/* Render fields grouped by their group property */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{Object.entries(groupedFields).map(([groupName, fields]) => (
|
{Object.entries(groupedFields).map(([groupName, fields]) => (
|
||||||
|
|
@ -322,14 +403,7 @@ export function UpdateComponent<T>({
|
||||||
))}
|
))}
|
||||||
</div>
|
</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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Language, LanguageSelectionComponentProps } from "@/components/common/schemas";
|
||||||
export type Language = "en" | "tr";
|
|
||||||
|
|
||||||
interface LanguageSelectionComponentProps {
|
|
||||||
lang: Language;
|
|
||||||
setLang: (lang: Language) => void;
|
|
||||||
translations?: Record<string, any>;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({
|
export const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({
|
||||||
lang,
|
lang,
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,16 @@
|
||||||
import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
|
import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
|
||||||
import { PageProps } from "@/validations/translations/translation";
|
import { PageProps } from "@/validations/translations/translation";
|
||||||
import React, { ReactElement } from "react";
|
import React from "react";
|
||||||
|
|
||||||
export interface DashboardPageParams {
|
export interface DashboardPageParams {
|
||||||
/**
|
|
||||||
* The active page path, e.g., "/application", "/dashboard"
|
|
||||||
*/
|
|
||||||
pageUrl: string;
|
pageUrl: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* The search parameters from Next.js
|
|
||||||
*/
|
|
||||||
searchParams: Promise<{ [key: string]: string | undefined }>;
|
searchParams: Promise<{ [key: string]: string | undefined }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardPageResult {
|
export interface DashboardPageResult {
|
||||||
/**
|
|
||||||
* The active page path
|
|
||||||
*/
|
|
||||||
activePage: string;
|
activePage: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* The resolved search parameters
|
|
||||||
*/
|
|
||||||
searchParamsInstance: { [key: string]: string | undefined };
|
searchParamsInstance: { [key: string]: string | undefined };
|
||||||
|
|
||||||
/**
|
|
||||||
* The current language, either from search params or default
|
|
||||||
*/
|
|
||||||
lang: "en" | "tr";
|
lang: "en" | "tr";
|
||||||
|
|
||||||
/**
|
|
||||||
* The page component to render
|
|
||||||
*/
|
|
||||||
PageComponent: React.FC<PageProps>;
|
PageComponent: React.FC<PageProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,38 +28,29 @@ export async function useDashboardPage({
|
||||||
}: DashboardPageParams): Promise<DashboardPageResult> {
|
}: DashboardPageParams): Promise<DashboardPageResult> {
|
||||||
let searchParamsInstance: { [key: string]: string | undefined } = {};
|
let searchParamsInstance: { [key: string]: string | undefined } = {};
|
||||||
const defaultLang = "en";
|
const defaultLang = "en";
|
||||||
// Validate pageUrl
|
|
||||||
if (!pageUrl || typeof pageUrl !== "string") {
|
if (!pageUrl || typeof pageUrl !== "string") {
|
||||||
throw new Error(`Invalid page URL: ${pageUrl}`);
|
throw new Error(`Invalid page URL: ${pageUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve search params
|
|
||||||
try {
|
try {
|
||||||
searchParamsInstance = await searchParams;
|
searchParamsInstance = await searchParams;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error resolving search parameters:", err);
|
console.error("Error resolving search parameters:", err);
|
||||||
// Still throw the error to be caught by Next.js error boundary
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine language
|
|
||||||
const lang = (searchParamsInstance?.lang as "en" | "tr") || defaultLang;
|
const lang = (searchParamsInstance?.lang as "en" | "tr") || defaultLang;
|
||||||
|
|
||||||
// Validate language
|
|
||||||
if (lang !== "en" && lang !== "tr") {
|
if (lang !== "en" && lang !== "tr") {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Invalid language "${lang}" specified, falling back to "${defaultLang}"`
|
`Invalid language "${lang}" specified, falling back to "${defaultLang}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get page component
|
const PageComponent = retrievePageByUrl(pageUrl, lang);
|
||||||
const PageComponent = retrievePageByUrl(pageUrl);
|
|
||||||
|
|
||||||
// Check if page component exists
|
|
||||||
if (!PageComponent) {
|
if (!PageComponent) {
|
||||||
throw new Error(`Page component not found for URL: ${pageUrl}`);
|
throw new Error(`Page component not found for URL: ${pageUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activePage: pageUrl,
|
activePage: pageUrl,
|
||||||
searchParamsInstance,
|
searchParamsInstance,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Carried schemas from any request and response
|
// Carried schemas from any request and response
|
||||||
|
|
||||||
// Common request parameters interface
|
// Common request parameters interface
|
||||||
|
|
@ -35,3 +34,12 @@ export interface PagePagination {
|
||||||
orderType: string[];
|
orderType: string[];
|
||||||
query: Record<string, any>;
|
query: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Language = "en" | "tr";
|
||||||
|
|
||||||
|
export interface LanguageSelectionComponentProps {
|
||||||
|
lang: Language;
|
||||||
|
setLang: (lang: Language) => void;
|
||||||
|
translations?: Record<string, any>;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,11 @@ import {
|
||||||
import { searchPlaceholder, menuLanguage } from "@/app/commons/pageDefaults";
|
import { searchPlaceholder, menuLanguage } from "@/app/commons/pageDefaults";
|
||||||
import { logoutActiveSession } from "@/apicalls/login/login";
|
import { logoutActiveSession } from "@/apicalls/login/login";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { LanguageSelectionComponent } from "../common/HeaderSelections/LanguageSelectionComponent";
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
lang: "en" | "tr";
|
lang: "en" | "tr";
|
||||||
|
setLang: (lang: "en" | "tr") => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language dictionary for the dropdown menu
|
// Language dictionary for the dropdown menu
|
||||||
|
|
@ -96,7 +98,7 @@ const mockMessages = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Header: React.FC<HeaderProps> = ({ lang }) => {
|
const Header: React.FC<HeaderProps> = ({ lang, setLang }) => {
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
const [isNotificationsOpen, setIsNotificationsOpen] = useState(false);
|
const [isNotificationsOpen, setIsNotificationsOpen] = useState(false);
|
||||||
const [isMessagesOpen, setIsMessagesOpen] = useState(false);
|
const [isMessagesOpen, setIsMessagesOpen] = useState(false);
|
||||||
|
|
@ -185,7 +187,8 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 bg-white shadow-md z-10 p-4 flex justify-between items-center">
|
<div className="w-full">
|
||||||
|
<header className="sticky top-0 bg-white shadow-md z-10 p-4 flex justify-between items-center w-full">
|
||||||
<h1 className="text-2xl font-semibold">{menuLanguage[lang]}</h1>
|
<h1 className="text-2xl font-semibold">{menuLanguage[lang]}</h1>
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<input
|
<input
|
||||||
|
|
@ -354,6 +357,15 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Language selection */}
|
||||||
|
<div className="mr-4">
|
||||||
|
<LanguageSelectionComponent
|
||||||
|
lang={lang}
|
||||||
|
setLang={setLang}
|
||||||
|
className="border px-3 py-2 rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Profile dropdown */}
|
{/* Profile dropdown */}
|
||||||
<div className="relative" ref={dropdownRef}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
<div
|
<div
|
||||||
|
|
@ -399,6 +411,7 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,44 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { ReactNode } from "react";
|
import React, { useState, useEffect, ReactNode } from "react";
|
||||||
import Header from "@/components/header/Header";
|
import Header from "@/components/header/Header";
|
||||||
import ClientMenu from "@/components/menu/menu";
|
import ClientMenu from "@/components/menu/menu";
|
||||||
|
import { DashboardLayoutProps } from "./schema";
|
||||||
|
import { Language } from "@/components/common/schemas";
|
||||||
|
|
||||||
interface DashboardLayoutProps {
|
// Page Content component to wrap the children
|
||||||
|
interface PageContentProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
lang: "en" | "tr";
|
lang: Language;
|
||||||
activePage: string;
|
|
||||||
siteUrls: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const PageContent: React.FC<PageContentProps> = ({ children, lang }) => {
|
||||||
* A reusable dashboard layout component that provides consistent structure
|
return (
|
||||||
* for all dashboard pages with sidebar, header, and content area.
|
<div className="container mx-auto p-4">
|
||||||
*/
|
{React.cloneElement(children as React.ReactElement<any>, { lang })}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const DashboardLayout: React.FC<DashboardLayoutProps> = ({
|
export const DashboardLayout: React.FC<DashboardLayoutProps> = ({
|
||||||
children,
|
children,
|
||||||
lang,
|
lang,
|
||||||
activePage,
|
activePage,
|
||||||
siteUrls,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const [language, setLanguage] = useState<Language>(lang as Language);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen min-w-screen flex h-screen w-screen">
|
<div className="min-h-screen min-w-screen flex h-screen w-screen">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className="w-1/4 border-r p-4 overflow-y-auto">
|
<aside className="w-1/4 border-r p-4 overflow-y-auto">
|
||||||
<ClientMenu siteUrls={siteUrls} lang={lang} activePage={activePage} />
|
<ClientMenu lang={language} activePage={activePage} />
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<div className="flex flex-col w-3/4 overflow-y-auto">
|
<div className="flex flex-col w-3/4 overflow-y-auto">
|
||||||
{/* Header Component */}
|
{/* Header Component - Either custom or default */}
|
||||||
<Header lang={lang} />
|
<Header lang={language} setLang={setLanguage} />
|
||||||
|
|
||||||
{/* Page Content */}
|
{/* Page Content */}
|
||||||
<div className="container mx-auto p-4">
|
<PageContent lang={language}>{children}</PageContent>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ export const PageTemplate: React.FC<PageTemplateProps> = ({
|
||||||
<LanguageSelectionComponent
|
<LanguageSelectionComponent
|
||||||
lang={lang as Language}
|
lang={lang as Language}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
setLang={setLang || (() => { })}
|
setLang={setLang || (() => {})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Additional header actions */}
|
{/* Additional header actions */}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export interface DashboardLayoutProps {
|
||||||
|
children: ReactNode;
|
||||||
|
lang: "en" | "tr";
|
||||||
|
activePage: string;
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,12 @@ async function DashboardPage({
|
||||||
}: {
|
}: {
|
||||||
searchParams: Promise<{ [key: string]: string | undefined }>;
|
searchParams: Promise<{ [key: string]: string | undefined }>;
|
||||||
}) {
|
}) {
|
||||||
const { activePage, searchParamsInstance, lang, PageComponent } = await useDashboardPage({
|
const {
|
||||||
|
activePage,
|
||||||
|
searchParamsInstance,
|
||||||
|
lang,
|
||||||
|
PageComponent,
|
||||||
|
} = await useDashboardPage({
|
||||||
pageUrl: "/application",
|
pageUrl: "/application",
|
||||||
searchParams,
|
searchParams,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import { CardSkeleton } from "./CardSkeleton";
|
||||||
import { getFieldValue, getGridClasses } from "./utils";
|
import { getFieldValue, getGridClasses } from "./utils";
|
||||||
import { CardDisplayProps } from "./schema";
|
import { CardDisplayProps } from "./schema";
|
||||||
|
|
||||||
// Interface moved to schema.ts
|
|
||||||
|
|
||||||
export function CardDisplay<T>({
|
export function CardDisplay<T>({
|
||||||
showFields,
|
showFields,
|
||||||
data,
|
data,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Language, LanguageSelectionComponentProps } from "@/components/common/schemas";
|
||||||
export type Language = "en" | "tr";
|
|
||||||
|
|
||||||
interface LanguageSelectionComponentProps {
|
|
||||||
lang: Language;
|
|
||||||
setLang: (lang: Language) => void;
|
|
||||||
translations?: Record<string, any>;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({
|
export const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({
|
||||||
lang,
|
lang,
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,16 @@
|
||||||
import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
|
import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
|
||||||
import { PageProps } from "@/validations/translations/translation";
|
import { PageProps } from "@/validations/translations/translation";
|
||||||
import React, { ReactElement } from "react";
|
import React from "react";
|
||||||
|
|
||||||
export interface DashboardPageParams {
|
export interface DashboardPageParams {
|
||||||
/**
|
|
||||||
* The active page path, e.g., "/application", "/dashboard"
|
|
||||||
*/
|
|
||||||
pageUrl: string;
|
pageUrl: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* The search parameters from Next.js
|
|
||||||
*/
|
|
||||||
searchParams: Promise<{ [key: string]: string | undefined }>;
|
searchParams: Promise<{ [key: string]: string | undefined }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardPageResult {
|
export interface DashboardPageResult {
|
||||||
/**
|
|
||||||
* The active page path
|
|
||||||
*/
|
|
||||||
activePage: string;
|
activePage: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* The resolved search parameters
|
|
||||||
*/
|
|
||||||
searchParamsInstance: { [key: string]: string | undefined };
|
searchParamsInstance: { [key: string]: string | undefined };
|
||||||
|
|
||||||
/**
|
|
||||||
* The current language, either from search params or default
|
|
||||||
*/
|
|
||||||
lang: "en" | "tr";
|
lang: "en" | "tr";
|
||||||
|
|
||||||
/**
|
|
||||||
* The page component to render
|
|
||||||
*/
|
|
||||||
PageComponent: React.FC<PageProps>;
|
PageComponent: React.FC<PageProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,38 +28,29 @@ export async function useDashboardPage({
|
||||||
}: DashboardPageParams): Promise<DashboardPageResult> {
|
}: DashboardPageParams): Promise<DashboardPageResult> {
|
||||||
let searchParamsInstance: { [key: string]: string | undefined } = {};
|
let searchParamsInstance: { [key: string]: string | undefined } = {};
|
||||||
const defaultLang = "en";
|
const defaultLang = "en";
|
||||||
// Validate pageUrl
|
|
||||||
if (!pageUrl || typeof pageUrl !== "string") {
|
if (!pageUrl || typeof pageUrl !== "string") {
|
||||||
throw new Error(`Invalid page URL: ${pageUrl}`);
|
throw new Error(`Invalid page URL: ${pageUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve search params
|
|
||||||
try {
|
try {
|
||||||
searchParamsInstance = await searchParams;
|
searchParamsInstance = await searchParams;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error resolving search parameters:", err);
|
console.error("Error resolving search parameters:", err);
|
||||||
// Still throw the error to be caught by Next.js error boundary
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine language
|
|
||||||
const lang = (searchParamsInstance?.lang as "en" | "tr") || defaultLang;
|
const lang = (searchParamsInstance?.lang as "en" | "tr") || defaultLang;
|
||||||
|
|
||||||
// Validate language
|
|
||||||
if (lang !== "en" && lang !== "tr") {
|
if (lang !== "en" && lang !== "tr") {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Invalid language "${lang}" specified, falling back to "${defaultLang}"`
|
`Invalid language "${lang}" specified, falling back to "${defaultLang}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get page component
|
const PageComponent = retrievePageByUrl(pageUrl, lang);
|
||||||
const PageComponent = retrievePageByUrl(pageUrl);
|
|
||||||
|
|
||||||
// Check if page component exists
|
|
||||||
if (!PageComponent) {
|
if (!PageComponent) {
|
||||||
throw new Error(`Page component not found for URL: ${pageUrl}`);
|
throw new Error(`Page component not found for URL: ${pageUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activePage: pageUrl,
|
activePage: pageUrl,
|
||||||
searchParamsInstance,
|
searchParamsInstance,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Carried schemas from any request and response
|
// Carried schemas from any request and response
|
||||||
|
|
||||||
// Common request parameters interface
|
// Common request parameters interface
|
||||||
|
|
@ -35,3 +34,12 @@ export interface PagePagination {
|
||||||
orderType: string[];
|
orderType: string[];
|
||||||
query: Record<string, any>;
|
query: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Language = "en" | "tr";
|
||||||
|
|
||||||
|
export interface LanguageSelectionComponentProps {
|
||||||
|
lang: Language;
|
||||||
|
setLang: (lang: Language) => void;
|
||||||
|
translations?: Record<string, any>;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,11 @@ import {
|
||||||
import { searchPlaceholder, menuLanguage } from "@/app/commons/pageDefaults";
|
import { searchPlaceholder, menuLanguage } from "@/app/commons/pageDefaults";
|
||||||
import { logoutActiveSession } from "@/apicalls/login/login";
|
import { logoutActiveSession } from "@/apicalls/login/login";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { LanguageSelectionComponent } from "../common/HeaderSelections/LanguageSelectionComponent";
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
lang: "en" | "tr";
|
lang: "en" | "tr";
|
||||||
|
setLang: (lang: "en" | "tr") => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language dictionary for the dropdown menu
|
// Language dictionary for the dropdown menu
|
||||||
|
|
@ -96,7 +98,7 @@ const mockMessages = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Header: React.FC<HeaderProps> = ({ lang }) => {
|
const Header: React.FC<HeaderProps> = ({ lang, setLang }) => {
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
const [isNotificationsOpen, setIsNotificationsOpen] = useState(false);
|
const [isNotificationsOpen, setIsNotificationsOpen] = useState(false);
|
||||||
const [isMessagesOpen, setIsMessagesOpen] = useState(false);
|
const [isMessagesOpen, setIsMessagesOpen] = useState(false);
|
||||||
|
|
@ -185,7 +187,8 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 bg-white shadow-md z-10 p-4 flex justify-between items-center">
|
<div className="w-full">
|
||||||
|
<header className="sticky top-0 bg-white shadow-md z-10 p-4 flex justify-between items-center w-full">
|
||||||
<h1 className="text-2xl font-semibold">{menuLanguage[lang]}</h1>
|
<h1 className="text-2xl font-semibold">{menuLanguage[lang]}</h1>
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<input
|
<input
|
||||||
|
|
@ -236,8 +239,7 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
notifications.map((notification) => (
|
notifications.map((notification) => (
|
||||||
<div
|
<div
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
className={`px-4 py-2 border-b last:border-b-0 ${
|
className={`px-4 py-2 border-b last:border-b-0 ${!notification.read ? "bg-blue-50" : ""
|
||||||
!notification.read ? "bg-blue-50" : ""
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
|
|
@ -315,8 +317,7 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
messages.map((message) => (
|
messages.map((message) => (
|
||||||
<div
|
<div
|
||||||
key={message.id}
|
key={message.id}
|
||||||
className={`px-4 py-2 border-b last:border-b-0 ${
|
className={`px-4 py-2 border-b last:border-b-0 ${!message.read ? "bg-blue-50" : ""
|
||||||
!message.read ? "bg-blue-50" : ""
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-start">
|
<div className="flex items-start">
|
||||||
|
|
@ -356,6 +357,15 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Language selection */}
|
||||||
|
<div className="mr-4">
|
||||||
|
<LanguageSelectionComponent
|
||||||
|
lang={lang}
|
||||||
|
setLang={setLang}
|
||||||
|
className="border px-3 py-2 rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Profile dropdown */}
|
{/* Profile dropdown */}
|
||||||
<div className="relative" ref={dropdownRef}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
<div
|
<div
|
||||||
|
|
@ -401,6 +411,7 @@ const Header: React.FC<HeaderProps> = ({ lang }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,44 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { ReactNode } from "react";
|
import React, { useState, useEffect, ReactNode } from "react";
|
||||||
import Header from "@/components/header/Header";
|
import Header from "@/components/header/Header";
|
||||||
import ClientMenu from "@/components/menu/menu";
|
import ClientMenu from "@/components/menu/menu";
|
||||||
|
import { DashboardLayoutProps } from "./schema";
|
||||||
|
import { Language } from "@/components/common/schemas";
|
||||||
|
|
||||||
interface DashboardLayoutProps {
|
// Page Content component to wrap the children
|
||||||
|
interface PageContentProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
lang: "en" | "tr";
|
lang: Language;
|
||||||
activePage: string;
|
|
||||||
|
|
||||||
// Optional props for client-frontend application
|
|
||||||
sidebarContent?: ReactNode;
|
|
||||||
customHeader?: ReactNode;
|
|
||||||
pageInfo?: Record<string, string>;
|
|
||||||
searchPlaceholder?: Record<string, string>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const PageContent: React.FC<PageContentProps> = ({ children, lang }) => {
|
||||||
* A reusable dashboard layout component that provides consistent structure
|
return (
|
||||||
* for all dashboard pages with sidebar, header, and content area.
|
<div className="container mx-auto p-4">
|
||||||
*/
|
{React.cloneElement(children as React.ReactElement<any>, { lang })}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const DashboardLayout: React.FC<DashboardLayoutProps> = ({
|
export const DashboardLayout: React.FC<DashboardLayoutProps> = ({
|
||||||
children,
|
children,
|
||||||
lang,
|
lang,
|
||||||
activePage,
|
activePage,
|
||||||
sidebarContent,
|
|
||||||
customHeader,
|
|
||||||
pageInfo,
|
|
||||||
searchPlaceholder,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const [language, setLanguage] = useState<Language>(lang as Language);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen min-w-screen flex h-screen w-screen overflow-y-auto">
|
<div className="min-h-screen min-w-screen flex h-screen w-screen">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className="w-1/4 border-r p-4 overflow-y-auto">
|
<aside className="w-1/4 border-r p-4 overflow-y-auto">
|
||||||
{sidebarContent ? (
|
<ClientMenu lang={language} activePage={activePage} />
|
||||||
sidebarContent
|
|
||||||
) : (
|
|
||||||
<ClientMenu lang={lang} activePage={activePage} />
|
|
||||||
)}
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<div className="flex flex-col w-3/4 overflow-y-auto">
|
<div className="flex flex-col w-3/4 overflow-y-auto">
|
||||||
{/* Header Component - Either custom or default */}
|
{/* Header Component - Either custom or default */}
|
||||||
{customHeader ? (
|
<Header lang={language} setLang={setLanguage} />
|
||||||
customHeader
|
|
||||||
) : pageInfo && searchPlaceholder ? (
|
|
||||||
<header className="sticky top-0 bg-white shadow-md z-10 p-4 flex justify-between items-center">
|
|
||||||
<h1 className="text-2xl font-semibold">{pageInfo[lang]}</h1>
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder={searchPlaceholder[lang]}
|
|
||||||
className="border px-3 py-2 rounded-lg"
|
|
||||||
/>
|
|
||||||
<div className="w-10 h-10 bg-gray-300 rounded-full"></div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
) : (
|
|
||||||
<Header lang={lang} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Page Content */}
|
{/* Page Content */}
|
||||||
<div className={`${customHeader ? 'p-4 overflow-y-auto' : 'container mx-auto p-4'}`}>
|
<PageContent lang={language}>{children}</PageContent>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export interface DashboardLayoutProps {
|
||||||
|
children: ReactNode;
|
||||||
|
lang: "en" | "tr";
|
||||||
|
activePage: string;
|
||||||
|
}
|
||||||
|
|
@ -1,53 +1,32 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import React, { JSX } from "react";
|
||||||
const NavigationLanguage = {
|
import { getNavigationMenu } from "./type";
|
||||||
en: {
|
|
||||||
"/dashboard": "Dashboard",
|
|
||||||
"/append/event": "Event Append",
|
|
||||||
"/append/service": "Service Append",
|
|
||||||
"/application": "Application",
|
|
||||||
"/employee": "Employee",
|
|
||||||
"/ocuppant": "Ocuppant",
|
|
||||||
},
|
|
||||||
tr: {
|
|
||||||
"/dashboard": "Kontrol Paneli",
|
|
||||||
"/append/event": "Event Append",
|
|
||||||
"/append/service": "Service Append",
|
|
||||||
"/application": "Application",
|
|
||||||
"/employee": "Employee",
|
|
||||||
"/ocuppant": "Ocuppant",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function NavigationMenu({
|
function NavigationMenu({
|
||||||
lang,
|
lang,
|
||||||
activePage,
|
activePage,
|
||||||
}: {
|
}: {
|
||||||
lang: string;
|
lang: "en" | "tr";
|
||||||
activePage: string;
|
activePage: string;
|
||||||
}) {
|
}) {
|
||||||
// Get the navigation items based on the selected language
|
|
||||||
const navItems =
|
|
||||||
NavigationLanguage[lang as keyof typeof NavigationLanguage] ||
|
|
||||||
NavigationLanguage.en;
|
|
||||||
|
|
||||||
|
const navItems = getNavigationMenu(lang);
|
||||||
|
|
||||||
|
function createLinkComponent(url: string, title: string): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<nav className="flex flex-col space-y-2 p-4">
|
|
||||||
{Object.entries(navItems).map(([url, title]) => (
|
|
||||||
<Link
|
<Link
|
||||||
key={url}
|
key={url}
|
||||||
href={url}
|
href={url}
|
||||||
className={`px-4 py-2 rounded-md transition-colors duration-200 ${
|
className={`px-4 py-2 rounded-md transition-colors duration-200 ${url === activePage ? "bg-emerald-500 text-white" : "bg-white hover:bg-gray-100"}`}
|
||||||
url === activePage
|
|
||||||
? "bg-emerald-500 text-white"
|
|
||||||
: "bg-white hover:bg-gray-100"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<nav className="flex flex-col space-y-2 p-4">
|
||||||
|
{Object.entries(navItems).map(([url, title]) => createLinkComponent(url, title))}
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import Menu from "./store";
|
|
||||||
|
|
||||||
// Define TypeScript interfaces for menu structure
|
|
||||||
export interface LanguageTranslation {
|
|
||||||
tr: string;
|
|
||||||
en: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MenuThirdLevel {
|
|
||||||
name: string;
|
|
||||||
lg: LanguageTranslation;
|
|
||||||
siteUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MenuSecondLevel {
|
|
||||||
name: string;
|
|
||||||
lg: LanguageTranslation;
|
|
||||||
subList: MenuThirdLevel[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MenuFirstLevel {
|
|
||||||
name: string;
|
|
||||||
lg: LanguageTranslation;
|
|
||||||
subList: MenuSecondLevel[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define interfaces for the filtered menu structure
|
|
||||||
export interface FilteredMenuThirdLevel {
|
|
||||||
name: string;
|
|
||||||
lg: LanguageTranslation;
|
|
||||||
siteUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FilteredMenuSecondLevel {
|
|
||||||
name: string;
|
|
||||||
lg: LanguageTranslation;
|
|
||||||
subList: FilteredMenuThirdLevel[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FilteredMenuFirstLevel {
|
|
||||||
name: string;
|
|
||||||
lg: LanguageTranslation;
|
|
||||||
subList: FilteredMenuSecondLevel[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters the menu structure based on intersections with provided URLs
|
|
||||||
* @param {string[]} siteUrls - Array of site URLs to check for intersection
|
|
||||||
* @returns {Array} - Filtered menu structure with only matching items
|
|
||||||
*/
|
|
||||||
export function transformMenu(siteUrls: string[]) {
|
|
||||||
// Process the menu structure
|
|
||||||
const filteredMenu: FilteredMenuFirstLevel[] = Menu.reduce(
|
|
||||||
(acc: FilteredMenuFirstLevel[], firstLevel: MenuFirstLevel) => {
|
|
||||||
// Create a new first level item with empty subList
|
|
||||||
const newFirstLevel: FilteredMenuFirstLevel = {
|
|
||||||
name: firstLevel.name,
|
|
||||||
lg: { ...firstLevel.lg },
|
|
||||||
subList: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Process second level items
|
|
||||||
firstLevel.subList.forEach((secondLevel: MenuSecondLevel) => {
|
|
||||||
// Create a new second level item with empty subList
|
|
||||||
const newSecondLevel: FilteredMenuSecondLevel = {
|
|
||||||
name: secondLevel.name,
|
|
||||||
lg: { ...secondLevel.lg },
|
|
||||||
subList: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Process third level items
|
|
||||||
secondLevel.subList.forEach((thirdLevel: MenuThirdLevel) => {
|
|
||||||
// Check if the third level's siteUrl matches exactly
|
|
||||||
if (
|
|
||||||
thirdLevel.siteUrl &&
|
|
||||||
siteUrls.some((url) => url === thirdLevel.siteUrl)
|
|
||||||
) {
|
|
||||||
// Create a modified third level item
|
|
||||||
const newThirdLevel: FilteredMenuThirdLevel = {
|
|
||||||
name: thirdLevel.name,
|
|
||||||
lg: { ...thirdLevel.lg },
|
|
||||||
siteUrl: thirdLevel.siteUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the modified third level to the second level's subList
|
|
||||||
newSecondLevel.subList.push(newThirdLevel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only add the second level to the first level if it has any matching third level items
|
|
||||||
if (newSecondLevel.subList.length > 0) {
|
|
||||||
newFirstLevel.subList.push(newSecondLevel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only add the first level to the result if it has any matching second level items
|
|
||||||
if (newFirstLevel.subList.length > 0) {
|
|
||||||
acc.push(newFirstLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return filteredMenu;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState, Suspense } from "react";
|
import React, { useEffect, useState, Suspense, JSX } from "react";
|
||||||
import { retrieveUserSelection } from "@/apicalls/cookies/token";
|
import { retrieveUserSelection } from "@/apicalls/cookies/token";
|
||||||
import EmployeeProfileSection from "./EmployeeProfileSection";
|
import EmployeeProfileSection from "./EmployeeProfileSection";
|
||||||
import OccupantProfileSection from "./OccupantProfileSection";
|
import OccupantProfileSection from "./OccupantProfileSection";
|
||||||
|
|
@ -23,18 +23,13 @@ const dashboardLanguage = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClientMenu: React.FC<ClientMenuProps> = ({ lang = "en", activePage }) => {
|
const ClientMenu: React.FC<ClientMenuProps> = ({ lang, activePage }) => {
|
||||||
const t =
|
const t = dashboardLanguage[lang as keyof typeof dashboardLanguage] || dashboardLanguage.en;
|
||||||
dashboardLanguage[lang as keyof typeof dashboardLanguage] ||
|
|
||||||
dashboardLanguage.en;
|
|
||||||
|
|
||||||
// State for loading indicator, user type, and user selection data
|
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [userType, setUserType] = useState<string | null>(null);
|
const [userType, setUserType] = useState<string | null>(null);
|
||||||
const [userSelectionData, setUserSelectionData] =
|
const [userSelectionData, setUserSelectionData] = useState<UserSelection | null>(null);
|
||||||
useState<UserSelection | null>(null);
|
|
||||||
|
|
||||||
// Fetch user selection data
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
|
|
@ -52,6 +47,21 @@ const ClientMenu: React.FC<ClientMenuProps> = ({ lang = "en", activePage }) => {
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
function createProfileComponent(): JSX.Element {
|
||||||
|
return (
|
||||||
|
loading ? (
|
||||||
|
<ProfileLoadingState loadingText={t.loading} />
|
||||||
|
) : userType === "employee" && userSelectionData ? (
|
||||||
|
<EmployeeProfileSection userSelectionData={userSelectionData} lang={lang as "en" | "tr"} />
|
||||||
|
) : userType === "occupant" && userSelectionData ? (
|
||||||
|
<OccupantProfileSection userSelectionData={userSelectionData} lang={lang as "en" | "tr"} />
|
||||||
|
) : (
|
||||||
|
<div className="text-center text-gray-500">{t.loading}</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full bg-white shadow-sm rounded-lg overflow-hidden">
|
<div className="w-full bg-white shadow-sm rounded-lg overflow-hidden">
|
||||||
<div className="p-4 border-b border-gray-200">
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
|
@ -60,32 +70,14 @@ const ClientMenu: React.FC<ClientMenuProps> = ({ lang = "en", activePage }) => {
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Profile Section with Suspense */}
|
|
||||||
<div className="p-4 border-b border-gray-200 bg-gray-50">
|
<div className="p-4 border-b border-gray-200 bg-gray-50">
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={<div className="text-center py-4">{t.loading}</div>}
|
fallback={<div className="text-center py-4">{t.loading}</div>}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{createProfileComponent()}
|
||||||
<ProfileLoadingState loadingText={t.loading} />
|
|
||||||
) : userType === "employee" && userSelectionData ? (
|
|
||||||
<EmployeeProfileSection
|
|
||||||
userSelectionData={userSelectionData}
|
|
||||||
lang={lang as "en" | "tr"}
|
|
||||||
/>
|
|
||||||
) : userType === "occupant" && userSelectionData ? (
|
|
||||||
<OccupantProfileSection
|
|
||||||
userSelectionData={userSelectionData}
|
|
||||||
lang={lang as "en" | "tr"}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="text-center text-gray-500">{t.loading}</div>
|
|
||||||
)}
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
<NavigationMenu activePage={activePage} lang={lang as "en" | "tr"} />
|
||||||
{/* Navigation Menu
|
|
||||||
<NavigationMenu transformedMenu={transformedMenu} lang={lang} /> */}
|
|
||||||
<NavigationMenu activePage={activePage} lang={lang} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
export const NavigationLanguage = {
|
||||||
|
en: {
|
||||||
|
"/dashboard": "Dashboard",
|
||||||
|
"/append/event": "Event Board",
|
||||||
|
"/append/service": "Service Board",
|
||||||
|
"/application": "Application Board",
|
||||||
|
},
|
||||||
|
tr: {
|
||||||
|
"/dashboard": "Kontrol Paneli",
|
||||||
|
"/append/event": "Event Paneli",
|
||||||
|
"/append/service": "Servis Paneli",
|
||||||
|
"/application": "Uygulama Paneli",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getNavigationMenu(lang: string) {
|
||||||
|
return (
|
||||||
|
NavigationLanguage[lang as keyof typeof NavigationLanguage] ||
|
||||||
|
NavigationLanguage.en
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -12,17 +12,15 @@ import { CardDisplay } from "@/components/common/CardDisplay/CardDisplay";
|
||||||
import { FormMode } from "@/components/common/FormDisplay/types";
|
import { FormMode } from "@/components/common/FormDisplay/types";
|
||||||
import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay";
|
import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay";
|
||||||
import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
|
import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
|
||||||
import { LanguageSelectionComponent, Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
|
import { LanguageSelectionComponent } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
|
||||||
import { getCreateApplicationSchema, getUpdateApplicationSchema } from "./schema";
|
import { getCreateApplicationSchema, getUpdateApplicationSchema } from "./schema";
|
||||||
import { translations } from "./language";
|
import { translations } from "./language";
|
||||||
import { PageProps } from "@/validations/translations/translation";
|
import { PageProps } from "@/validations/translations/translation";
|
||||||
import { useApiData } from "@/components/common";
|
import { useApiData } from "@/components/common";
|
||||||
|
import { Language } from "@/components/common/schemas";
|
||||||
|
|
||||||
const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
const ApplicationPage: React.FC<PageProps> = ({ lang }: { lang: Language }) => {
|
||||||
// Add local state for language to ensure it persists when changed
|
|
||||||
const [lang, setLang] = useState<Language>(initialLang as Language);
|
|
||||||
|
|
||||||
// Use the API data hook directly
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
pagination,
|
pagination,
|
||||||
|
|
@ -45,8 +43,8 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const [validationSchema, setValidationSchema] = useState(() =>
|
const [validationSchema, setValidationSchema] = useState(() =>
|
||||||
mode === 'create' ? getCreateApplicationSchema(lang as "en" | "tr") :
|
mode === 'create' ? getCreateApplicationSchema(lang) :
|
||||||
mode === 'update' ? getUpdateApplicationSchema(lang as "en" | "tr") :
|
mode === 'update' ? getUpdateApplicationSchema(lang) :
|
||||||
schema.ViewApplicationSchema
|
schema.ViewApplicationSchema
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -63,8 +61,8 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
// Update validation schema when mode or language changes
|
// Update validation schema when mode or language changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValidationSchema(
|
setValidationSchema(
|
||||||
mode === 'create' ? getCreateApplicationSchema(lang as "en" | "tr") :
|
mode === 'create' ? getCreateApplicationSchema(lang) :
|
||||||
mode === 'update' ? getUpdateApplicationSchema(lang as "en" | "tr") :
|
mode === 'update' ? getUpdateApplicationSchema(lang) :
|
||||||
schema.ViewApplicationSchema
|
schema.ViewApplicationSchema
|
||||||
);
|
);
|
||||||
}, [mode, lang]);
|
}, [mode, lang]);
|
||||||
|
|
@ -98,7 +96,7 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
additionalFields: [
|
additionalFields: [
|
||||||
// {
|
// {
|
||||||
// name: "status",
|
// name: "status",
|
||||||
// label: translations[lang as "en" | "tr"].status,
|
// label: translations[lang].status,
|
||||||
// type: "select" as const,
|
// type: "select" as const,
|
||||||
// options: [
|
// options: [
|
||||||
// { value: "active", label: "Active" },
|
// { value: "active", label: "Active" },
|
||||||
|
|
@ -170,13 +168,6 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
gridCols={gridCols}
|
gridCols={gridCols}
|
||||||
setGridCols={setGridCols}
|
setGridCols={setGridCols}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Language Selection */}
|
|
||||||
<LanguageSelectionComponent
|
|
||||||
lang={lang}
|
|
||||||
translations={translations}
|
|
||||||
setLang={setLang}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ import { PageProps } from "../validations/translations/translation";
|
||||||
import { UnAuthorizedPage } from "./unauthorizedpage";
|
import { UnAuthorizedPage } from "./unauthorizedpage";
|
||||||
import menuPages from "./index";
|
import menuPages from "./index";
|
||||||
|
|
||||||
export function retrievePageByUrl(url: string): React.FC<PageProps> {
|
export function retrievePageByUrl(url: string, lang: "en" | "tr"): React.FC<PageProps> {
|
||||||
if (url in menuPages) {
|
if (url in menuPages) {
|
||||||
return menuPages[url as keyof typeof menuPages];
|
const PageComponent = menuPages[url as keyof typeof menuPages];
|
||||||
|
// Return a new component that passes the lang prop to the original component
|
||||||
|
return (props: PageProps) => <PageComponent {...props} lang={lang} />;
|
||||||
}
|
}
|
||||||
return UnAuthorizedPage;
|
// Also pass lang to UnAuthorizedPage
|
||||||
|
return (props: PageProps) => <UnAuthorizedPage {...props} lang={lang} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ interface FilteredMenuFirstLevel {
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
lang: keyof LanguageTranslation;
|
lang: keyof LanguageTranslation;
|
||||||
queryParams: { [key: string]: string | undefined };
|
queryParams?: { [key: string]: string | undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageComponent = React.ComponentType<PageProps>;
|
type PageComponent = React.ComponentType<PageProps>;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,14 @@
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
"../../menu/EmployeeProfileSection.tsx",
|
||||||
|
"src/components/menu/NavigationMenu.tsx",
|
||||||
|
"../../menu/menu.tsx"
|
||||||
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue