updated Api Defaults
This commit is contained in:
parent
1920c2a25d
commit
1ce28ec5f0
|
|
@ -40,7 +40,7 @@ class EventCluster:
|
||||||
db=db_session,
|
db=db_session,
|
||||||
).data:
|
).data:
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
event_to_save_database = Events.find_or_create(
|
event_dict_to_save = dict(
|
||||||
function_code=event.key,
|
function_code=event.key,
|
||||||
function_class=event.name,
|
function_class=event.name,
|
||||||
description=event.description,
|
description=event.description,
|
||||||
|
|
@ -54,13 +54,16 @@ class EventCluster:
|
||||||
Events.function_class,
|
Events.function_class,
|
||||||
Events.endpoint_code,
|
Events.endpoint_code,
|
||||||
Events.endpoint_uu_id,
|
Events.endpoint_uu_id,
|
||||||
],
|
]
|
||||||
)
|
)
|
||||||
|
event_to_save_database = Events.find_or_create(**event_dict_to_save)
|
||||||
if event_to_save_database.meta_data.created:
|
if event_to_save_database.meta_data.created:
|
||||||
|
print(f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}")
|
||||||
|
else:
|
||||||
|
event_to_save_database.update(**event_dict_to_save)
|
||||||
|
if event_to_save_database.meta_data.updated:
|
||||||
|
print(f"UUID: {event_to_save_database.uu_id} event is updated to {to_save_endpoint.uu_id}")
|
||||||
event_to_save_database.save(db=db_session)
|
event_to_save_database.save(db=db_session)
|
||||||
print(
|
|
||||||
f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def match_event(self, event_key: str) -> "Event":
|
def match_event(self, event_key: str) -> "Event":
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ SupersPeopleCreateEvent = Event(
|
||||||
key="ec4c2404-a61b-46c7-bbdf-ce3357e6cf41",
|
key="ec4c2404-a61b-46c7-bbdf-ce3357e6cf41",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Create events of people endpoint",
|
description="Super Users Create events of people endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update endpoint
|
# Update endpoint
|
||||||
|
|
@ -26,7 +26,7 @@ SupersPeopleUpdateEvent = Event(
|
||||||
key="91e77de4-9f29-4309-b121-4aad256d440c",
|
key="91e77de4-9f29-4309-b121-4aad256d440c",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Update events of people endpoint",
|
description="Super Users Update events of people endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# List endpoint
|
# List endpoint
|
||||||
|
|
@ -35,7 +35,7 @@ SupersPeopleListEvent = Event(
|
||||||
key="6828d280-e587-400d-a622-c318277386c3",
|
key="6828d280-e587-400d-a622-c318277386c3",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="List events of people endpoint",
|
description="Super Users List events of people endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ SuperUsersListEvent = Event(
|
||||||
key="341b394f-9f11-4abb-99e7-4b27fa6bf012",
|
key="341b394f-9f11-4abb-99e7-4b27fa6bf012",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="List events of users endpoint",
|
description="Super User List events of users endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create endpoint
|
# Create endpoint
|
||||||
|
|
@ -19,7 +19,7 @@ SuperUsersCreateEvent = Event(
|
||||||
key="4e7e189e-e015-4ff8-902d-60138cbc77a6",
|
key="4e7e189e-e015-4ff8-902d-60138cbc77a6",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Create events of users endpoint",
|
description="Super User Create events of users endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update endpoint
|
# Update endpoint
|
||||||
|
|
@ -28,7 +28,7 @@ SuperUsersUpdateEvent = Event(
|
||||||
key="efa4aa4a-d414-4391-91ee-97eb617b7755",
|
key="efa4aa4a-d414-4391-91ee-97eb617b7755",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Update events of users endpoint",
|
description="Super User Update events of users endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from ApiControllers.abstracts.default_validations import CommonHeaders
|
||||||
from ApiControllers.providers.token_provider import TokenProvider
|
from ApiControllers.providers.token_provider import TokenProvider
|
||||||
|
|
||||||
from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult
|
from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult
|
||||||
from Controllers.Postgres.response import EndpointResponse
|
from Controllers.Postgres.response import EndpointResponse, CreateEndpointResponse
|
||||||
from Schemas import Applications
|
from Schemas import Applications
|
||||||
from Validations.application.validations import (
|
from Validations.application.validations import (
|
||||||
RequestApplication,
|
RequestApplication,
|
||||||
|
|
@ -66,18 +66,18 @@ def application_create_route(
|
||||||
created_application = Applications.find_or_create(
|
created_application = Applications.find_or_create(
|
||||||
db=db_session,
|
db=db_session,
|
||||||
include_args=[
|
include_args=[
|
||||||
Applications.application_for == data.application_for,
|
Applications.application_for,
|
||||||
Applications.application_code == data.application_code,
|
Applications.application_code,
|
||||||
Applications.site_url == data.site_url,
|
Applications.site_url,
|
||||||
]
|
],
|
||||||
** created_application_dict,
|
**created_application_dict,
|
||||||
)
|
)
|
||||||
if created_application.meta_data.created:
|
if created_application.meta_data.created:
|
||||||
return EndpointResponse(
|
return CreateEndpointResponse(
|
||||||
message="MSG0001-INSERT",
|
message="MSG0001-INSERT",
|
||||||
data=created_application,
|
data=created_application,
|
||||||
).response
|
).response
|
||||||
return EndpointResponse(
|
return CreateEndpointResponse(
|
||||||
message="MSG0002-FOUND",
|
message="MSG0002-FOUND",
|
||||||
data=created_application,
|
data=created_application,
|
||||||
).response
|
).response
|
||||||
|
|
@ -98,23 +98,23 @@ def application_update_route(
|
||||||
"""
|
"""
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||||
with Applications.new_session() as db_session:
|
with Applications.new_session() as db_session:
|
||||||
updated_application_dict = data.model_dump()
|
updated_application_dict = data.model_dump(exclude_unset=True, exclude_none=True)
|
||||||
found_application = Applications.filter_one(
|
found_application = Applications.filter_one(
|
||||||
Applications.uu_id == application_uuid, db=db_session
|
Applications.uu_id == application_uuid, db=db_session
|
||||||
).data
|
).data
|
||||||
if not found_application:
|
if not found_application:
|
||||||
return EndpointResponse(
|
return CreateEndpointResponse(
|
||||||
message="MSG0002-FOUND",
|
message="MSG0002-FOUND",
|
||||||
data=found_application,
|
data=found_application,
|
||||||
).response
|
).response
|
||||||
updated_application = found_application.update(**updated_application_dict)
|
updated_application = found_application.update(db=db_session,**updated_application_dict)
|
||||||
updated_application.save(db_session)
|
updated_application.save(db_session)
|
||||||
if updated_application.meta_data.updated:
|
if updated_application.meta_data.updated:
|
||||||
return EndpointResponse(
|
return CreateEndpointResponse(
|
||||||
message="MSG0003-UPDATE",
|
message="MSG0003-UPDATE",
|
||||||
data=updated_application,
|
data=updated_application,
|
||||||
).response
|
).response
|
||||||
return EndpointResponse(
|
return CreateEndpointResponse(
|
||||||
message="MSG0003-UPDATE",
|
message="MSG0003-UPDATE",
|
||||||
data=updated_application,
|
data=updated_application,
|
||||||
).response
|
).response
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import APIRouter, Depends
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
from ApiControllers.abstracts.default_validations import CommonHeaders
|
from ApiControllers.abstracts.default_validations import CommonHeaders
|
||||||
from ApiControllers.providers.token_provider import TokenProvider
|
from ApiControllers.providers.token_provider import TokenProvider
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ ApplicationListEvent = Event(
|
||||||
key="b4efda1e-bde7-4659-ab1a-ef74c0fd88b6",
|
key="b4efda1e-bde7-4659-ab1a-ef74c0fd88b6",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="List events of users endpoint",
|
description="Super Users List applications endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create endpoint
|
# Create endpoint
|
||||||
|
|
@ -23,7 +23,7 @@ ApplicationCreateEvent = Event(
|
||||||
key="f53ca9aa-5536-4d77-9129-78d67e61db4a",
|
key="f53ca9aa-5536-4d77-9129-78d67e61db4a",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Create events of users endpoint",
|
description="Super Users Create applications endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update endpoint
|
# Update endpoint
|
||||||
|
|
@ -32,7 +32,7 @@ ApplicationUpdateEvent = Event(
|
||||||
key="0e9a855e-4e69-44b5-8ac2-825daa32840c",
|
key="0e9a855e-4e69-44b5-8ac2-825daa32840c",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Update events of users endpoint",
|
description="Super Users Update applications endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
#Bind Application to employee
|
#Bind Application to employee
|
||||||
|
|
@ -41,7 +41,7 @@ ApplicationBindEmployeeEvent = Event(
|
||||||
key="26a96c2d-bca8-41cb-8ac1-f3ca8124434b",
|
key="26a96c2d-bca8-41cb-8ac1-f3ca8124434b",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Bind events of users endpoint",
|
description="Super Users Application Bind employee endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
#Bind Application to occupant
|
#Bind Application to occupant
|
||||||
|
|
@ -50,7 +50,7 @@ ApplicationBindOccupantEvent = Event(
|
||||||
key="4eaf2bb0-2a42-4d21-ae65-a9259ebee189",
|
key="4eaf2bb0-2a42-4d21-ae65-a9259ebee189",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Bind events of users endpoint",
|
description="Super Users Application Bind occupant endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ EventsListEvent = Event(
|
||||||
key="0a08c64b-ce20-4791-b1e9-014db6b75ea7",
|
key="0a08c64b-ce20-4791-b1e9-014db6b75ea7",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="List services endpoint",
|
description="Super Users List services endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ EventRegisterServiceEvent = Event(
|
||||||
key="e18e7f89-5708-4a15-9258-99b0903ed43d",
|
key="e18e7f89-5708-4a15-9258-99b0903ed43d",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Register service endpoint",
|
description="Super Users Register service endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Bind employee extra endpoint
|
# Bind employee extra endpoint
|
||||||
|
|
@ -37,7 +37,7 @@ EventBindEmployeeExtraEvent = Event(
|
||||||
key="cd452928-4256-4fb4-b81e-0ca41d723616",
|
key="cd452928-4256-4fb4-b81e-0ca41d723616",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Bind service to employee extra endpoint",
|
description="Super Users Bind service to employee extra endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Bind occupant extra endpoint
|
# Bind occupant extra endpoint
|
||||||
|
|
@ -46,7 +46,7 @@ EventBindOccupantExtraEvent = Event(
|
||||||
key="cb11a150-8049-45c9-8cf3-d5290ffd2e4a",
|
key="cb11a150-8049-45c9-8cf3-d5290ffd2e4a",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="Bind service to occupant extra endpoint",
|
description="Super Users Bind service to occupant extra endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ ServiceEndpointListEvent = Event(
|
||||||
key="7da6ceac-925a-4faa-9cc5-3f34396b5684",
|
key="7da6ceac-925a-4faa-9cc5-3f34396b5684",
|
||||||
request_validator=None, # TODO: Add request validator
|
request_validator=None, # TODO: Add request validator
|
||||||
response_validator=None, # TODO: Add response validator
|
response_validator=None, # TODO: Add response validator
|
||||||
description="List services endpoint",
|
description="Super Users List services endpoint",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
FROM python:3.12-slim
|
|
||||||
|
|
||||||
WORKDIR /
|
|
||||||
|
|
||||||
# Install system dependencies and Poetry
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry
|
|
||||||
|
|
||||||
# Copy Poetry configuration
|
|
||||||
COPY /pyproject.toml ./pyproject.toml
|
|
||||||
|
|
||||||
# Configure Poetry and install dependencies with optimizations
|
|
||||||
RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi --no-root --only main \
|
|
||||||
&& pip cache purge && rm -rf ~/.cache/pypoetry
|
|
||||||
|
|
||||||
# Copy application code
|
|
||||||
COPY /ApiControllers /ApiControllers
|
|
||||||
COPY /ApiDefaults /ApiDefaults
|
|
||||||
COPY /Controllers /Controllers
|
|
||||||
COPY /Schemas /Schemas
|
|
||||||
|
|
||||||
COPY /ApiServices/IdentityService/Endpoints /ApiDefaults/Endpoints
|
|
||||||
COPY /ApiServices/IdentityService/Events /ApiDefaults/Events
|
|
||||||
COPY /ApiServices/IdentityService/Validations /ApiDefaults/Validations
|
|
||||||
|
|
||||||
# Set Python path to include app directory
|
|
||||||
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
|
|
||||||
|
|
||||||
# Run the application using the configured uvicorn server
|
|
||||||
CMD ["poetry", "run", "python", "ApiDefaults/app.py"]
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
from fastapi import APIRouter, Depends
|
|
||||||
|
|
||||||
from ApiControllers.abstracts.default_validations import CommonHeaders
|
|
||||||
from ApiControllers.providers.token_provider import TokenProvider
|
|
||||||
from Controllers.Postgres.pagination import PaginateOnly
|
|
||||||
from Events.people.cluster import PeopleRouterCluster
|
|
||||||
|
|
||||||
|
|
||||||
people_route = APIRouter(prefix="/people", tags=["People"])
|
|
||||||
|
|
||||||
|
|
||||||
@people_route.post(
|
|
||||||
path="/list",
|
|
||||||
description="List people endpoint",
|
|
||||||
operation_id="f102db46-031a-43e4-966a-dae6896f985b",
|
|
||||||
)
|
|
||||||
def people_route_list(
|
|
||||||
data: PaginateOnly,
|
|
||||||
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
List people endpoint
|
|
||||||
"""
|
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
|
||||||
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleList")
|
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
|
||||||
return event_cluster_matched.event_callable(data=data)
|
|
||||||
|
|
||||||
|
|
||||||
@people_route.post(
|
|
||||||
path="/create",
|
|
||||||
description="Create people endpoint",
|
|
||||||
operation_id="eb465fde-337f-4b81-94cf-28c6d4f2b1b6",
|
|
||||||
)
|
|
||||||
def people_route_create(
|
|
||||||
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Create people endpoint
|
|
||||||
"""
|
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
|
||||||
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleCreate")
|
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
|
||||||
return event_cluster_matched.event_callable()
|
|
||||||
|
|
||||||
|
|
||||||
@people_route.post(
|
|
||||||
path="/update",
|
|
||||||
description="Update people endpoint",
|
|
||||||
operation_id="c9e5ba69-6915-43f5-8f9c-a5c2aa865b89",
|
|
||||||
)
|
|
||||||
def people_route_update(
|
|
||||||
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Update people endpoint
|
|
||||||
"""
|
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
|
||||||
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleUpdate")
|
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
|
||||||
return event_cluster_matched.event_callable()
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
from fastapi import APIRouter
|
|
||||||
|
|
||||||
|
|
||||||
def get_routes() -> list[APIRouter]:
|
|
||||||
from .people.route import people_route
|
|
||||||
from .user.route import user_route
|
|
||||||
|
|
||||||
return [user_route, people_route]
|
|
||||||
|
|
||||||
|
|
||||||
def get_safe_endpoint_urls() -> list[tuple[str, str]]:
|
|
||||||
return [
|
|
||||||
("/", "GET"),
|
|
||||||
("/docs", "GET"),
|
|
||||||
("/redoc", "GET"),
|
|
||||||
("/openapi.json", "GET"),
|
|
||||||
("/metrics", "GET"),
|
|
||||||
]
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import uuid
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Header
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from ApiDefaults.config import api_config
|
|
||||||
from Events.user.cluster import UserRouterCluster
|
|
||||||
from ApiControllers.providers.token_provider import TokenProvider
|
|
||||||
from ApiControllers.abstracts.default_validations import CommonHeaders
|
|
||||||
from Controllers.Postgres.pagination import PaginateOnly
|
|
||||||
|
|
||||||
user_route = APIRouter(prefix="/user", tags=["User"])
|
|
||||||
|
|
||||||
|
|
||||||
@user_route.post(
|
|
||||||
path="/list",
|
|
||||||
description="List users endpoint",
|
|
||||||
operation_id="1aca3094-fe80-4e0f-a460-1a506419082a",
|
|
||||||
)
|
|
||||||
def user_list_route(
|
|
||||||
data: PaginateOnly,
|
|
||||||
headers: CommonHeaders,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
List users endpoint
|
|
||||||
"""
|
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
|
||||||
FoundCluster = UserRouterCluster.get_event_cluster("UserList")
|
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
|
||||||
return event_cluster_matched.event_callable(data=data)
|
|
||||||
|
|
||||||
|
|
||||||
@user_route.post(
|
|
||||||
path="/create",
|
|
||||||
description="Create users endpoint",
|
|
||||||
operation_id="9686211f-4260-485d-8076-186c22c053aa",
|
|
||||||
)
|
|
||||||
def user_create_route(
|
|
||||||
data: Any,
|
|
||||||
headers: CommonHeaders,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Create users endpoint
|
|
||||||
"""
|
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
|
||||||
FoundCluster = UserRouterCluster.get_event_cluster("UserCreate")
|
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
|
||||||
return event_cluster_matched.event_callable(data=data)
|
|
||||||
|
|
||||||
|
|
||||||
@user_route.post(
|
|
||||||
path="/update",
|
|
||||||
description="Update users endpoint",
|
|
||||||
operation_id="268e887b-5ff5-4f99-b1be-7e127e28a198",
|
|
||||||
)
|
|
||||||
def user_update_route(
|
|
||||||
data: Any,
|
|
||||||
headers: CommonHeaders,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Update users endpoint
|
|
||||||
"""
|
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
|
||||||
FoundCluster = UserRouterCluster.get_event_cluster("UserUpdate")
|
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
|
||||||
return event_cluster_matched.event_callable(data=data)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
from .people.cluster import PeopleRouterCluster
|
|
||||||
from .user.cluster import UserRouterCluster
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"PeopleRouterCluster",
|
|
||||||
"UserRouterCluster",
|
|
||||||
]
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
|
|
||||||
from .supers_events import (
|
|
||||||
SupersPeopleCreateEvent,
|
|
||||||
SupersPeopleUpdateEvent,
|
|
||||||
SupersPeopleListEvent,
|
|
||||||
)
|
|
||||||
|
|
||||||
PeopleRouterCluster = RouterCluster(name="PeopleRouterCluster")
|
|
||||||
|
|
||||||
PeopleEventClusterList = EventCluster(
|
|
||||||
name="PeopleList", endpoint_uu_id="f102db46-031a-43e4-966a-dae6896f985b"
|
|
||||||
)
|
|
||||||
PeopleEventClusterList.add_event(SupersPeopleListEvent)
|
|
||||||
PeopleEventClusterCreate = EventCluster(
|
|
||||||
name="PeopleCreate", endpoint_uu_id="eb465fde-337f-4b81-94cf-28c6d4f2b1b6"
|
|
||||||
)
|
|
||||||
PeopleEventClusterCreate.add_event(SupersPeopleCreateEvent)
|
|
||||||
PeopleEventClusterUpdate = EventCluster(
|
|
||||||
name="PeopleUpdate", endpoint_uu_id="c9e5ba69-6915-43f5-8f9c-a5c2aa865b89"
|
|
||||||
)
|
|
||||||
PeopleEventClusterUpdate.add_event(SupersPeopleUpdateEvent)
|
|
||||||
|
|
||||||
PeopleRouterCluster.set_event_cluster(PeopleEventClusterList)
|
|
||||||
PeopleRouterCluster.set_event_cluster(PeopleEventClusterCreate)
|
|
||||||
PeopleRouterCluster.set_event_cluster(PeopleEventClusterUpdate)
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
from ApiControllers.abstracts.event_clusters import Event
|
|
||||||
from Validations.people.validations import (
|
|
||||||
REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV,
|
|
||||||
)
|
|
||||||
from Controllers.Postgres.pagination import (
|
|
||||||
ListOptions,
|
|
||||||
Pagination,
|
|
||||||
PaginationResult,
|
|
||||||
PaginateOnly,
|
|
||||||
)
|
|
||||||
from Controllers.Postgres.response import EndpointResponse
|
|
||||||
from Schemas import People
|
|
||||||
|
|
||||||
|
|
||||||
SupersPeopleCreateEvent = Event(
|
|
||||||
name="supers_people_create",
|
|
||||||
key="ec4c2404-a61b-46c7-bbdf-ce3357e6cf41",
|
|
||||||
request_validator=None, # TODO: Add request validator
|
|
||||||
response_validator=None, # TODO: Add response validator
|
|
||||||
description="Create events of people endpoint",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update endpoint
|
|
||||||
SupersPeopleUpdateEvent = Event(
|
|
||||||
name="supers_people_update",
|
|
||||||
key="91e77de4-9f29-4309-b121-4aad256d440c",
|
|
||||||
request_validator=None, # TODO: Add request validator
|
|
||||||
response_validator=None, # TODO: Add response validator
|
|
||||||
description="Update events of people endpoint",
|
|
||||||
)
|
|
||||||
|
|
||||||
# List endpoint
|
|
||||||
SupersPeopleListEvent = Event(
|
|
||||||
name="supers_people_list",
|
|
||||||
key="6828d280-e587-400d-a622-c318277386c3",
|
|
||||||
request_validator=None, # TODO: Add request validator
|
|
||||||
response_validator=None, # TODO: Add response validator
|
|
||||||
description="List events of people endpoint",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def supers_people_create_callable():
|
|
||||||
"""
|
|
||||||
Example callable method
|
|
||||||
"""
|
|
||||||
|
|
||||||
return {
|
|
||||||
"completed": True,
|
|
||||||
"message": "Example callable method 2",
|
|
||||||
"info": {
|
|
||||||
"host": "example_host",
|
|
||||||
"user_agent": "example_user_agent",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SupersPeopleCreateEvent.event_callable = supers_people_create_callable
|
|
||||||
|
|
||||||
|
|
||||||
def supers_people_update_callable():
|
|
||||||
"""
|
|
||||||
Example callable method
|
|
||||||
"""
|
|
||||||
|
|
||||||
return {
|
|
||||||
"completed": True,
|
|
||||||
"message": "Example callable method 2",
|
|
||||||
"info": {
|
|
||||||
"host": "example_host",
|
|
||||||
"user_agent": "example_user_agent",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SupersPeopleUpdateEvent.event_callable = supers_people_update_callable
|
|
||||||
|
|
||||||
|
|
||||||
def supers_people_list_callable(data: PaginateOnly):
|
|
||||||
"""
|
|
||||||
Example callable method
|
|
||||||
"""
|
|
||||||
list_options = PaginateOnly(**data.model_dump())
|
|
||||||
with People.new_session() as db_session:
|
|
||||||
if list_options.query:
|
|
||||||
people_list = People.filter_all(
|
|
||||||
*People.convert(list_options.query), db=db_session
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
people_list = People.filter_all(db=db_session)
|
|
||||||
pagination = Pagination(data=people_list)
|
|
||||||
pagination.change(**list_options.model_dump())
|
|
||||||
pagination_result = PaginationResult(
|
|
||||||
data=people_list,
|
|
||||||
pagination=pagination,
|
|
||||||
response_model=REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV,
|
|
||||||
)
|
|
||||||
return EndpointResponse(
|
|
||||||
message="MSG0002-LIST",
|
|
||||||
pagination_result=pagination_result,
|
|
||||||
).response
|
|
||||||
|
|
||||||
|
|
||||||
SupersPeopleListEvent.event_callable = supers_people_list_callable
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
|
|
||||||
from .supers_events import (
|
|
||||||
SuperUsersListEvent,
|
|
||||||
SuperUsersCreateEvent,
|
|
||||||
SuperUsersUpdateEvent,
|
|
||||||
)
|
|
||||||
|
|
||||||
UserRouterCluster = RouterCluster(name="UserRouterCluster")
|
|
||||||
|
|
||||||
UserEventClusterList = EventCluster(
|
|
||||||
name="UserList", endpoint_uu_id="1aca3094-fe80-4e0f-a460-1a506419082a"
|
|
||||||
)
|
|
||||||
UserEventClusterList.add_event(SuperUsersListEvent)
|
|
||||||
|
|
||||||
UserEventClusterCreate = EventCluster(
|
|
||||||
name="UserCreate", endpoint_uu_id="9686211f-4260-485d-8076-186c22c053aa"
|
|
||||||
)
|
|
||||||
UserEventClusterCreate.add_event(SuperUsersCreateEvent)
|
|
||||||
|
|
||||||
UserEventClusterUpdate = EventCluster(
|
|
||||||
name="UserUpdate", endpoint_uu_id="268e887b-5ff5-4f99-b1be-7e127e28a198"
|
|
||||||
)
|
|
||||||
UserEventClusterUpdate.add_event(SuperUsersUpdateEvent)
|
|
||||||
|
|
||||||
UserRouterCluster.set_event_cluster(UserEventClusterList)
|
|
||||||
UserRouterCluster.set_event_cluster(UserEventClusterCreate)
|
|
||||||
UserRouterCluster.set_event_cluster(UserEventClusterUpdate)
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
from ApiControllers.abstracts.event_clusters import Event
|
|
||||||
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
|
|
||||||
from Controllers.Postgres.response import EndpointResponse
|
|
||||||
from Schemas import Users
|
|
||||||
|
|
||||||
|
|
||||||
# List endpoint
|
|
||||||
SuperUsersListEvent = Event(
|
|
||||||
name="supers_users_list",
|
|
||||||
key="341b394f-9f11-4abb-99e7-4b27fa6bf012",
|
|
||||||
request_validator=None, # TODO: Add request validator
|
|
||||||
response_validator=None, # TODO: Add response validator
|
|
||||||
description="List events of users endpoint",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create endpoint
|
|
||||||
SuperUsersCreateEvent = Event(
|
|
||||||
name="supers_users_create",
|
|
||||||
key="4e7e189e-e015-4ff8-902d-60138cbc77a6",
|
|
||||||
request_validator=None, # TODO: Add request validator
|
|
||||||
response_validator=None, # TODO: Add response validator
|
|
||||||
description="Create events of users endpoint",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update endpoint
|
|
||||||
SuperUsersUpdateEvent = Event(
|
|
||||||
name="supers_users_update",
|
|
||||||
key="efa4aa4a-d414-4391-91ee-97eb617b7755",
|
|
||||||
request_validator=None, # TODO: Add request validator
|
|
||||||
response_validator=None, # TODO: Add response validator
|
|
||||||
description="Update events of users endpoint",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def supers_users_list_callable(list_options: PaginateOnly):
|
|
||||||
"""
|
|
||||||
Example callable method
|
|
||||||
"""
|
|
||||||
list_options = PaginateOnly(**list_options.model_dump())
|
|
||||||
with Users.new_session() as db_session:
|
|
||||||
if list_options.query:
|
|
||||||
users_list = Users.filter_all(
|
|
||||||
*Users.convert(list_options.query), db=db_session
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
users_list = Users.filter_all(db=db_session)
|
|
||||||
pagination = Pagination(data=users_list)
|
|
||||||
pagination.change(**list_options.model_dump())
|
|
||||||
pagination_result = PaginationResult(
|
|
||||||
data=users_list,
|
|
||||||
pagination=pagination,
|
|
||||||
# response_model="",
|
|
||||||
).pagination.as_dict
|
|
||||||
return EndpointResponse(
|
|
||||||
message="MSG0003-LIST",
|
|
||||||
pagination_result=pagination_result,
|
|
||||||
).response
|
|
||||||
|
|
||||||
|
|
||||||
SuperUsersListEvent.event_callable = supers_users_list_callable
|
|
||||||
|
|
||||||
|
|
||||||
def supers_users_create_callable():
|
|
||||||
"""
|
|
||||||
Example callable method
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"completed": True,
|
|
||||||
"message": "Example callable method 2",
|
|
||||||
"info": {
|
|
||||||
"host": "example_host",
|
|
||||||
"user_agent": "example_user_agent",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SuperUsersCreateEvent.event_callable = supers_users_create_callable
|
|
||||||
|
|
||||||
|
|
||||||
def supers_users_update_callable():
|
|
||||||
"""
|
|
||||||
Example callable method
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"completed": True,
|
|
||||||
"message": "Example callable method 2",
|
|
||||||
"info": {
|
|
||||||
"host": "example_host",
|
|
||||||
"user_agent": "example_user_agent",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SuperUsersUpdateEvent.event_callable = supers_users_update_callable
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
|
|
||||||
class ListOptions(BaseModel):
|
|
||||||
"""
|
|
||||||
Query for list option abilities
|
|
||||||
"""
|
|
||||||
|
|
||||||
page: Optional[int] = 1
|
|
||||||
size: Optional[int] = 10
|
|
||||||
order_field: Optional[str] = "id"
|
|
||||||
order_type: Optional[str] = "asc"
|
|
||||||
# include_joins: Optional[list] = None
|
|
||||||
query: Optional[dict] = None
|
|
||||||
|
|
||||||
|
|
||||||
class PaginateOnly(BaseModel):
|
|
||||||
"""
|
|
||||||
Query for list option abilities
|
|
||||||
"""
|
|
||||||
|
|
||||||
page: Optional[int] = 1
|
|
||||||
size: Optional[int] = 10
|
|
||||||
order_field: Optional[str] = "id"
|
|
||||||
order_type: Optional[str] = "asc"
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV(
|
|
||||||
BaseModel
|
|
||||||
):
|
|
||||||
uu_id: str
|
|
||||||
created_at: str
|
|
||||||
updated_at: str
|
|
||||||
person_tag: str
|
|
||||||
expiry_starts: str
|
|
||||||
expiry_ends: str
|
|
||||||
firstname: str
|
|
||||||
middle_name: str
|
|
||||||
surname: str
|
|
||||||
birth_date: str
|
|
||||||
birth_place: str
|
|
||||||
sex_code: str
|
|
||||||
country_code: str
|
|
||||||
tax_no: str
|
|
||||||
active: bool
|
|
||||||
deleted: bool
|
|
||||||
is_confirmed: bool
|
|
||||||
is_notification_send: bool
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class FF7C859A068EB4583A47AB5924EC8C19A033881823FBA42EAAD089BF7AC8059CE(BaseModel):
|
|
||||||
"""
|
|
||||||
a13143ade48954c2ba3f86869e027de5b28c8a9b619bf4ef28264a8e375371601
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class F1B82565925FF4F5DAD2A36370788305A0A362E031EAC4A9E8BDFF4F35E265A6C(BaseModel):
|
|
||||||
"""
|
|
||||||
aa487ab3bfd9e4e6abc2db714ac6197f60bbc9068ac6541e7a815d5b1e969796b
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class F3117E7D66FE6471C8452B97AB504EF0C29822B6395CA4D65A18FDD563F0EC8D7(BaseModel):
|
|
||||||
"""
|
|
||||||
a1bf55a214b684438a97a47e4f097ac7ae27b0dff03c4475cbd4301e24a032aac
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class F33B4DE316B8A456480DD6ED5B5E2D35A2E6FCAF74BAC40D7A2970D318B153F85(BaseModel):
|
|
||||||
"""
|
|
||||||
F33B4DE316B8A456480DD6ED5B5E2D35A2E6FCAF74BAC40D7A2970D318B153F85
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
@ -134,3 +134,21 @@ class EndpointResponse(BaseModel):
|
||||||
"data": resutl_data,
|
"data": resutl_data,
|
||||||
"pagination": pagination_dict,
|
"pagination": pagination_dict,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CreateEndpointResponse(BaseModel):
|
||||||
|
"""Create endpoint response model."""
|
||||||
|
|
||||||
|
completed: bool = True
|
||||||
|
message: str = "Success"
|
||||||
|
data: Any
|
||||||
|
|
||||||
|
@property
|
||||||
|
def response(self):
|
||||||
|
"""Convert response to dictionary format."""
|
||||||
|
return {
|
||||||
|
"completed": self.completed,
|
||||||
|
"message": self.message,
|
||||||
|
"data": self.data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
"use server";
|
||||||
|
import { retrieveAccessToken } from "@/apicalls/cookies/token";
|
||||||
|
import {
|
||||||
|
DEFAULT_RESPONSE,
|
||||||
|
defaultHeaders,
|
||||||
|
FetchOptions,
|
||||||
|
HttpMethod,
|
||||||
|
ApiResponse,
|
||||||
|
DEFAULT_TIMEOUT,
|
||||||
|
} from "./basics";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a promise that rejects after a specified timeout
|
||||||
|
* @param ms Timeout in milliseconds
|
||||||
|
* @param controller AbortController to abort the fetch request
|
||||||
|
* @returns A promise that rejects after the timeout
|
||||||
|
*/
|
||||||
|
const createTimeoutPromise = (
|
||||||
|
ms: number,
|
||||||
|
controller: AbortController
|
||||||
|
): Promise<never> => {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
controller.abort();
|
||||||
|
reject(new Error(`Request timed out after ${ms}ms`));
|
||||||
|
}, ms);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a standardized API response
|
||||||
|
* @param response The response data
|
||||||
|
* @param statusCode HTTP status code
|
||||||
|
* @returns Standardized API response
|
||||||
|
*/
|
||||||
|
const prepareResponse = <T>(
|
||||||
|
response: T,
|
||||||
|
statusCode: number
|
||||||
|
): ApiResponse<T> => {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
status: statusCode,
|
||||||
|
data: response || ({} as T),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error preparing response:", error);
|
||||||
|
return {
|
||||||
|
...DEFAULT_RESPONSE,
|
||||||
|
error: "Response parsing error",
|
||||||
|
} as ApiResponse<T>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core fetch function with timeout and error handling
|
||||||
|
* @param url The URL to fetch
|
||||||
|
* @param options Fetch options
|
||||||
|
* @param headers Request headers
|
||||||
|
* @param payload Request payload
|
||||||
|
* @returns API response
|
||||||
|
*/
|
||||||
|
async function coreFetch<T>(
|
||||||
|
url: string,
|
||||||
|
options: FetchOptions = {},
|
||||||
|
headers: Record<string, string> = defaultHeaders,
|
||||||
|
payload?: any
|
||||||
|
): Promise<ApiResponse<T>> {
|
||||||
|
const { method = "POST", cache = false, timeout = DEFAULT_TIMEOUT } = options;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
cache: cache ? "force-cache" : "no-cache",
|
||||||
|
signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add body if needed
|
||||||
|
if (method !== "GET" && payload) {
|
||||||
|
fetchOptions.body = JSON.stringify(
|
||||||
|
// Handle special case for updateDataWithToken
|
||||||
|
payload.payload ? payload.payload : payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create timeout promise
|
||||||
|
const timeoutPromise = createTimeoutPromise(timeout, controller);
|
||||||
|
|
||||||
|
// Race between fetch and timeout
|
||||||
|
const response = (await Promise.race([
|
||||||
|
fetch(url, fetchOptions),
|
||||||
|
timeoutPromise,
|
||||||
|
])) as Response;
|
||||||
|
|
||||||
|
const responseJson = await response.json();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
console.log("Fetching:", url, fetchOptions);
|
||||||
|
console.log("Response:", responseJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareResponse(responseJson, response.status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Fetch error (${url}):`, error);
|
||||||
|
return {
|
||||||
|
...DEFAULT_RESPONSE,
|
||||||
|
error: error instanceof Error ? error.message : "Network error",
|
||||||
|
} as ApiResponse<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data without authentication
|
||||||
|
*/
|
||||||
|
async function fetchData<T>(
|
||||||
|
endpoint: string,
|
||||||
|
payload?: any,
|
||||||
|
method: HttpMethod = "POST",
|
||||||
|
cache: boolean = false,
|
||||||
|
timeout: number = DEFAULT_TIMEOUT
|
||||||
|
): Promise<ApiResponse<T>> {
|
||||||
|
return coreFetch<T>(
|
||||||
|
endpoint,
|
||||||
|
{ method, cache, timeout },
|
||||||
|
defaultHeaders,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data with authentication token
|
||||||
|
*/
|
||||||
|
async function fetchDataWithToken<T>(
|
||||||
|
endpoint: string,
|
||||||
|
payload?: any,
|
||||||
|
method: HttpMethod = "POST",
|
||||||
|
cache: boolean = false,
|
||||||
|
timeout: number = DEFAULT_TIMEOUT
|
||||||
|
): Promise<ApiResponse<T>> {
|
||||||
|
const accessToken = (await retrieveAccessToken()) || "";
|
||||||
|
const headers = {
|
||||||
|
...defaultHeaders,
|
||||||
|
"eys-acs-tkn": accessToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
return coreFetch<T>(endpoint, { method, cache, timeout }, headers, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update data with authentication token and UUID
|
||||||
|
*/
|
||||||
|
async function updateDataWithToken<T>(
|
||||||
|
endpoint: string,
|
||||||
|
uuid: string,
|
||||||
|
payload?: any,
|
||||||
|
method: HttpMethod = "POST",
|
||||||
|
cache: boolean = false,
|
||||||
|
timeout: number = DEFAULT_TIMEOUT
|
||||||
|
): Promise<ApiResponse<T>> {
|
||||||
|
const accessToken = (await retrieveAccessToken()) || "";
|
||||||
|
const headers = {
|
||||||
|
...defaultHeaders,
|
||||||
|
"eys-acs-tkn": accessToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
return coreFetch<T>(
|
||||||
|
`${endpoint}/${uuid}`,
|
||||||
|
{ method, cache, timeout },
|
||||||
|
headers,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fetchData, fetchDataWithToken, updateDataWithToken };
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
"use server";
|
|
||||||
import { retrieveAccessToken } from "@/apicalls/cookies/token";
|
|
||||||
|
|
||||||
const defaultHeaders = {
|
|
||||||
accept: "application/json",
|
|
||||||
language: "tr",
|
|
||||||
domain: "evyos.com.tr",
|
|
||||||
tz: "GMT+3",
|
|
||||||
"Content-type": "application/json",
|
|
||||||
};
|
|
||||||
|
|
||||||
const DefaultResponse = {
|
|
||||||
error: "Hata tipi belirtilmedi",
|
|
||||||
status: "500",
|
|
||||||
data: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const cacheList = ["no-cache", "no-store", "force-cache", "only-if-cached"];
|
|
||||||
|
|
||||||
const prepareResponse = (response: any, statusCode: number) => {
|
|
||||||
try {
|
|
||||||
return {
|
|
||||||
status: statusCode,
|
|
||||||
data: response || {},
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error preparing response:", error);
|
|
||||||
return {
|
|
||||||
...DefaultResponse,
|
|
||||||
error: "Response parsing error",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchData = async (
|
|
||||||
endpoint: string,
|
|
||||||
payload: any,
|
|
||||||
method: string = "POST",
|
|
||||||
cache: boolean = false
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
const headers = {
|
|
||||||
...defaultHeaders,
|
|
||||||
};
|
|
||||||
const fetchOptions: RequestInit = {
|
|
||||||
method,
|
|
||||||
headers,
|
|
||||||
cache: cache ? "force-cache" : "no-cache",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (method === "POST" && payload) {
|
|
||||||
fetchOptions.body = JSON.stringify(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(endpoint, fetchOptions);
|
|
||||||
const responseJson = await response.json();
|
|
||||||
console.log("Fetching:", endpoint, fetchOptions);
|
|
||||||
console.log("Response:", responseJson);
|
|
||||||
return prepareResponse(responseJson, response.status);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fetch error:", error);
|
|
||||||
return {
|
|
||||||
...DefaultResponse,
|
|
||||||
error: error instanceof Error ? error.message : "Network error",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateDataWithToken = async (
|
|
||||||
endpoint: string,
|
|
||||||
uuid: string,
|
|
||||||
payload: any,
|
|
||||||
method: string = "POST",
|
|
||||||
cache: boolean = false
|
|
||||||
) => {
|
|
||||||
const accessToken = (await retrieveAccessToken()) || "";
|
|
||||||
const headers = {
|
|
||||||
...defaultHeaders,
|
|
||||||
"eys-acs-tkn": accessToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fetchOptions: RequestInit = {
|
|
||||||
method,
|
|
||||||
headers,
|
|
||||||
cache: cache ? "force-cache" : "no-cache",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (method !== "GET" && payload) {
|
|
||||||
fetchOptions.body = JSON.stringify(payload.payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(`${endpoint}/${uuid}`, fetchOptions);
|
|
||||||
const responseJson = await response.json();
|
|
||||||
console.log("Fetching:", `${endpoint}/${uuid}`, fetchOptions);
|
|
||||||
console.log("Response:", responseJson);
|
|
||||||
return prepareResponse(responseJson, response.status);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Update error:", error);
|
|
||||||
return {
|
|
||||||
...DefaultResponse,
|
|
||||||
error: error instanceof Error ? error.message : "Network error",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchDataWithToken = async (
|
|
||||||
endpoint: string,
|
|
||||||
payload: any,
|
|
||||||
method: string = "POST",
|
|
||||||
cache: boolean = false
|
|
||||||
) => {
|
|
||||||
const accessToken = (await retrieveAccessToken()) || "";
|
|
||||||
const headers = {
|
|
||||||
...defaultHeaders,
|
|
||||||
"eys-acs-tkn": accessToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fetchOptions: RequestInit = {
|
|
||||||
method,
|
|
||||||
headers,
|
|
||||||
cache: cache ? "force-cache" : "no-cache",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (method === "POST" && payload) {
|
|
||||||
fetchOptions.body = JSON.stringify(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(endpoint, fetchOptions);
|
|
||||||
const responseJson = await response.json();
|
|
||||||
console.log("Fetching:", endpoint, fetchOptions);
|
|
||||||
console.log("Response:", responseJson);
|
|
||||||
return prepareResponse(responseJson, response.status);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fetch with token error:", error);
|
|
||||||
return {
|
|
||||||
...DefaultResponse,
|
|
||||||
error: error instanceof Error ? error.message : "Network error",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export { fetchData, fetchDataWithToken, updateDataWithToken };
|
|
||||||
|
|
@ -3,18 +3,34 @@ const formatServiceUrl = (url: string) => {
|
||||||
return url.startsWith("http") ? url : `http://${url}`;
|
return url.startsWith("http") ? url : `http://${url}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const baseUrlAuth = formatServiceUrl(
|
const baseUrlAuth = formatServiceUrl(
|
||||||
process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "auth_service:8001"
|
process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "auth_service:8001"
|
||||||
);
|
);
|
||||||
export const baseUrlPeople = formatServiceUrl(
|
const baseUrlPeople = formatServiceUrl(
|
||||||
process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "identity_service:8002"
|
process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "identity_service:8002"
|
||||||
);
|
);
|
||||||
// export const baseUrlEvent = formatServiceUrl(
|
const baseUrlApplication = formatServiceUrl(
|
||||||
// process.env.NEXT_PUBLIC_EVENT_SERVICE_URL || "eventservice:8888"
|
process.env.NEXT_PUBLIC_MANAGEMENT_SERVICE_URL || "management_service:8004"
|
||||||
// );
|
);
|
||||||
export const tokenSecret = process.env.TOKENSECRET_90 || "";
|
|
||||||
|
|
||||||
export const cookieObject: any = {
|
// Types for better type safety
|
||||||
|
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
||||||
|
|
||||||
|
interface ApiResponse<T = any> {
|
||||||
|
status: number;
|
||||||
|
data: T;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FetchOptions {
|
||||||
|
method?: HttpMethod;
|
||||||
|
cache?: boolean;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenSecret = process.env.TOKENSECRET_90 || "";
|
||||||
|
|
||||||
|
const cookieObject: any = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: "/",
|
path: "/",
|
||||||
sameSite: "none",
|
sameSite: "none",
|
||||||
|
|
@ -23,53 +39,29 @@ export const cookieObject: any = {
|
||||||
priority: "high",
|
priority: "high",
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FilterListInterface {
|
// Constants
|
||||||
page?: number | null | undefined;
|
const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
||||||
size?: number | null | undefined;
|
const defaultHeaders = {
|
||||||
orderField?: string | null | undefined;
|
accept: "application/json",
|
||||||
orderType?: string | null | undefined;
|
language: "tr",
|
||||||
includeJoins?: any[] | null | undefined;
|
domain: "management.com.tr",
|
||||||
query?: any | null | undefined;
|
tz: "GMT+3",
|
||||||
}
|
"Content-type": "application/json",
|
||||||
|
};
|
||||||
|
const DEFAULT_RESPONSE: ApiResponse = {
|
||||||
|
error: "Hata tipi belirtilmedi",
|
||||||
|
status: 500,
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
|
||||||
class FilterList {
|
export type { HttpMethod, ApiResponse, FetchOptions };
|
||||||
page: number;
|
export {
|
||||||
size: number;
|
DEFAULT_TIMEOUT,
|
||||||
orderField: string;
|
DEFAULT_RESPONSE,
|
||||||
orderType: string;
|
defaultHeaders,
|
||||||
includeJoins: any[];
|
baseUrlAuth,
|
||||||
query: any;
|
baseUrlPeople,
|
||||||
|
baseUrlApplication,
|
||||||
constructor({
|
tokenSecret,
|
||||||
page = 1,
|
cookieObject,
|
||||||
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({});
|
|
||||||
|
|
||||||
export { FilterList, defaultFilterList };
|
|
||||||
export type { FilterListInterface };
|
|
||||||
|
|
|
||||||
|
|
@ -2,29 +2,17 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } 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 { CreateComponentProps } from "./types";
|
import { CreateComponentProps, FieldDefinition } from "./types";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm, SubmitHandler } from "react-hook-form";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
|
|
||||||
// Import field definitions type
|
|
||||||
interface FieldDefinition {
|
|
||||||
type: string;
|
|
||||||
group: string;
|
|
||||||
label: string;
|
|
||||||
options?: string[];
|
|
||||||
readOnly?: boolean;
|
|
||||||
required?: boolean;
|
|
||||||
defaultValue?: any;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CreateComponent<T>({
|
export function CreateComponent<T>({
|
||||||
refetch,
|
refetch,
|
||||||
setMode,
|
setMode,
|
||||||
|
|
@ -33,6 +21,7 @@ export function CreateComponent<T>({
|
||||||
lang,
|
lang,
|
||||||
translations,
|
translations,
|
||||||
formProps = {},
|
formProps = {},
|
||||||
|
apiUrl,
|
||||||
}: CreateComponentProps<T>) {
|
}: CreateComponentProps<T>) {
|
||||||
const t = translations[lang as keyof typeof translations] || {};
|
const t = translations[lang as keyof typeof translations] || {};
|
||||||
|
|
||||||
|
|
@ -75,7 +64,7 @@ export function CreateComponent<T>({
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
} = useForm({
|
} = useForm<Record<string, any>>({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
||||||
});
|
});
|
||||||
|
|
@ -83,14 +72,26 @@ export function CreateComponent<T>({
|
||||||
const formValues = watch();
|
const formValues = watch();
|
||||||
|
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
const onSubmit = async (data: Record<string, any>) => {
|
const onSubmit: SubmitHandler<Record<string, any>> = async (data) => {
|
||||||
try {
|
try {
|
||||||
console.log("Form data to save:", data);
|
if (apiUrl) {
|
||||||
|
const createUrl = `${apiUrl}/create`;
|
||||||
|
const response = await fetch(createUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
console.log("Response:", response.ok);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Here you would make an API call to save the data
|
const createdItem = await response.json();
|
||||||
// For example: await createApplication(data);
|
console.log("Created item:", createdItem);
|
||||||
|
}
|
||||||
|
|
||||||
// Mock API call success
|
|
||||||
if (refetch) refetch();
|
if (refetch) refetch();
|
||||||
setMode("list");
|
setMode("list");
|
||||||
setSelectedItem(null);
|
setSelectedItem(null);
|
||||||
|
|
@ -140,13 +141,13 @@ export function CreateComponent<T>({
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
<Label htmlFor={fieldName}>
|
||||||
{t[fieldName] || field.label}
|
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
placeholder={t[fieldName] || field.label}
|
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
disabled={field.readOnly}
|
disabled={field.readOnly}
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
|
|
@ -159,13 +160,13 @@ export function CreateComponent<T>({
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
<Label htmlFor={fieldName}>
|
||||||
{t[fieldName] || field.label}
|
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
placeholder={t[fieldName] || field.label}
|
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
rows={3}
|
rows={3}
|
||||||
disabled={field.readOnly}
|
disabled={field.readOnly}
|
||||||
/>
|
/>
|
||||||
|
|
@ -179,7 +180,7 @@ export function CreateComponent<T>({
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
<Label htmlFor={fieldName}>
|
||||||
{t[fieldName] || field.label}
|
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|
@ -188,7 +189,7 @@ export function CreateComponent<T>({
|
||||||
disabled={field.readOnly}
|
disabled={field.readOnly}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder={t[fieldName] || field.label} />
|
<SelectValue placeholder={t[fieldName] || field.label[lang as "en" | "tr"]} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{field.options?.map((option) => (
|
{field.options?.map((option) => (
|
||||||
|
|
@ -216,7 +217,7 @@ export function CreateComponent<T>({
|
||||||
disabled={field.readOnly}
|
disabled={field.readOnly}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={fieldName}>
|
<Label htmlFor={fieldName}>
|
||||||
{t[fieldName] || field.label}
|
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
</Label>
|
</Label>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
|
|
@ -231,7 +232,7 @@ export function CreateComponent<T>({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(() => console.log("Form data to save:"))}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<Card className="w-full mb-6">
|
<Card className="w-full mb-6">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{t.create || "Create"}</CardTitle>
|
<CardTitle>{t.create || "Create"}</CardTitle>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export function FormDisplay<T>({
|
||||||
lang,
|
lang,
|
||||||
translations,
|
translations,
|
||||||
formProps = {},
|
formProps = {},
|
||||||
|
apiUrl,
|
||||||
}: FormDisplayProps<T>) {
|
}: FormDisplayProps<T>) {
|
||||||
const [enhancedFormProps, setEnhancedFormProps] = useState(formProps);
|
const [enhancedFormProps, setEnhancedFormProps] = useState(formProps);
|
||||||
|
|
||||||
|
|
@ -84,6 +85,7 @@ export function FormDisplay<T>({
|
||||||
lang={lang}
|
lang={lang}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
formProps={enhancedFormProps}
|
formProps={enhancedFormProps}
|
||||||
|
apiUrl={apiUrl}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "update":
|
case "update":
|
||||||
|
|
@ -98,6 +100,7 @@ export function FormDisplay<T>({
|
||||||
lang={lang}
|
lang={lang}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
formProps={enhancedFormProps}
|
formProps={enhancedFormProps}
|
||||||
|
apiUrl={apiUrl}
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
case "view":
|
case "view":
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } 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 } from "./types";
|
import { UpdateComponentProps, FieldDefinition } from "./types";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
|
@ -13,18 +13,6 @@ import { useForm } from "react-hook-form";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
|
|
||||||
// Import field definitions type
|
|
||||||
interface FieldDefinition {
|
|
||||||
type: string;
|
|
||||||
group: string;
|
|
||||||
label: string;
|
|
||||||
options?: string[];
|
|
||||||
readOnly?: boolean;
|
|
||||||
required?: boolean;
|
|
||||||
defaultValue?: any;
|
|
||||||
name?: string; // Add name property for TypeScript compatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UpdateComponent<T>({
|
export function UpdateComponent<T>({
|
||||||
initialData,
|
initialData,
|
||||||
refetch,
|
refetch,
|
||||||
|
|
@ -33,23 +21,20 @@ export function UpdateComponent<T>({
|
||||||
onCancel,
|
onCancel,
|
||||||
lang,
|
lang,
|
||||||
translations,
|
translations,
|
||||||
|
apiUrl,
|
||||||
formProps = {},
|
formProps = {},
|
||||||
}: 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;
|
||||||
|
|
||||||
// Group fields by their group property
|
|
||||||
const [groupedFields, setGroupedFields] = useState<Record<string, FieldDefinition[]>>({});
|
const [groupedFields, setGroupedFields] = useState<Record<string, FieldDefinition[]>>({});
|
||||||
|
|
||||||
// Process field definitions to group them
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(fieldDefinitions).length > 0) {
|
if (Object.keys(fieldDefinitions).length > 0) {
|
||||||
const groups: Record<string, FieldDefinition[]> = {};
|
const groups: Record<string, FieldDefinition[]> = {};
|
||||||
|
|
||||||
// Group fields by their group property
|
|
||||||
Object.entries(fieldDefinitions).forEach(([fieldName, definition]) => {
|
Object.entries(fieldDefinitions).forEach(([fieldName, definition]) => {
|
||||||
const def = definition as FieldDefinition;
|
const def = definition as FieldDefinition;
|
||||||
if (!groups[def.group]) {
|
if (!groups[def.group]) {
|
||||||
|
|
@ -62,32 +47,36 @@ export function UpdateComponent<T>({
|
||||||
}
|
}
|
||||||
}, [fieldDefinitions]);
|
}, [fieldDefinitions]);
|
||||||
|
|
||||||
// Initialize form with default values from field definitions and initialData
|
|
||||||
const defaultValues: Record<string, any> = {};
|
const defaultValues: Record<string, any> = {};
|
||||||
Object.entries(fieldDefinitions).forEach(([key, def]) => {
|
Object.entries(fieldDefinitions).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 : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Merge initialData with default values
|
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
Object.assign(defaultValues, initialData as Record<string, any>);
|
Object.assign(defaultValues, initialData as Record<string, any>);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup form with validation schema if available
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors, isSubmitting, isValid },
|
||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
reset,
|
reset,
|
||||||
|
trigger,
|
||||||
} = useForm({
|
} = useForm({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
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(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
reset({ ...initialData as Record<string, any> });
|
reset({ ...initialData as Record<string, any> });
|
||||||
|
|
@ -99,17 +88,52 @@ export function UpdateComponent<T>({
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
const onSubmit = async (data: any) => {
|
const onSubmit = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
console.log("Form data to update:", data);
|
|
||||||
|
|
||||||
// Here you would make an API call to update the data
|
const isFormValid = await trigger();
|
||||||
// For example: await updateApplication(data);
|
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
|
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();
|
if (refetch) refetch();
|
||||||
setMode("list");
|
setMode("list");
|
||||||
setSelectedItem(null);
|
setSelectedItem(null);
|
||||||
|
} catch (fetchError) {
|
||||||
|
console.error("Error during fetch:", fetchError);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating form:", error);
|
console.error("Error details:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -125,43 +149,53 @@ export function UpdateComponent<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
|
|
||||||
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
|
|
||||||
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render a field based on its type
|
|
||||||
const renderField = (fieldName: string, field: FieldDefinition) => {
|
const renderField = (fieldName: string, field: FieldDefinition) => {
|
||||||
const errorMessage = errors[fieldName]?.message as string;
|
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) {
|
switch (field.type) {
|
||||||
case "text":
|
case "text":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Input
|
<Input
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
placeholder={t[fieldName] || field.label}
|
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
|
|
@ -172,16 +206,12 @@ export function UpdateComponent<T>({
|
||||||
case "textarea":
|
case "textarea":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Textarea
|
<Textarea
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
placeholder={t[fieldName] || field.label}
|
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
rows={3}
|
rows={3}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
|
|
@ -192,17 +222,13 @@ export function UpdateComponent<T>({
|
||||||
case "select":
|
case "select":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Select
|
<Select
|
||||||
value={formValues[fieldName]}
|
value={fieldValue}
|
||||||
onValueChange={(value) => handleSelectChange(fieldName, value)}
|
onValueChange={(value) => handleSelectChange(fieldName, value)}
|
||||||
disabled={field.readOnly}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder={t[fieldName] || field.label} />
|
<SelectValue placeholder={t[fieldName] || field.label[lang as "en" | "tr"]} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{field.options?.map((option) => (
|
{field.options?.map((option) => (
|
||||||
|
|
@ -223,16 +249,12 @@ export function UpdateComponent<T>({
|
||||||
<div className="flex items-center space-x-2" key={fieldName}>
|
<div className="flex items-center space-x-2" key={fieldName}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
checked={formValues[fieldName]}
|
checked={fieldValue}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleCheckboxChange(fieldName, checked as boolean)
|
handleCheckboxChange(fieldName, checked as boolean)
|
||||||
}
|
}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -242,15 +264,11 @@ export function UpdateComponent<T>({
|
||||||
case "date":
|
case "date":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Input
|
<Input
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
type="date"
|
type="date"
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
|
|
@ -277,6 +295,13 @@ export function UpdateComponent<T>({
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-4 w-4" />
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{t.formErrors || "Please correct the errors in the form"}
|
{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>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,10 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } 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 { ViewComponentProps } from "./types";
|
import { ViewComponentProps, FieldDefinition } from "./types";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
// Import field definitions type
|
|
||||||
export interface FieldDefinition {
|
|
||||||
type: string;
|
|
||||||
group: string;
|
|
||||||
label: string;
|
|
||||||
options?: string[];
|
|
||||||
readOnly?: boolean;
|
|
||||||
required?: boolean;
|
|
||||||
defaultValue?: any;
|
|
||||||
name?: string; // Add name property for TypeScript compatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to format field label
|
// Utility function to format field label
|
||||||
const formatFieldLabel = (fieldName: string) =>
|
const formatFieldLabel = (fieldName: string) =>
|
||||||
|
|
@ -103,7 +92,7 @@ const ViewFieldGroup: React.FC<{
|
||||||
key={fieldName}
|
key={fieldName}
|
||||||
fieldName={fieldName}
|
fieldName={fieldName}
|
||||||
value={value}
|
value={value}
|
||||||
label={field.label}
|
label={field.label[lang as "en" | "tr"]}
|
||||||
lang={lang}
|
lang={lang}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
hasError={hasError}
|
hasError={hasError}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,17 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
// Import field definitions type
|
||||||
|
export interface FieldDefinition {
|
||||||
|
type: string;
|
||||||
|
group: string;
|
||||||
|
label: { tr: string; en: string };
|
||||||
|
options?: string[];
|
||||||
|
readOnly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
defaultValue?: any;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Define the FormMode type to ensure consistency
|
// Define the FormMode type to ensure consistency
|
||||||
export type FormMode = "list" | "create" | "update" | "view";
|
export type FormMode = "list" | "create" | "update" | "view";
|
||||||
|
|
||||||
|
|
@ -12,12 +24,14 @@ export interface BaseFormProps<T> {
|
||||||
lang: string;
|
lang: string;
|
||||||
translations: Record<string, Record<string, string>>;
|
translations: Record<string, Record<string, string>>;
|
||||||
formProps?: Record<string, any>;
|
formProps?: Record<string, any>;
|
||||||
|
apiUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateComponentProps<T> extends BaseFormProps<T> {}
|
export interface CreateComponentProps<T> extends BaseFormProps<T> {}
|
||||||
|
|
||||||
export interface UpdateComponentProps<T> extends BaseFormProps<T> {
|
export interface UpdateComponentProps<T> extends BaseFormProps<T> {
|
||||||
initialData: T; // Required for update
|
initialData: T; // Required for update
|
||||||
|
apiUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewComponentProps<T> extends BaseFormProps<T> {
|
export interface ViewComponentProps<T> extends BaseFormProps<T> {
|
||||||
|
|
@ -34,4 +48,5 @@ export interface FormDisplayProps<T> {
|
||||||
lang: string;
|
lang: string;
|
||||||
translations: Record<string, Record<string, string>>;
|
translations: Record<string, Record<string, string>>;
|
||||||
formProps?: Record<string, any>;
|
formProps?: Record<string, any>;
|
||||||
|
apiUrl: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@ export function useApiData<T>(
|
||||||
params: RequestParams
|
params: RequestParams
|
||||||
): Promise<ApiResponse<T>> => {
|
): Promise<ApiResponse<T>> => {
|
||||||
try {
|
try {
|
||||||
// Prepare the request body with action and all params
|
// Prepare the request body with pagination parameters
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
action: "list",
|
|
||||||
page: params.page,
|
page: params.page,
|
||||||
size: params.size,
|
size: params.size,
|
||||||
orderField: params.orderField,
|
orderField: params.orderField,
|
||||||
|
|
@ -26,8 +25,11 @@ export function useApiData<T>(
|
||||||
query: params.query,
|
query: params.query,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Construct the list endpoint URL
|
||||||
|
const listEndpoint = `${endpoint}/list`;
|
||||||
|
|
||||||
// Make the API request using POST
|
// Make the API request using POST
|
||||||
const response = await fetch(endpoint, {
|
const response = await fetch(listEndpoint, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { retrievePagebyUrl, retrievePageList } from "@/apicalls/cookies/token";
|
import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
|
||||||
import { retrievePageByUrlAndPageId } from "@/components/navigator/retriever";
|
import { PageProps } from "@/validations/translations/translation";
|
||||||
import React from "react";
|
import React, { ReactElement } from "react";
|
||||||
|
|
||||||
export interface DashboardPageParams {
|
export interface DashboardPageParams {
|
||||||
/**
|
/**
|
||||||
* The active page path, e.g., "/individual", "/dashboard"
|
* The active page path, e.g., "/application", "/dashboard"
|
||||||
*/
|
*/
|
||||||
pageUrl: string;
|
pageUrl: string;
|
||||||
|
|
||||||
|
|
@ -33,20 +33,15 @@ export interface DashboardPageResult {
|
||||||
/**
|
/**
|
||||||
* The page component to render
|
* The page component to render
|
||||||
*/
|
*/
|
||||||
PageComponent: any;
|
PageComponent: React.FC<PageProps>;
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of site URLs for the menu
|
|
||||||
*/
|
|
||||||
siteUrlsList: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to retrieve and prepare dashboard page data for client-frontend application
|
* Hook to retrieve and prepare dashboard page data
|
||||||
* Handles retrieving site URLs, page components, and other necessary data
|
* Throws errors for Next.js error boundary to catch
|
||||||
*
|
*
|
||||||
* @param params The dashboard page parameters
|
* @param params The dashboard page parameters
|
||||||
* @returns The processed dashboard page data including site URLs
|
* @returns The processed dashboard page data
|
||||||
* @throws Error if page URL is invalid or page component is not found
|
* @throws Error if page URL is invalid or page component is not found
|
||||||
*/
|
*/
|
||||||
export async function useDashboardPage({
|
export async function useDashboardPage({
|
||||||
|
|
@ -55,7 +50,6 @@ 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
|
// 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}`);
|
||||||
|
|
@ -80,25 +74,8 @@ export async function useDashboardPage({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get site URLs list
|
|
||||||
let siteUrlsList: string[] = [];
|
|
||||||
try {
|
|
||||||
siteUrlsList = (await retrievePageList()) || [];
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error retrieving site URLs:", err);
|
|
||||||
throw new Error(`Failed to retrieve site URLs: ${err}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get page component
|
// Get page component
|
||||||
let pageToDirect;
|
const PageComponent = retrievePageByUrl(pageUrl);
|
||||||
let PageComponent;
|
|
||||||
try {
|
|
||||||
pageToDirect = await retrievePagebyUrl(pageUrl);
|
|
||||||
PageComponent = retrievePageByUrlAndPageId(pageToDirect, pageUrl);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error retrieving page component:", err);
|
|
||||||
throw new Error(`Page component not found for URL: ${pageUrl}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if page component exists
|
// Check if page component exists
|
||||||
if (!PageComponent) {
|
if (!PageComponent) {
|
||||||
|
|
@ -110,7 +87,6 @@ export async function useDashboardPage({
|
||||||
searchParamsInstance,
|
searchParamsInstance,
|
||||||
lang,
|
lang,
|
||||||
PageComponent,
|
PageComponent,
|
||||||
siteUrlsList,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,17 @@
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { fetchData, fetchDataWithToken } from "../api-fetcher";
|
import { fetchDataWithToken } from "../api-fetcher";
|
||||||
import { baseUrlApplication } from "../basics";
|
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 applicationListEndpoint = `${baseUrlApplication}/application/list`;
|
||||||
const applicationUpdateEndpoint = `${baseUrlApplication}/application/update`;
|
const applicationUpdateEndpoint = `${baseUrlApplication}/application/update`;
|
||||||
const applicationCreateEndpoint = `${baseUrlApplication}/application/create`;
|
const applicationCreateEndpoint = `${baseUrlApplication}/application/create`;
|
||||||
const applicationDeleteEndpoint = `${baseUrlApplication}/application/delete`;
|
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 {
|
try {
|
||||||
const response = await fetchDataWithToken(
|
const response = await fetchDataWithToken(
|
||||||
applicationListEndpoint,
|
applicationListEndpoint,
|
||||||
|
|
@ -32,12 +25,62 @@ async function listApplications(payload: PageListOptions) {
|
||||||
"POST",
|
"POST",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
return response?.status === 200 || response?.status === 202
|
|
||||||
? response.data
|
// Format the response to match the expected PaginatedApiResponse format
|
||||||
: null;
|
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) {
|
} catch (error) {
|
||||||
console.error("Error fetching application list:", 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",
|
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
|
// Constants
|
||||||
const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
||||||
const defaultHeaders = {
|
const defaultHeaders = {
|
||||||
|
|
@ -102,18 +54,11 @@ const DEFAULT_RESPONSE: ApiResponse = {
|
||||||
data: {},
|
data: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type {
|
export type { HttpMethod, ApiResponse, FetchOptions };
|
||||||
FilterList,
|
|
||||||
FilterListInterface,
|
|
||||||
HttpMethod,
|
|
||||||
ApiResponse,
|
|
||||||
FetchOptions,
|
|
||||||
};
|
|
||||||
export {
|
export {
|
||||||
DEFAULT_TIMEOUT,
|
DEFAULT_TIMEOUT,
|
||||||
DEFAULT_RESPONSE,
|
DEFAULT_RESPONSE,
|
||||||
defaultHeaders,
|
defaultHeaders,
|
||||||
defaultFilterList,
|
|
||||||
baseUrlAuth,
|
baseUrlAuth,
|
||||||
baseUrlPeople,
|
baseUrlPeople,
|
||||||
baseUrlApplication,
|
baseUrlApplication,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export interface PaginateOnly {
|
||||||
orderType?: string[];
|
orderType?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageListOptions {
|
export interface PaginationParams {
|
||||||
page?: number;
|
page?: number;
|
||||||
size?: number;
|
size?: number;
|
||||||
orderField?: string[];
|
orderField?: string[];
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default async function DashLayout({
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-full">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
import { NextRequest } from "next/server";
|
import { createCreateHandler } from "@/app/api/utils";
|
||||||
// Import the createApplication function when it's available
|
import { createApplication } from "@/apicalls/application/application";
|
||||||
// import { createApplication } from "@/apicalls/application/application";
|
|
||||||
import { createCreateHandler } from "../../utils";
|
|
||||||
|
|
||||||
// Create a handler for creating applications using our utility function
|
export const POST = createCreateHandler(createApplication);
|
||||||
// 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();
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { NextRequest } from "next/server";
|
|
||||||
import { listApplications } from "@/apicalls/application/application";
|
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);
|
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 { createUpdateHandler } from "../../utils";
|
||||||
|
import { updateApplication } from "@/apicalls/application/application";
|
||||||
|
|
||||||
// Create a handler for updating applications using our utility function
|
export const POST = createUpdateHandler(updateApplication);
|
||||||
// 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();
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
paginationResponse,
|
paginationResponse,
|
||||||
createResponse,
|
createResponse,
|
||||||
updateResponse,
|
updateResponse,
|
||||||
deleteResponse
|
deleteResponse,
|
||||||
} from "./responseHandlers";
|
} from "./responseHandlers";
|
||||||
import { withErrorHandling, validateRequiredFields } from "./requestHandlers";
|
import { withErrorHandling, validateRequiredFields } from "./requestHandlers";
|
||||||
import {
|
import {
|
||||||
|
|
@ -14,7 +14,7 @@ import {
|
||||||
ListFunction,
|
ListFunction,
|
||||||
CreateFunction,
|
CreateFunction,
|
||||||
UpdateFunction,
|
UpdateFunction,
|
||||||
DeleteFunction
|
DeleteFunction,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,7 +23,7 @@ import {
|
||||||
* @param body Request body
|
* @param body Request body
|
||||||
* @param listFunction The function to call to get the list data
|
* @param listFunction The function to call to get the list data
|
||||||
*/
|
*/
|
||||||
export async function handleListOperation<T>(
|
export async function handleListOperation(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
body: any,
|
body: any,
|
||||||
listFunction: ListFunction
|
listFunction: ListFunction
|
||||||
|
|
@ -59,8 +59,7 @@ export async function handleListOperation<T>(
|
||||||
* @param createFunction The function to call to create the item
|
* @param createFunction The function to call to create the item
|
||||||
* @param requiredFields Array of required field names
|
* @param requiredFields Array of required field names
|
||||||
*/
|
*/
|
||||||
export async function handleCreateOperation<T>(
|
export async function handleCreateOperation(
|
||||||
request: NextRequest,
|
|
||||||
body: any,
|
body: any,
|
||||||
createFunction?: CreateFunction,
|
createFunction?: CreateFunction,
|
||||||
requiredFields: string[] = []
|
requiredFields: string[] = []
|
||||||
|
|
@ -91,23 +90,28 @@ export async function handleCreateOperation<T>(
|
||||||
* @param request NextRequest object
|
* @param request NextRequest object
|
||||||
* @param body Request body
|
* @param body Request body
|
||||||
* @param updateFunction The function to call to update the item
|
* @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,
|
request: NextRequest,
|
||||||
body: any,
|
body: any,
|
||||||
updateFunction?: UpdateFunction,
|
updateFunction?: UpdateFunction
|
||||||
requiredFields: string[] = ['id']
|
|
||||||
) {
|
) {
|
||||||
// Validate required fields
|
// Skip validation as it's handled at the page level with Yup
|
||||||
const validation = validateRequiredFields(body, requiredFields);
|
// and IDs are extracted from URL paths for update/delete operations
|
||||||
if (!validation.valid) {
|
|
||||||
return errorResponse(validation.error as string, 400);
|
// 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) {
|
if (updateFunction) {
|
||||||
const result = await updateFunction(body.id, body);
|
console.log("Body:", body);
|
||||||
|
const result = await updateFunction(body, uuid);
|
||||||
return updateResponse(result);
|
return updateResponse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,23 +122,26 @@ export async function handleUpdateOperation<T>(
|
||||||
/**
|
/**
|
||||||
* Generic delete operation handler
|
* Generic delete operation handler
|
||||||
* @param request NextRequest object
|
* @param request NextRequest object
|
||||||
* @param body Request body
|
|
||||||
* @param deleteFunction The function to call to delete the item
|
* @param deleteFunction The function to call to delete the item
|
||||||
*/
|
*/
|
||||||
export async function handleDeleteOperation(
|
export async function handleDeleteOperation(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
body: any,
|
|
||||||
deleteFunction?: DeleteFunction
|
deleteFunction?: DeleteFunction
|
||||||
) {
|
) {
|
||||||
// Validate that ID is provided
|
// Skip ID validation as it's handled at the page level
|
||||||
const validation = validateRequiredFields(body, ['id']);
|
// and IDs are typically extracted from URL paths
|
||||||
if (!validation.valid) {
|
|
||||||
return errorResponse(validation.error as string, 400);
|
// 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) {
|
if (deleteFunction) {
|
||||||
await deleteFunction(body.id);
|
await deleteFunction(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a success response
|
// Return a success response
|
||||||
|
|
@ -146,7 +153,7 @@ export async function handleDeleteOperation(
|
||||||
* @param listFunction The function to call to get the list data
|
* @param listFunction The function to call to get the list data
|
||||||
*/
|
*/
|
||||||
export function createListHandler(listFunction: ListFunction) {
|
export function createListHandler(listFunction: ListFunction) {
|
||||||
return withErrorHandling((request, body) =>
|
return withErrorHandling((request: NextRequest, body: any) =>
|
||||||
handleListOperation(request, body, listFunction)
|
handleListOperation(request, body, listFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -160,22 +167,19 @@ export function createCreateHandler(
|
||||||
createFunction?: CreateFunction,
|
createFunction?: CreateFunction,
|
||||||
requiredFields: string[] = []
|
requiredFields: string[] = []
|
||||||
) {
|
) {
|
||||||
return withErrorHandling((request, body) =>
|
console.log("Required fields:", requiredFields);
|
||||||
handleCreateOperation(request, body, createFunction, requiredFields)
|
return withErrorHandling((body: any) =>
|
||||||
|
handleCreateOperation(body, createFunction, requiredFields)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a wrapped update handler with error handling
|
* Create a wrapped update handler with error handling
|
||||||
* @param updateFunction The function to call to update the item
|
* @param updateFunction The function to call to update the item
|
||||||
* @param requiredFields Array of required field names
|
|
||||||
*/
|
*/
|
||||||
export function createUpdateHandler(
|
export function createUpdateHandler(updateFunction?: UpdateFunction) {
|
||||||
updateFunction?: UpdateFunction,
|
return withErrorHandling((request: NextRequest, body: any) =>
|
||||||
requiredFields: string[] = ['id']
|
handleUpdateOperation(request, body, updateFunction)
|
||||||
) {
|
|
||||||
return withErrorHandling((request, body) =>
|
|
||||||
handleUpdateOperation(request, body, updateFunction, requiredFields)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,10 +187,8 @@ export function createUpdateHandler(
|
||||||
* Create a wrapped delete handler with error handling
|
* Create a wrapped delete handler with error handling
|
||||||
* @param deleteFunction The function to call to delete the item
|
* @param deleteFunction The function to call to delete the item
|
||||||
*/
|
*/
|
||||||
export function createDeleteHandler(
|
export function createDeleteHandler(deleteFunction?: DeleteFunction) {
|
||||||
deleteFunction?: DeleteFunction
|
return withErrorHandling((request: NextRequest) =>
|
||||||
) {
|
handleDeleteOperation(request, deleteFunction)
|
||||||
return withErrorHandling((request, body) =>
|
|
||||||
handleDeleteOperation(request, body, deleteFunction)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,14 +72,37 @@ 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 {
|
||||||
console.log("Form data to save:", data);
|
|
||||||
|
|
||||||
// Make an API call to save the data if apiUrl is provided
|
|
||||||
if (apiUrl) {
|
if (apiUrl) {
|
||||||
// Use the create endpoint by appending '/create' to the apiUrl
|
|
||||||
const createUrl = `${apiUrl}/create`;
|
const createUrl = `${apiUrl}/create`;
|
||||||
const response = await fetch(createUrl, {
|
const response = await fetch(createUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -87,13 +111,13 @@ export function CreateComponent<T>({
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
|
console.log("Response:", response.ok);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`API error: ${response.status}`);
|
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();
|
if (refetch) refetch();
|
||||||
|
|
@ -116,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -237,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>
|
||||||
|
|
@ -269,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}
|
||||||
|
|
@ -100,6 +123,7 @@ export function FormDisplay<T>({
|
||||||
lang={lang}
|
lang={lang}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
formProps={enhancedFormProps}
|
formProps={enhancedFormProps}
|
||||||
|
apiUrl={apiUrl}
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
case "view":
|
case "view":
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -21,6 +21,7 @@ export function UpdateComponent<T>({
|
||||||
onCancel,
|
onCancel,
|
||||||
lang,
|
lang,
|
||||||
translations,
|
translations,
|
||||||
|
apiUrl,
|
||||||
formProps = {},
|
formProps = {},
|
||||||
}: UpdateComponentProps<T>) {
|
}: UpdateComponentProps<T>) {
|
||||||
const t = translations[lang as keyof typeof translations] || {};
|
const t = translations[lang as keyof typeof translations] || {};
|
||||||
|
|
@ -29,75 +30,179 @@ export function UpdateComponent<T>({
|
||||||
const fieldDefinitions = formProps.fieldDefinitions || {};
|
const fieldDefinitions = formProps.fieldDefinitions || {};
|
||||||
const validationSchema = formProps.validationSchema;
|
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[]>>({});
|
const [groupedFields, setGroupedFields] = useState<Record<string, FieldDefinition[]>>({});
|
||||||
|
|
||||||
// Process field definitions to group them
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(fieldDefinitions).length > 0) {
|
if (Object.keys(processedFieldDefinitions).length > 0) {
|
||||||
const groups: Record<string, FieldDefinition[]> = {};
|
const groups: Record<string, FieldDefinition[]> = {};
|
||||||
|
|
||||||
// Group fields by their group property
|
// Group the processed field definitions
|
||||||
Object.entries(fieldDefinitions).forEach(([fieldName, definition]) => {
|
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]);
|
||||||
|
|
||||||
// Initialize form with default values from field definitions and initialData
|
|
||||||
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 : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Merge initialData with default values
|
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
Object.assign(defaultValues, initialData as Record<string, any>);
|
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 {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors, isSubmitting, isValid },
|
||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
reset,
|
reset,
|
||||||
|
trigger,
|
||||||
} = useForm({
|
} = useForm({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
resolver: validationSchema ? zodResolver(validationSchema) : undefined,
|
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(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
reset({ ...initialData as Record<string, any> });
|
reset({ ...initialData as Record<string, any> });
|
||||||
}
|
}
|
||||||
}, [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
|
||||||
const onSubmit = async (data: any) => {
|
const onSubmit = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
console.log("Form data to update:", data);
|
|
||||||
|
|
||||||
// Here you would make an API call to update the data
|
const isFormValid = await trigger();
|
||||||
// For example: await updateApplication(data);
|
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
|
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();
|
if (refetch) refetch();
|
||||||
setMode("list");
|
setMode("list");
|
||||||
setSelectedItem(null);
|
setSelectedItem(null);
|
||||||
|
} catch (fetchError) {
|
||||||
|
console.error("Error during fetch:", fetchError);
|
||||||
|
}
|
||||||
} catch (error) {
|
} 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
|
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render a field based on its type
|
|
||||||
const renderField = (fieldName: string, field: FieldDefinition) => {
|
const renderField = (fieldName: string, field: FieldDefinition) => {
|
||||||
const errorMessage = errors[fieldName]?.message as string;
|
const errorMessage = errors[fieldName]?.message as string;
|
||||||
|
const fieldValue = formValues[fieldName];
|
||||||
|
|
||||||
switch (field.type) {
|
const renderLabel = () => (
|
||||||
case "text":
|
|
||||||
return (
|
|
||||||
<div className="space-y-2" key={fieldName}>
|
|
||||||
<Label htmlFor={fieldName}>
|
<Label htmlFor={fieldName}>
|
||||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
{field.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
</Label>
|
</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}>
|
||||||
|
{renderLabel()}
|
||||||
<Input
|
<Input
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
|
|
@ -160,16 +277,12 @@ export function UpdateComponent<T>({
|
||||||
case "textarea":
|
case "textarea":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Textarea
|
<Textarea
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
placeholder={t[fieldName] || field.label[lang as "en" | "tr"]}
|
||||||
rows={3}
|
rows={3}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
|
|
@ -180,14 +293,10 @@ export function UpdateComponent<T>({
|
||||||
case "select":
|
case "select":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Select
|
<Select
|
||||||
value={formValues[fieldName]}
|
value={fieldValue}
|
||||||
onValueChange={(value) => handleSelectChange(fieldName, value)}
|
onValueChange={(value) => handleSelectChange(fieldName, value)}
|
||||||
disabled={field.readOnly}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder={t[fieldName] || field.label[lang as "en" | "tr"]} />
|
<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}>
|
<div className="flex items-center space-x-2" key={fieldName}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
checked={formValues[fieldName]}
|
checked={fieldValue}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleCheckboxChange(fieldName, checked as boolean)
|
handleCheckboxChange(fieldName, checked as boolean)
|
||||||
}
|
}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -230,15 +335,11 @@ export function UpdateComponent<T>({
|
||||||
case "date":
|
case "date":
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2" key={fieldName}>
|
<div className="space-y-2" key={fieldName}>
|
||||||
<Label htmlFor={fieldName}>
|
{renderLabel()}
|
||||||
{t[fieldName] || field.label[lang as "en" | "tr"]}
|
|
||||||
{field.required && <span className="text-red-500 ml-1">*</span>}
|
|
||||||
</Label>
|
|
||||||
<Input
|
<Input
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
type="date"
|
type="date"
|
||||||
{...register(fieldName)}
|
{...register(fieldName)}
|
||||||
disabled={field.readOnly}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||||
|
|
@ -259,16 +360,33 @@ 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">
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-4 w-4" />
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{t.formErrors || "Please correct the errors in the form"}
|
{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>
|
</AlertDescription>
|
||||||
</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]) => (
|
||||||
|
|
@ -285,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>
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ export interface CreateComponentProps<T> extends BaseFormProps<T> {}
|
||||||
|
|
||||||
export interface UpdateComponentProps<T> extends BaseFormProps<T> {
|
export interface UpdateComponentProps<T> extends BaseFormProps<T> {
|
||||||
initialData: T; // Required for update
|
initialData: T; // Required for update
|
||||||
|
apiUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewComponentProps<T> extends BaseFormProps<T> {
|
export interface ViewComponentProps<T> extends BaseFormProps<T> {
|
||||||
|
|
@ -47,5 +48,5 @@ export interface FormDisplayProps<T> {
|
||||||
lang: string;
|
lang: string;
|
||||||
translations: Record<string, Record<string, string>>;
|
translations: Record<string, Record<string, string>>;
|
||||||
formProps?: Record<string, any>;
|
formProps?: Record<string, any>;
|
||||||
apiUrl?: string;
|
apiUrl: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,7 @@
|
||||||
export type LanguageKey = "en" | "tr";
|
import {
|
||||||
|
LanguageKey,
|
||||||
export interface TranslationSet {
|
TranslationSet,
|
||||||
showing: string;
|
} from "@/validations/translations/translation";
|
||||||
of: string;
|
|
||||||
items: string;
|
|
||||||
total: string;
|
|
||||||
filtered: string;
|
|
||||||
page: string;
|
|
||||||
previous: string;
|
|
||||||
next: string;
|
|
||||||
itemsPerPage: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fieldLanguageTranslation = {
|
export const fieldLanguageTranslation = {
|
||||||
tr: {
|
tr: {
|
||||||
|
|
@ -53,6 +44,8 @@ export const translations = {
|
||||||
update: "Update",
|
update: "Update",
|
||||||
delete: "Delete",
|
delete: "Delete",
|
||||||
view: "View",
|
view: "View",
|
||||||
|
save: "Save",
|
||||||
|
cancel: "Cancel",
|
||||||
|
|
||||||
// Search and filters
|
// Search and filters
|
||||||
search: "Search",
|
search: "Search",
|
||||||
|
|
@ -104,6 +97,14 @@ export const translations = {
|
||||||
availableApplications: "Available Applications",
|
availableApplications: "Available Applications",
|
||||||
code: "Code",
|
code: "Code",
|
||||||
sortBy: "Sort by:",
|
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: {
|
tr: {
|
||||||
// Page title
|
// Page title
|
||||||
|
|
@ -115,7 +116,8 @@ export const translations = {
|
||||||
update: "Güncelle",
|
update: "Güncelle",
|
||||||
delete: "Sil",
|
delete: "Sil",
|
||||||
view: "Görüntüle",
|
view: "Görüntüle",
|
||||||
|
save: "Kaydet",
|
||||||
|
cancel: "İptal",
|
||||||
// Search and filters
|
// Search and filters
|
||||||
search: "Ara",
|
search: "Ara",
|
||||||
typeSelection: "Tür Seçimi",
|
typeSelection: "Tür Seçimi",
|
||||||
|
|
@ -166,6 +168,14 @@ export const translations = {
|
||||||
availableApplications: "Mevcut Uygulamalar",
|
availableApplications: "Mevcut Uygulamalar",
|
||||||
code: "Kod",
|
code: "Kod",
|
||||||
sortBy: "Sırala:",
|
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";
|
"use client";
|
||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import * as schema from "./schema";
|
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 { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Building, Filter, User } from "lucide-react";
|
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 { CreateButton } from "@/components/common/ActionButtonsDisplay/CreateButton";
|
||||||
import { PaginationToolsComponent } from "@/components/common/PaginationModifiers/PaginationToolsComponent";
|
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 { 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, 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" }) => {
|
const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
// Add local state for language to ensure it persists when changed
|
// 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 [selectedItem, setSelectedItem] = useState<schema.ApplicationData | null>(null);
|
||||||
const [gridCols, setGridCols] = useState<GridSize>(3);
|
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
|
// Fields to display in the cards
|
||||||
const showFields = ["application_code", "site_url", "application_type"];
|
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
|
// Handle card actions
|
||||||
const handleCardClick = (item: schema.ApplicationData) => {
|
const handleCardClick = (item: schema.ApplicationData) => {
|
||||||
|
|
@ -115,7 +143,9 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
|
|
||||||
const handleUpdateClick = (item: schema.ApplicationData) => {
|
const handleUpdateClick = (item: schema.ApplicationData) => {
|
||||||
setSelectedItem(item);
|
setSelectedItem(item);
|
||||||
|
setTimeout(() => {
|
||||||
setMode("update");
|
setMode("update");
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle create button click
|
// Handle create button click
|
||||||
|
|
@ -282,12 +312,8 @@ const ApplicationPage: React.FC<PageProps> = ({ lang: initialLang = "en" }) => {
|
||||||
translations={translations}
|
translations={translations}
|
||||||
apiUrl='/api/applications'
|
apiUrl='/api/applications'
|
||||||
formProps={{
|
formProps={{
|
||||||
fieldDefinitions: mode === 'create' ? schema.createFieldDefinitions :
|
fieldDefinitions,
|
||||||
mode === 'update' ? schema.updateFieldDefinitions :
|
validationSchema,
|
||||||
schema.viewFieldDefinitions,
|
|
||||||
validationSchema: mode === 'create' ? schema.CreateApplicationSchema :
|
|
||||||
mode === 'update' ? schema.UpdateApplicationSchema :
|
|
||||||
schema.ViewApplicationSchema,
|
|
||||||
fieldsByMode: schema.fieldsByMode
|
fieldsByMode: schema.fieldsByMode
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,32 @@ export interface ApplicationData {
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base schema with all possible fields
|
// Validation error messages by language
|
||||||
const ApplicationBaseSchema = z.object({
|
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
|
// Identification fields
|
||||||
uu_id: z.number().optional(),
|
uu_id: z.string().optional(),
|
||||||
name: z.string().min(1, "Name is required"),
|
name: z.string().min(1, errorMessages[lang].nameRequired),
|
||||||
application_code: z.string().min(1, "Application code is required"),
|
application_code: z.string().min(1, errorMessages[lang].applicationCodeRequired),
|
||||||
|
|
||||||
// Application details
|
// Application details
|
||||||
site_url: z.string().min(1, "Site URL is required"),
|
site_url: z.string().min(1, errorMessages[lang].siteUrlRequired),
|
||||||
application_type: z.string().min(1, "Application type is required"),
|
application_type: z.string().min(1, errorMessages[lang].applicationTypeRequired),
|
||||||
application_for: z.string().optional(),
|
application_for: z.string().optional(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
|
|
||||||
|
|
@ -37,6 +53,9 @@ const ApplicationBaseSchema = z.object({
|
||||||
updated_at: z.string().optional(),
|
updated_at: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// For backward compatibility
|
||||||
|
const ApplicationBaseSchema = getApplicationBaseSchema("en");
|
||||||
|
|
||||||
const ApplicationBaseTranslationTr = {
|
const ApplicationBaseTranslationTr = {
|
||||||
uu_id: "UUID",
|
uu_id: "UUID",
|
||||||
name: "Name",
|
name: "Name",
|
||||||
|
|
@ -65,28 +84,46 @@ const ApplicationBaseTranslationEn = {
|
||||||
updated_at: "Updated At",
|
updated_at: "Updated At",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Schema for creating a new application
|
// Create schema for creating new applications with language support
|
||||||
export const CreateApplicationSchema = ApplicationBaseSchema.omit({
|
const getCreateApplicationSchema = (lang: "en" | "tr" = "en") =>
|
||||||
|
getApplicationBaseSchema(lang).omit({
|
||||||
uu_id: true,
|
uu_id: true,
|
||||||
created_at: true,
|
created_at: true,
|
||||||
updated_at: true,
|
updated_at: true,
|
||||||
deleted: true,
|
deleted: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Schema for updating an existing application
|
// Update schema for updating existing applications with language support
|
||||||
export const UpdateApplicationSchema = ApplicationBaseSchema.omit({
|
const getUpdateApplicationSchema = (lang: "en" | "tr" = "en") =>
|
||||||
|
getApplicationBaseSchema(lang).omit({
|
||||||
created_at: true,
|
created_at: true,
|
||||||
updated_at: true,
|
updated_at: true,
|
||||||
deleted: true,
|
deleted: true,
|
||||||
}).required({
|
}).required({
|
||||||
uu_id: true,
|
uu_id: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// For backward compatibility
|
||||||
|
const CreateApplicationSchema = getCreateApplicationSchema("en");
|
||||||
|
const UpdateApplicationSchema = getUpdateApplicationSchema("en");
|
||||||
|
|
||||||
// Schema for viewing an application (all fields)
|
// Schema for viewing an application (all fields)
|
||||||
export const ViewApplicationSchema = ApplicationBaseSchema;
|
const ViewApplicationSchema = ApplicationBaseSchema;
|
||||||
|
|
||||||
// Default schema (used for validation)
|
// 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 ApplicationFormData = z.infer<typeof ApplicationSchema>;
|
||||||
export type CreateApplicationFormData = z.infer<typeof CreateApplicationSchema>;
|
export type CreateApplicationFormData = z.infer<typeof CreateApplicationSchema>;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,21 @@ interface PageProps {
|
||||||
|
|
||||||
type PageComponent = React.ComponentType<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 {
|
export type {
|
||||||
PageComponent,
|
PageComponent,
|
||||||
PageProps,
|
PageProps,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
services:
|
services:
|
||||||
client_frontend:
|
# client_frontend:
|
||||||
container_name: client_frontend
|
# container_name: client_frontend
|
||||||
build:
|
# build:
|
||||||
context: .
|
# context: .
|
||||||
dockerfile: WebServices/client-frontend/Dockerfile
|
# dockerfile: WebServices/client-frontend/Dockerfile
|
||||||
networks:
|
# networks:
|
||||||
- wag-services
|
# - wag-services
|
||||||
ports:
|
# ports:
|
||||||
- "3000:3000"
|
# - "3000:3000"
|
||||||
environment:
|
# environment:
|
||||||
- NODE_ENV=development
|
# - NODE_ENV=development
|
||||||
cpus: 1
|
# cpus: 1
|
||||||
mem_limit: 2048m
|
# mem_limit: 2048m
|
||||||
|
|
||||||
# volumes:
|
# volumes:
|
||||||
# - client-frontend:/WebServices/client-frontend
|
# - client-frontend:/WebServices/client-frontend
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue