tested appender service to events

This commit is contained in:
Berkay 2025-05-04 21:24:29 +03:00
parent 0cde34a9bc
commit dd707b2463
30 changed files with 1167 additions and 277 deletions

View File

@ -49,21 +49,27 @@ class EventCluster:
endpoint_uu_id=str(to_save_endpoint.uu_id), endpoint_uu_id=str(to_save_endpoint.uu_id),
is_confirmed=True, is_confirmed=True,
db=db_session, db=db_session,
include_args=[
Events.function_code,
Events.function_class,
Events.endpoint_code,
Events.endpoint_uu_id,
]
) )
event_to_save_database = Events.find_or_create(**event_dict_to_save) event_found = Events.filter_one(
if event_to_save_database.meta_data.created: Events.function_code == event_dict_to_save["function_code"],
print(f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}") db=db_session,
).data
if event_found:
event_found.update(**event_dict_to_save)
event_found.save(db=db_session)
else: else:
event_to_save_database.update(**event_dict_to_save) event_to_save_database = Events.find_or_create(
if event_to_save_database.meta_data.updated: **event_dict_to_save,
print(f"UUID: {event_to_save_database.uu_id} event is updated to {to_save_endpoint.uu_id}") include_args=[
event_to_save_database.save(db=db_session) Events.function_code,
Events.function_class,
Events.endpoint_code,
Events.endpoint_uu_id,
]
)
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}")
event_to_save_database.save(db=db_session)
def match_event(self, event_key: str) -> "Event": def match_event(self, event_key: str) -> "Event":
""" """

View File

@ -23,17 +23,24 @@ class RouteRegisterController:
for route_method in [ for route_method in [
method.lower() for method in getattr(route, "methods") method.lower() for method in getattr(route, "methods")
]: ]:
restriction = EndpointRestriction.find_or_create( add_or_update_dict = dict(
endpoint_method=route_method, endpoint_method=route_method,
endpoint_name=route_path, endpoint_name=route_path,
endpoint_desc=route_summary.replace("_", " "), endpoint_desc=route_summary.replace("_", " "),
endpoint_function=route_summary, endpoint_function=route_summary,
operation_uu_id=operation_id, # UUID of the endpoint operation_uu_id=operation_id,
is_confirmed=True, is_confirmed=True,
db=db_session,
) )
if restriction.meta_data.created: endpoint_restriction_found = EndpointRestriction.filter_one_system(
restriction.save(db=db_session) EndpointRestriction.operation_uu_id == operation_id, db=db_session,
).data
if endpoint_restriction_found:
endpoint_restriction_found.update(**add_or_update_dict, db=db_session)
endpoint_restriction_found.save(db=db_session)
else:
restriction = EndpointRestriction.find_or_create(**add_or_update_dict, db=db_session)
if restriction.meta_data.created:
restriction.save(db=db_session)
def register_routes(self): def register_routes(self):
for router in self.router_list: for router in self.router_list:

View File

@ -5,18 +5,39 @@ 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
from Validations.service_endpoints.validations import Event2Employee, Event2Occupant from Validations.service_endpoints.validations import Event2Employee, Event2Occupant, AddRemoveService
from Events.event_endpoints.cluster import EventsEndpointRouterCluster
# Create API router # Create API router
event_endpoint_route = APIRouter(prefix="/events", tags=["Event Actions"]) event_endpoint_route = APIRouter(prefix="/events", tags=["Event Actions"])
@event_endpoint_route.post( @event_endpoint_route.post(
path="/list", path="/list/available",
description="List events endpoint", description="List available events endpoint",
operation_id="0659d5e4-671f-466c-a84f-47a1290a6f0d", operation_id="0659d5e4-671f-466c-a84f-47a1290a6f0d",
) )
def event_list_route( def event_list_available_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List available events with pagination and filtering options
"""
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 = EventsEndpointRouterCluster.get_event_cluster("EventsListAvailable")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(list_options=data)
@event_endpoint_route.post(
path="/list/appended",
description="List appended events endpoint",
operation_id="4d563973-cdcd-44e1-94e0-4262ffb456a1",
)
def event_list_appended_route(
data: PaginateOnly, data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency), headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
): ):
@ -26,9 +47,9 @@ def event_list_route(
token_object = TokenProvider.get_dict_from_redis(token=headers.token) token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventsList") FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventsListAppended")
event_cluster_matched = FoundCluster.match_event(event_key=event_key) event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data) return event_cluster_matched.event_callable(list_options=data)
@event_endpoint_route.post( @event_endpoint_route.post(
@ -37,7 +58,7 @@ def event_list_route(
operation_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76", operation_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76",
) )
def event_register_service_route( def event_register_service_route(
data: Any, data: AddRemoveService,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency), headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
): ):
""" """
@ -46,7 +67,27 @@ def event_register_service_route(
token_object = TokenProvider.get_dict_from_redis(token=headers.token) token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventRegisterService") FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventRegisterService")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@event_endpoint_route.post(
path="/unregister/service",
description="Unregister event from service endpoint",
operation_id="2f16dc9e-de02-449d-9c3f-1a21f87e8794",
)
def event_unregister_service_route(
data: AddRemoveService,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Unregister event from service
"""
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 = EventsEndpointRouterCluster.get_event_cluster("EventUnregisterService")
event_cluster_matched = FoundCluster.match_event(event_key=event_key) event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data) return event_cluster_matched.event_callable(data=data)
@ -66,7 +107,7 @@ def event_bind_employee_extra_route(
token_object = TokenProvider.get_dict_from_redis(token=headers.token) token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventBindEmployeeExtra") FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventBindEmployeeExtra")
event_cluster_matched = FoundCluster.match_event(event_key=event_key) event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data) return event_cluster_matched.event_callable(data=data)
@ -86,6 +127,6 @@ def event_bind_occupant_extra_route(
token_object = TokenProvider.get_dict_from_redis(token=headers.token) token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventBindOccupantExtra") FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventBindOccupantExtra")
event_cluster_matched = FoundCluster.match_event(event_key=event_key) event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data) return event_cluster_matched.event_callable(data=data)

View File

@ -1,23 +1,35 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import ( from .supers_events import (
EventsListEvent, EventsListAvailableEvent,
EventsListAppendedEvent,
EventRegisterServiceEvent, EventRegisterServiceEvent,
EventUnRegisterServiceEvent,
EventBindEmployeeExtraEvent, EventBindEmployeeExtraEvent,
EventBindOccupantExtraEvent, EventBindOccupantExtraEvent,
) )
EventsEndpointRouterCluster = RouterCluster(name="EventsEndpointRouterCluster") EventsEndpointRouterCluster = RouterCluster(name="EventsEndpointRouterCluster")
EventsEndpointEventClusterList = EventCluster( EventsEndpointEventClusterListAvailable = EventCluster(
name="EventsList", endpoint_uu_id="0659d5e4-671f-466c-a84f-47a1290a6f0d" name="EventsListAvailable", endpoint_uu_id="0659d5e4-671f-466c-a84f-47a1290a6f0d"
) )
EventsEndpointEventClusterList.add_event(EventsListEvent) EventsEndpointEventClusterListAvailable.add_event(EventsListAvailableEvent)
EventsEndpointEventClusterListAppended = EventCluster(
name="EventsListAppended", endpoint_uu_id="4d563973-cdcd-44e1-94e0-4262ffb456a1"
)
EventsEndpointEventClusterListAppended.add_event(EventsListAppendedEvent)
EventsEndpointEventClusterRegisterService = EventCluster( EventsEndpointEventClusterRegisterService = EventCluster(
name="EventRegisterService", endpoint_uu_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76" name="EventRegisterService", endpoint_uu_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76"
) )
EventsEndpointEventClusterRegisterService.add_event(EventRegisterServiceEvent) EventsEndpointEventClusterRegisterService.add_event(EventRegisterServiceEvent)
EventsEndpointEventClusterUnregisterService = EventCluster(
name="EventUnregisterService", endpoint_uu_id="2f16dc9e-de02-449d-9c3f-1a21f87e8794"
)
EventsEndpointEventClusterUnregisterService.add_event(EventUnRegisterServiceEvent)
EventsEndpointEventClusterBindEmployeeExtra = EventCluster( EventsEndpointEventClusterBindEmployeeExtra = EventCluster(
name="EventBindEmployeeExtra", endpoint_uu_id="58ef3640-04ec-43f9-8f3e-f86be3ce4a24" name="EventBindEmployeeExtra", endpoint_uu_id="58ef3640-04ec-43f9-8f3e-f86be3ce4a24"
) )
@ -28,8 +40,9 @@ EventsEndpointEventClusterBindOccupantExtra = EventCluster(
) )
EventsEndpointEventClusterBindOccupantExtra.add_event(EventBindOccupantExtraEvent) EventsEndpointEventClusterBindOccupantExtra.add_event(EventBindOccupantExtraEvent)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterList) EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterListAvailable)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterListAppended)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterRegisterService) EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterRegisterService)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterUnregisterService)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterBindEmployeeExtra) EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterBindEmployeeExtra)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterBindOccupantExtra) EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterBindOccupantExtra)

View File

@ -9,28 +9,45 @@ from Schemas import (
Event2EmployeeExtra, Event2EmployeeExtra,
Event2OccupantExtra, Event2OccupantExtra,
Service2Events, Service2Events,
Services,
) )
# List available events endpoint
# List endpoint EventsListAvailableEvent = Event(
EventsListEvent = Event( name="event_endpoint_list_available",
name="service_endpoint_list", key="d39af512-ec71-4c0f-9b35-e53b0d06d3a4",
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="Super Users List services endpoint", description="Super Users List available events endpoint",
) )
# List appended events endpoint
EventsListAppendedEvent = Event(
name="event_endpoint_list_appended",
key="bea77d6a-d99f-468b-9002-b3bda6bb6ad0",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List appended events endpoint",
)
# Event Register endpoint # Event Register endpoint
EventRegisterServiceEvent = Event( EventRegisterServiceEvent = Event(
name="service_endpoint_register_service", name="event_endpoint_register_service",
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="Super Users Register service endpoint", description="Super Users Register service endpoint",
) )
# Event Unregister endpoint
EventUnRegisterServiceEvent = Event(
name="service_endpoint_unregister_service",
key="4d693774-4857-435b-a63c-c39baebfe916",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Unregister service endpoint",
)
# Bind employee extra endpoint # Bind employee extra endpoint
EventBindEmployeeExtraEvent = Event( EventBindEmployeeExtraEvent = Event(
name="service_endpoint_bind_employee_extra", name="service_endpoint_bind_employee_extra",
@ -50,58 +67,196 @@ EventBindOccupantExtraEvent = Event(
) )
def events_list_callable(list_options: PaginateOnly): def events_list_available_callable(list_options: PaginateOnly):
""" """
Example callable method List available events with pagination and filtering options
""" """
list_options = PaginateOnly(**list_options.model_dump()) list_options = PaginateOnly(**list_options.model_dump())
with Services.new_session() as db_session: service_uu_id = list_options.query.get('service_uu_id__ilike', None)
if not service_uu_id:
return {
"message": "MSG0003-PARAM-MISSING",
"data": list_options.query,
"completed": False,
}
with Events.new_session() as db_session:
service2events = Service2Events.filter_all(*Service2Events.convert(list_options.query), db=db_session)
already_events = [service_to_event.event_id for service_to_event in service2events.data]
list_options.query.pop('service_uu_id__ilike', None)
list_options.query.pop('service_uu_id', None)
if list_options.query: if list_options.query:
services_list = Services.filter_all( events_list = Events.filter_all(*Events.convert(list_options.query), Events.id.not_in(already_events), db=db_session)
*Services.convert(list_options.query), db=db_session
)
else: else:
services_list = Services.filter_all(db=db_session) events_list = Events.filter_all(Events.id.not_in(already_events), db=db_session)
pagination = Pagination(data=services_list) pagination = Pagination(data=events_list)
pagination.change(**list_options.model_dump()) pagination.change(**list_options.model_dump())
pagination_result = PaginationResult( pagination_result = PaginationResult(data=events_list, pagination=pagination)
data=services_list,
pagination=pagination,
# response_model="",
).pagination.as_dict
return EndpointResponse( return EndpointResponse(
message="MSG0003-LIST", message="MSG0003-LIST",
pagination_result=pagination_result, pagination_result=pagination_result,
).response ).response
EventsListEvent.event_callable = events_list_callable
EventsListAvailableEvent.event_callable = events_list_available_callable
def events_list_appended_callable(list_options: PaginateOnly):
"""
List appended events with pagination and filtering options
"""
list_options = PaginateOnly(**list_options.model_dump())
service_uu_id = list_options.query.get('service_uu_id__ilike', None)
if not service_uu_id:
return {
"message": "MSG0003-PARAM-MISSING",
"data": list_options.query,
"completed": False,
}
with Events.new_session() as db_session:
service2events = Service2Events.filter_all(*Service2Events.convert(list_options.query), db=db_session)
already_events = [service_to_event.event_id for service_to_event in service2events.data]
list_options.query.pop('service_uu_id__ilike', None)
list_options.query.pop('service_uu_id', None)
if list_options.query:
events_list = Events.filter_all(*Events.convert(list_options.query), Events.id.in_(already_events), db=db_session)
else:
events_list = Events.filter_all(Events.id.in_(already_events), db=db_session)
pagination = Pagination(data=events_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(data=events_list, pagination=pagination)
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
EventsListAppendedEvent.event_callable = events_list_appended_callable
def event_register_service_callable(data: Any): def event_register_service_callable(data: Any):
""" """
Example callable method Register event to service
""" """
return EndpointResponse( with Events.new_session() as db_session:
message="MSG0003-REGISTER", event = Events.filter_one_system(
).response Events.uu_id == data.event_uu_id,
db=db_session
)
print('event', event.data)
if not event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service = Services.filter_one_system(
Services.uu_id == data.service_uu_id,
db=db_session
)
print('service', service.data)
if not service.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event = Service2Events.find_or_create(
db=db_session,
include_args=[
Service2Events.service_uu_id,
Service2Events.event_uu_id,
],
service_id=service.data.id,
event_id=event.data.id,
is_confirmed=True,
service_uu_id=str(service.data.uu_id),
event_uu_id=str(event.data.uu_id),
)
print('service_to_event', service_to_event)
if not service_to_event.meta_data.created:
return {
"message": "MSG0003-ALREADY-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event.save(db=db_session)
return {
"message": "MSG0003-REGISTER",
"data": data.model_dump(),
"completed": True,
}
EventRegisterServiceEvent.event_callable = event_register_service_callable EventRegisterServiceEvent.event_callable = event_register_service_callable
def event_unregister_service_callable(data: Any):
"""
Unregister event from service
"""
with Events.new_session() as db_session:
event = Events.filter_one_system(
Events.uu_id == data.event_uu_id, db=db_session
)
if not event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service = Services.filter_one_system(
Services.uu_id == data.service_uu_id, db=db_session
)
if not service.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event = Service2Events.filter_one_system(
Service2Events.service_id==service.data.id,
Service2Events.event_id==event.data.id,
db=db_session,
)
if not service_to_event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event.query.delete()
db_session.commit()
return {
"message": "MSG0003-UNREGISTER",
"data": data.model_dump(),
"completed": True,
}
EventUnRegisterServiceEvent.event_callable = event_unregister_service_callable
def event_bind_employee_extra_callable(data: Any): def event_bind_employee_extra_callable(data: Any):
""" """
Example callable method Bind event to employee extra
""" """
return EndpointResponse( return {
message="MSG0003-BIND", "message": "MSG0003-BIND",
).response "data": data.model_dump(),
"completed": True,
}
EventBindEmployeeExtraEvent.event_callable = event_bind_employee_extra_callable EventBindEmployeeExtraEvent.event_callable = event_bind_employee_extra_callable
def event_bind_occupant_extra_callable(data: Any): def event_bind_occupant_extra_callable(data: Any):
""" """
Example callable method Bind event to occupant extra
""" """
return EndpointResponse( return EndpointResponse(
message="MSG0003-BIND", message="MSG0003-BIND",
).response ).response
EventBindOccupantExtraEvent.event_callable = event_bind_occupant_extra_callable EventBindOccupantExtraEvent.event_callable = event_bind_occupant_extra_callable

View File

@ -16,3 +16,4 @@ ServiceEndpointEventClusterToService = EventCluster(
ServiceEndpointEventClusterToService.add_event(ServiceEndpointToEventsEvent) ServiceEndpointEventClusterToService.add_event(ServiceEndpointToEventsEvent)
ServiceEndpointRouterCluster.set_event_cluster(ServiceEndpointEventClusterList) ServiceEndpointRouterCluster.set_event_cluster(ServiceEndpointEventClusterList)
ServiceEndpointRouterCluster.set_event_cluster(ServiceEndpointEventClusterToService)

View File

@ -1,8 +1,13 @@
from pydantic import BaseModel
class Event2Employee(BaseModel):
class Event2Employee:
pass pass
class Event2Occupant: class Event2Occupant(BaseModel):
pass pass
class AddRemoveService(BaseModel):
event_uu_id: str
service_uu_id: str

View File

@ -270,6 +270,7 @@ class CRUDModel:
except Exception as e: except Exception as e:
db.rollback() db.rollback()
print('e', e)
cls.raise_exception( cls.raise_exception(
f"Failed to find or create record: {str(e)}", status_code=500 f"Failed to find or create record: {str(e)}", status_code=500
) )

View File

@ -78,18 +78,13 @@ async function coreFetch<T>(
signal, signal,
}; };
// Add body if needed
if (method !== "GET" && payload) { if (method !== "GET" && payload) {
fetchOptions.body = JSON.stringify( fetchOptions.body = JSON.stringify(
// Handle special case for updateDataWithToken
payload.payload ? payload.payload : payload payload.payload ? payload.payload : payload
); );
} }
// Create timeout promise
const timeoutPromise = createTimeoutPromise(timeout, controller); const timeoutPromise = createTimeoutPromise(timeout, controller);
// Race between fetch and timeout
const response = (await Promise.race([ const response = (await Promise.race([
fetch(url, fetchOptions), fetch(url, fetchOptions),
timeoutPromise, timeoutPromise,
@ -99,7 +94,7 @@ async function coreFetch<T>(
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== "production") {
console.log("Fetching:", url, fetchOptions); console.log("Fetching:", url, fetchOptions);
console.log("Response:", responseJson); // console.log("Response:", responseJson);
} }
return prepareResponse(responseJson, response.status); return prepareResponse(responseJson, response.status);

View File

@ -26,7 +26,6 @@ async function listApplications(payload: PaginationParams): Promise<PaginatedApi
false false
); );
// Format the response to match the expected PaginatedApiResponse format
if (response?.status === 200 || response?.status === 202) { if (response?.status === 200 || response?.status === 202) {
const responseData = response.data as any; const responseData = response.data as any;
return { return {
@ -64,7 +63,6 @@ async function listApplications(payload: PaginationParams): Promise<PaginatedApi
}; };
} catch (error) { } catch (error) {
console.error("Error fetching application list:", error); console.error("Error fetching application list:", error);
// Return a default empty response instead of null to match the expected return type
return { return {
data: [], data: [],
pagination: { pagination: {

View File

@ -3,82 +3,151 @@
import { fetchDataWithToken } from "../api-fetcher"; import { fetchDataWithToken } from "../api-fetcher";
import { baseUrlApplication } from "../basics"; import { baseUrlApplication } from "../basics";
import { PaginationParams } from "../schemas/list"; import { PaginationParams } from "../schemas/list";
import type { PaginatedApiResponse } from "@/app/api/utils/types"; import { PaginatedApiResponse, collectPaginationFromApiResponse, defaultPaginationResponse } from "@/app/api/utils/types";
const eventsListEndpoint = `${baseUrlApplication}/events/list`; const eventsListAvailableEndpoint = `${baseUrlApplication}/events/list/available`;
const eventsListAppendedEndpoint = `${baseUrlApplication}/events/list/appended`;
const appendEventToServiceEndpoint = `${baseUrlApplication}/events/register/service`;
const removeEventFromServiceEndpoint = `${baseUrlApplication}/events/unregister/service`;
interface AppendEventToService {
service_uu_id: string;
event_uu_id: string;
}
interface RemoveEventFromService extends AppendEventToService { }
async function list_events_available(payload: PaginationParams): Promise<PaginatedApiResponse<any>> {
if (!payload.query.service_uu_id__ilike) {
console.warn('Missing service_uu_id in query parameters');
return {
data: [],
pagination: defaultPaginationResponse,
};
}
async function listEvents(payload: PaginationParams): Promise<PaginatedApiResponse<any>> {
try { try {
const requestBody = {
page: payload.page,
size: payload.size,
order_field: payload.orderField,
order_type: payload.orderType,
query: payload.query,
};
console.log('Sending request to backend with service_uu_id:', payload.query.service_uu_id);
console.log('Full request body:', JSON.stringify(requestBody, null, 2));
const response = await fetchDataWithToken( const response = await fetchDataWithToken(
eventsListEndpoint, eventsListAvailableEndpoint,
{ requestBody,
page: payload.page,
size: payload.size,
order_field: payload.orderField,
order_type: payload.orderType,
query: payload.query,
},
"POST", "POST",
false false
); );
if (response?.status === 200 || response?.status === 202) { if (response?.status === 200 || response?.status === 202) {
const responseData = response.data as PaginatedApiResponse<any>; const responseData = response.data as PaginatedApiResponse<any>;
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
return { return {
data: responseData.data || [], data: responseData.data || [],
pagination: { pagination: collectPaginationFromApiResponse(responseData)
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 { return {
data: [], data: [],
pagination: { pagination: defaultPaginationResponse,
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 events list:", error); console.error("Error fetching events list:", error);
// Return a default empty response instead of null to match the expected return type
return { return {
data: [], data: [],
pagination: { pagination: defaultPaginationResponse,
page: 1,
size: 10,
totalCount: 0,
totalItems: 0,
totalPages: 0,
pageCount: 0,
orderField: ['name'],
orderType: ['asc'],
query: {},
next: false,
back: false
}
}; };
} }
} }
async function list_events_appended(payload: PaginationParams): Promise<PaginatedApiResponse<any>> {
if (!payload.query.service_uu_id__ilike) {
console.warn('Missing service_uu_id in query parameters for list_events_appended');
return {
data: [],
pagination: defaultPaginationResponse,
};
}
try {
const requestBody = {
page: payload.page,
size: payload.size,
order_field: payload.orderField,
order_type: payload.orderType,
query: payload.query,
};
console.log('Sending request to backend with service_uu_id:', payload.query.service_uu_id);
console.log('Full request body:', JSON.stringify(requestBody, null, 2));
const response = await fetchDataWithToken(
eventsListAppendedEndpoint,
requestBody,
"POST",
false
);
if (response?.status === 200 || response?.status === 202) {
const responseData = response.data as PaginatedApiResponse<any>;
console.log('list_events_appended responseData:', JSON.stringify(responseData, null, 2));
return {
data: responseData.data || [],
pagination: collectPaginationFromApiResponse(responseData)
};
}
return {
data: [],
pagination: defaultPaginationResponse,
};
} catch (error) {
console.error("Error fetching events list:", error);
return {
data: [],
pagination: defaultPaginationResponse,
};
}
}
async function appendEventToService(payload: AppendEventToService) {
try {
const response = await fetchDataWithToken(
appendEventToServiceEndpoint,
payload,
"POST",
false
);
return response?.status === 200 || response?.status === 202 ? response.data : null;
} catch (error) {
console.error("Error appending event to service:", error);
}
}
async function removeEventFromService(payload: RemoveEventFromService) {
try {
const response = await fetchDataWithToken(
removeEventFromServiceEndpoint,
payload,
"POST",
false
);
return response?.status === 200 || response?.status === 202 ? response.data : null;
} catch (error) {
console.error("Error removing event from service:", error);
}
}
export { export {
listEvents, list_events_available,
list_events_appended,
appendEventToService,
removeEventFromService,
}; };

View File

@ -0,0 +1,22 @@
import { appendEventToService } from "@/apicalls/events/endpoints";
import { NextRequest } from "next/server";
import { withErrorHandling } from "@/app/api/utils/requestHandlers";
import {
successResponse,
errorResponse,
} from "@/app/api/utils/responseHandlers";
export const POST = withErrorHandling(
async (request: NextRequest, body: any) => {
const payload = {
event_uu_id: body.event_uu_id,
service_uu_id: body.service_uu_id,
};
const result = await appendEventToService(payload);
if (!result) {
return errorResponse("Error appending event to service");
} else {
return successResponse(result);
}
}
);

View File

@ -0,0 +1,22 @@
import { removeEventFromService } from "@/apicalls/events/endpoints";
import { NextRequest } from "next/server";
import { withErrorHandling } from "@/app/api/utils/requestHandlers";
import {
successResponse,
errorResponse,
} from "@/app/api/utils/responseHandlers";
export const POST = withErrorHandling(
async (request: NextRequest, body: any) => {
const payload = {
event_uu_id: body.event_uu_id,
service_uu_id: body.service_uu_id,
};
const result = await removeEventFromService(payload);
if (!result) {
return errorResponse("Error removing event from service");
} else {
return successResponse(result);
}
}
);

View File

@ -1,4 +1,4 @@
import { listEventsToService } from "@/apicalls/services/endpoints"; import { list_events_appended } from "@/apicalls/events/endpoints";
import { createListHandler } from "@/app/api/utils"; import { createListHandler } from "@/app/api/utils/apiOperations";
export const POST = createListHandler(listEventsToService); export const POST = createListHandler(list_events_appended);

View File

@ -1,4 +1,4 @@
import { listEvents } from "@/apicalls/events/endpoints"; import { list_events_available } from "@/apicalls/events/endpoints";
import { createListHandler } from "@/app/api/utils"; import { createListHandler } from "@/app/api/utils/apiOperations";
export const POST = createListHandler(listEvents); export const POST = createListHandler(list_events_available);

View File

@ -28,18 +28,11 @@ export async function handleListOperation(
body: any, body: any,
listFunction: ListFunction listFunction: ListFunction
) { ) {
// Extract pagination parameters with defaults
const page = body.page || 1; const page = body.page || 1;
const size = body.size || 10; const size = body.size || 10;
const orderField = body.orderField || ["uu_id"];
// Extract sorting parameters with defaults
const orderField = body.orderField || ["name"];
const orderType = body.orderType || ["asc"]; const orderType = body.orderType || ["asc"];
// Extract query filters
const query = body.query || {}; const query = body.query || {};
// Call the actual API function for listing
const response = await listFunction({ const response = await listFunction({
page, page,
size, size,
@ -47,8 +40,6 @@ export async function handleListOperation(
orderType, orderType,
query, query,
} as PaginationParams); } as PaginationParams);
// Return the list response
return paginationResponse(response.data, response.pagination); return paginationResponse(response.data, response.pagination);
} }
@ -64,7 +55,6 @@ export async function handleCreateOperation(
createFunction?: CreateFunction, createFunction?: CreateFunction,
requiredFields: string[] = [] requiredFields: string[] = []
) { ) {
// Validate required fields if specified
if (requiredFields.length > 0) { if (requiredFields.length > 0) {
const validation = validateRequiredFields(body, requiredFields); const validation = validateRequiredFields(body, requiredFields);
if (!validation.valid) { if (!validation.valid) {
@ -72,13 +62,11 @@ export async function handleCreateOperation(
} }
} }
// If a create function is provided, call it
if (createFunction) { if (createFunction) {
const result = await createFunction(body); const result = await createFunction(body);
return createResponse(result); return createResponse(result);
} }
// Otherwise return a mock response
return createResponse({ return createResponse({
id: Math.floor(Math.random() * 1000), id: Math.floor(Math.random() * 1000),
...body, ...body,
@ -96,26 +84,15 @@ export async function handleUpdateOperation(
body: any, body: any,
updateFunction?: UpdateFunction updateFunction?: UpdateFunction
) { ) {
// Skip validation as it's handled at the page level with Yup
// and IDs are extracted from URL paths for update/delete operations
// Get UUID from query string. This is the value of the "uuid" query
// string parameter, e.g. if the URL is "?uuid=SOMEUUID", this will
// be "SOMEUUID".
const uuid = request.nextUrl.searchParams.get("uuid"); const uuid = request.nextUrl.searchParams.get("uuid");
console.log("UUID:", uuid);
if (!uuid) { if (!uuid) {
return errorResponse("UUID not found", 400); return errorResponse("UUID not found", 400);
} }
if (updateFunction) { if (updateFunction) {
console.log("Body:", body); console.log("Body:", body);
const result = await updateFunction(body, uuid); const result = await updateFunction(body, uuid);
return updateResponse(result); return updateResponse(result);
} }
// Otherwise return a mock response
return updateResponse(body); return updateResponse(body);
} }
@ -128,14 +105,7 @@ export async function handleDeleteOperation(
request: NextRequest, request: NextRequest,
deleteFunction?: DeleteFunction deleteFunction?: DeleteFunction
) { ) {
// Skip ID validation as it's handled at the page level
// and IDs are typically extracted from URL paths
// Get UUID from query string. This is the value of the "uuid" query
// string parameter, e.g. if the URL is "?uuid=SOMEUUID", this will
// be "SOMEUUID".
const uuid = request.nextUrl.searchParams.get("uuid"); const uuid = request.nextUrl.searchParams.get("uuid");
if (!uuid) { if (!uuid) {
return errorResponse("UUID not found", 400); return errorResponse("UUID not found", 400);
} }
@ -143,8 +113,6 @@ export async function handleDeleteOperation(
if (deleteFunction) { if (deleteFunction) {
await deleteFunction(uuid); await deleteFunction(uuid);
} }
// Return a success response
return deleteResponse(); return deleteResponse();
} }

View File

@ -40,6 +40,20 @@ export interface PaginationResponse {
back: boolean; back: boolean;
} }
export const defaultPaginationResponse: PaginationResponse = {
page: 1,
size: 10,
totalCount: 0,
totalItems: 0,
totalPages: 0,
pageCount: 0,
orderField: ["uu_id"],
orderType: ["asc"],
query: {},
next: false,
back: false,
};
/** /**
* API response interface * API response interface
*/ */
@ -57,6 +71,24 @@ export interface PaginatedApiResponse<T> {
pagination: PaginationResponse; pagination: PaginationResponse;
} }
export const collectPaginationFromApiResponse = (
response: PaginatedApiResponse<any>
): PaginationResponse => {
return {
page: response.pagination?.page || 1,
size: response.pagination?.size || 10,
totalCount: response.pagination?.totalCount || 0,
totalItems: response.pagination?.totalItems || 0,
totalPages: response.pagination?.totalPages || 0,
pageCount: response.pagination?.pageCount || 0,
orderField: response.pagination?.orderField || ["uu_id"],
orderType: response.pagination?.orderType || ["asc"],
query: response.pagination?.query || {},
next: response.pagination?.next || false,
back: response.pagination?.back || false,
};
};
/** /**
* API handler function type * API handler function type
*/ */
@ -65,7 +97,9 @@ export type ApiHandler = (request: NextRequest, body: any) => Promise<Response>;
/** /**
* List function type * List function type
*/ */
export type ListFunction = (params: PaginationParams) => Promise<PaginatedApiResponse<any>>; export type ListFunction = (
params: PaginationParams
) => Promise<PaginatedApiResponse<any>>;
/** /**
* Create function type * Create function type

View File

@ -21,6 +21,7 @@ export function CardDisplay<T>({
showUpdateIcon = false, showUpdateIcon = false,
onViewClick, onViewClick,
onUpdateClick, onUpdateClick,
size = "lg",
}: CardDisplayProps<T>) { }: CardDisplayProps<T>) {
if (error) { if (error) {
return ( return (
@ -44,7 +45,7 @@ export function CardDisplay<T>({
/> />
)) ))
) : data.length === 0 ? ( ) : data.length === 0 ? (
<div className="col-span-full text-center py-6"> <div className="col-span-full text-center py-1">
{translations[lang].noData || "No data found"} {translations[lang].noData || "No data found"}
</div> </div>
) : ( ) : (
@ -64,6 +65,7 @@ export function CardDisplay<T>({
onViewClick={onViewClick} onViewClick={onViewClick}
onUpdateClick={onUpdateClick} onUpdateClick={onUpdateClick}
getFieldValue={getFieldValue} getFieldValue={getFieldValue}
size={size}
/> />
)) ))
)} )}

View File

@ -1,10 +1,6 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { import { Card, CardContent, CardHeader } from "@/components/ui/card";
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Eye, Edit } from "lucide-react"; import { Eye, Edit } from "lucide-react";
import { CardItemProps, CardActionsProps, CardFieldProps } from "./schema"; import { CardItemProps, CardActionsProps, CardFieldProps } from "./schema";
@ -23,17 +19,108 @@ export function CardItem<T>({
onViewClick, onViewClick,
onUpdateClick, onUpdateClick,
getFieldValue, getFieldValue,
size = "lg",
}: CardItemProps<T>) { }: CardItemProps<T>) {
return ( const getCardHeight = () => {
switch (size) {
case "xs": return "h-16 max-h-16";
case "sm": return "h-20 max-h-20";
case "md": return "h-24 max-h-24";
case "lg":
default: return "h-full";
}
};
const getCardStyle = () => {
switch (size) {
case "xs": return "!py-0 !gap-0 !flex !flex-col";
case "sm": return "!py-1 !gap-1 !flex !flex-col";
case "md": return "!py-2 !gap-2 !flex !flex-col";
case "lg":
default: return "";
}
};
const getTitleSize = () => {
switch (size) {
case "xs": return "text-xs";
case "sm": return "text-sm";
case "md": return "text-base";
case "lg":
default: return "text-lg";
}
};
const getContentPadding = () => {
switch (size) {
case "xs": return "p-1 py-1";
case "sm": return "p-1 py-1";
case "md": return "p-2 py-1";
case "lg":
default: return "p-3";
}
};
if (size === "xs" || size === "sm") {
return (
<div key={index} className="w-full p-1">
<div
className={`border rounded-lg shadow-sm ${getCardHeight()} ${onCardClick ? 'cursor-pointer hover:shadow-md transition-shadow' : ''} overflow-hidden flex flex-col bg-card text-card-foreground relative`}
onClick={onCardClick ? () => onCardClick(item) : undefined}
>
{showViewIcon && (
<button
className="absolute top-1 right-1 h-5 w-5 inline-flex items-center justify-center rounded-full bg-white/80 hover:bg-white z-10"
onClick={(e) => {
e.stopPropagation();
if (onViewClick) onViewClick(item);
}}
>
<Eye className="h-3 w-3" />
</button>
)}
{showUpdateIcon && (
<button
className="absolute top-1 right-7 h-5 w-5 inline-flex items-center justify-center rounded-full bg-white/80 hover:bg-white z-10"
onClick={(e) => {
e.stopPropagation();
if (onUpdateClick) onUpdateClick(item);
}}
>
<Edit className="h-3 w-3" />
</button>
)}
<div className="px-2 pt-1 pb-1">
<h3 className={`${getTitleSize()} font-semibold truncate pr-6`}>{getFieldValue(item, titleField)}</h3>
<div className="flex flex-col justify-start mt-1">
{showFields.map((field) => (
<CardField
key={`${index}-${field}`}
item={item}
field={field}
lang={lang}
translations={translations}
renderCustomField={renderCustomField}
getFieldValue={getFieldValue}
size={size}
/>
))}
</div>
</div>
</div>
</div>
);
}
return (
<div key={index} className="w-full p-1"> <div key={index} className="w-full p-1">
<Card <Card
className={`h-full ${onCardClick ? 'cursor-pointer hover:shadow-md transition-shadow' : ''}`} className={`${getCardHeight()} ${onCardClick ? 'cursor-pointer hover:shadow-md transition-shadow' : ''} overflow-hidden flex flex-col`}
onClick={onCardClick ? () => onCardClick(item) : undefined} onClick={onCardClick ? () => onCardClick(item) : undefined}
> >
<CardHeader className="p-3 pb-0 flex justify-between items-start"> <CardHeader className={`${getContentPadding()} pb-0 flex justify-between items-start`}>
<h3 className="text-lg font-semibold">{getFieldValue(item, titleField)}</h3> <h3 className={`${getTitleSize()} font-semibold`}>{getFieldValue(item, titleField)}</h3>
<CardActions <CardActions
item={item} item={item}
showViewIcon={showViewIcon} showViewIcon={showViewIcon}
@ -42,7 +129,7 @@ export function CardItem<T>({
onUpdateClick={onUpdateClick} onUpdateClick={onUpdateClick}
/> />
</CardHeader> </CardHeader>
<CardContent className="p-3"> <CardContent className={`${getContentPadding()} flex-1 overflow-hidden`}>
<div className="space-y-2"> <div className="space-y-2">
{showFields.map((field) => ( {showFields.map((field) => (
<CardField <CardField
@ -53,6 +140,7 @@ export function CardItem<T>({
translations={translations} translations={translations}
renderCustomField={renderCustomField} renderCustomField={renderCustomField}
getFieldValue={getFieldValue} getFieldValue={getFieldValue}
size={size}
/> />
))} ))}
</div> </div>
@ -62,8 +150,6 @@ export function CardItem<T>({
); );
} }
// Interface moved to schema.ts
function CardActions<T>({ function CardActions<T>({
item, item,
showViewIcon, showViewIcon,
@ -105,8 +191,6 @@ function CardActions<T>({
); );
} }
// Interface moved to schema.ts
function CardField<T>({ function CardField<T>({
item, item,
field, field,
@ -114,17 +198,48 @@ function CardField<T>({
translations, translations,
renderCustomField, renderCustomField,
getFieldValue, getFieldValue,
size = "lg",
}: CardFieldProps<T>) { }: CardFieldProps<T>) {
const getTextSize = () => {
switch (size) {
case "xs": return "text-xs";
case "sm": return "text-xs";
case "md": return "text-sm";
case "lg":
default: return "text-base";
}
};
const getLabelWidth = () => {
switch (size) {
case "xs": return "w-16";
case "sm": return "w-20";
case "md": return "w-24";
case "lg":
default: return "w-32";
}
};
if (renderCustomField) {
return renderCustomField(item, field);
}
const label = translations?.[field]?.[lang] || field;
const value = getFieldValue(item, field);
if (size === "xs" || size === "sm") {
return (
<div className={`flex items-center ${getTextSize()} py-0 my-0 leading-tight`}>
<span className={`${getLabelWidth()} font-medium truncate mr-1`}>{label}:</span>
<span className="truncate max-w-[60%]">{value}</span>
</div>
);
}
return ( return (
<div className="flex"> <div className={`flex items-center ${getTextSize()}`}>
<span className="font-medium mr-2 min-w-[80px]"> <span className={`${getLabelWidth()} font-medium truncate`}>{label}:</span>
{translations[field]?.[lang] || field}: <span className="truncate">{value}</span>
</span>
<span className="flex-1">
{renderCustomField
? renderCustomField(item, field)
: getFieldValue(item, field)}
</span>
</div> </div>
); );
} }

View File

@ -1,5 +1,7 @@
import { GridSize } from "../HeaderSelections/GridSelectionComponent"; import { GridSize } from "../HeaderSelections/GridSelectionComponent";
export type CardSize = "xs" | "sm" | "md" | "lg";
export interface CardDisplayProps<T> { export interface CardDisplayProps<T> {
showFields: string[]; showFields: string[];
data: T[]; data: T[];
@ -15,6 +17,7 @@ export interface CardDisplayProps<T> {
showUpdateIcon?: boolean; showUpdateIcon?: boolean;
onViewClick?: (item: T) => void; onViewClick?: (item: T) => void;
onUpdateClick?: (item: T) => void; onUpdateClick?: (item: T) => void;
size?: CardSize;
} }
export interface CardItemProps<T> { export interface CardItemProps<T> {
@ -31,6 +34,7 @@ export interface CardItemProps<T> {
onViewClick?: (item: T) => void; onViewClick?: (item: T) => void;
onUpdateClick?: (item: T) => void; onUpdateClick?: (item: T) => void;
getFieldValue: (item: any, field: string) => any; getFieldValue: (item: any, field: string) => any;
size?: CardSize;
} }
export interface CardActionsProps<T> { export interface CardActionsProps<T> {
@ -47,6 +51,7 @@ export interface CardFieldProps<T> {
translations: Record<string, any>; translations: Record<string, any>;
renderCustomField?: (item: T, field: string) => React.ReactNode; renderCustomField?: (item: T, field: string) => React.ReactNode;
getFieldValue: (item: any, field: string) => any; getFieldValue: (item: any, field: string) => any;
size?: CardSize;
} }
export interface CardSkeletonProps { export interface CardSkeletonProps {

View File

@ -40,7 +40,7 @@ export interface ViewComponentProps<T> extends BaseFormProps<T> {
export interface FormDisplayProps<T> { export interface FormDisplayProps<T> {
mode: FormMode | FormModeView; mode: FormMode | FormModeView;
initialData?: T; initialData?: T;
refetch?: () => void; refetch?: (additionalParams?: Record<string, any>) => void;
setMode: React.Dispatch<React.SetStateAction<FormModeView | FormMode>>; setMode: React.Dispatch<React.SetStateAction<FormModeView | FormMode>>;
setSelectedItem: React.Dispatch<React.SetStateAction<T | null>>; setSelectedItem: React.Dispatch<React.SetStateAction<T | null>>;
onCancel: () => void; onCancel: () => void;

View File

@ -40,7 +40,6 @@ export function useApiData<T>(
} catch (error) { } catch (error) {
console.error("Error fetching data from API:", error); console.error("Error fetching data from API:", error);
// Return empty data with pagination info on error
return { return {
data: [], data: [],
pagination: { pagination: {
@ -60,6 +59,5 @@ export function useApiData<T>(
} }
}; };
// Use the generic data fetching hook with our API-specific fetch function
return useDataFetching<T>(fetchFromApi, initialParams); return useDataFetching<T>(fetchFromApi, initialParams);
} }

View File

@ -24,7 +24,7 @@ export function useDataFetching<T>(
const [requestParams, setRequestParams] = useState<RequestParams>({ const [requestParams, setRequestParams] = useState<RequestParams>({
page: initialParams.page || 1, page: initialParams.page || 1,
size: initialParams.size || 10, size: initialParams.size || 10,
orderField: initialParams.orderField || ["name"], orderField: initialParams.orderField || ["uu_id"],
orderType: initialParams.orderType || ["asc"], orderType: initialParams.orderType || ["asc"],
query: initialParams.query || {}, query: initialParams.query || {},
}); });
@ -85,14 +85,10 @@ export function useDataFetching<T>(
requestParams.query, requestParams.query,
]); ]);
// Track if this is the initial mount
const initialMountRef = useRef(true); const initialMountRef = useRef(true);
// Track previous request params to avoid unnecessary fetches
const prevRequestParamsRef = useRef<RequestParams>(requestParams); const prevRequestParamsRef = useRef<RequestParams>(requestParams);
useEffect(() => { useEffect(() => {
// Only fetch on mount or when request params actually change
const paramsChanged = const paramsChanged =
JSON.stringify(prevRequestParamsRef.current) !== JSON.stringify(prevRequestParamsRef.current) !==
JSON.stringify(requestParams); JSON.stringify(requestParams);
@ -102,19 +98,17 @@ export function useDataFetching<T>(
fetchDataFromApi(); fetchDataFromApi();
initialMountRef.current = false; initialMountRef.current = false;
prevRequestParamsRef.current = { ...requestParams }; prevRequestParamsRef.current = { ...requestParams };
}, 300); // Debounce }, 300);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
}, [fetchDataFromApi, requestParams]); }, [fetchDataFromApi, requestParams]);
const updatePagination = useCallback((updates: Partial<RequestParams>) => { const updatePagination = useCallback((updates: Partial<RequestParams>) => {
// Transform query parameters to use __ilike with %value% format
if (updates.query) { if (updates.query) {
const transformedQuery: Record<string, any> = {}; const transformedQuery: Record<string, any> = {};
Object.entries(updates.query).forEach(([key, value]) => { Object.entries(updates.query).forEach(([key, value]) => {
// Only transform string values that aren't already using a special operator
if ( if (
typeof value === "string" && typeof value === "string" &&
!key.includes("__") && !key.includes("__") &&
@ -127,13 +121,9 @@ export function useDataFetching<T>(
}); });
updates.query = transformedQuery; updates.query = transformedQuery;
// Always reset to page 1 when search query changes
if (!updates.hasOwnProperty("page")) { if (!updates.hasOwnProperty("page")) {
updates.page = 1; updates.page = 1;
} }
// Reset response metadata when search changes to avoid stale pagination data
setResponseMetadata({ setResponseMetadata({
totalCount: 0, totalCount: 0,
totalItems: 0, totalItems: 0,
@ -153,7 +143,6 @@ export function useDataFetching<T>(
// Create a combined refetch function // Create a combined refetch function
const refetch = useCallback(() => { const refetch = useCallback(() => {
// Reset pagination to page 1 when manually refetching
setRequestParams((prev) => ({ setRequestParams((prev) => ({
...prev, ...prev,
page: 1, page: 1,

View File

@ -6,9 +6,9 @@ import { Card, CardContent } from '@/components/ui/card';
import { TextQueryModifier } from '@/components/common/QueryModifiers/TextQueryModifier'; import { TextQueryModifier } from '@/components/common/QueryModifiers/TextQueryModifier';
import { PaginationToolsComponent } from '@/components/common/PaginationModifiers/PaginationToolsComponent'; import { PaginationToolsComponent } from '@/components/common/PaginationModifiers/PaginationToolsComponent';
import { CardDisplay } from '@/components/common/CardDisplay/CardDisplay'; import { CardDisplay } from '@/components/common/CardDisplay/CardDisplay';
import { ListComponentApplicationProps, ListComponentServiceProps } from './type'; import { ListComponentEventsProps, ListComponentServicesProps } from './type';
const ListComponentServices: React.FC<ListComponentServiceProps> = ({ const ListComponentServices: React.FC<ListComponentServicesProps> = ({
lang, lang,
loading, loading,
error, error,
@ -17,6 +17,7 @@ const ListComponentServices: React.FC<ListComponentServiceProps> = ({
showFields, showFields,
gridCols, gridCols,
titleField, titleField,
size,
handleQueryChange, handleQueryChange,
updatePagination, updatePagination,
handleCardClick, handleCardClick,
@ -75,6 +76,7 @@ const ListComponentServices: React.FC<ListComponentServiceProps> = ({
titleField={titleField} titleField={titleField}
onCardClick={handleCardClick} onCardClick={handleCardClick}
gridCols={gridCols} gridCols={gridCols}
size={size}
showViewIcon={true} showViewIcon={true}
onViewClick={handleViewClick} onViewClick={handleViewClick}
/> />
@ -83,7 +85,7 @@ const ListComponentServices: React.FC<ListComponentServiceProps> = ({
); );
}; };
const ListComponentEvents: React.FC<ListComponentApplicationProps> = ({ const ListComponentEvents: React.FC<ListComponentEventsProps> = ({
lang, lang,
loading, loading,
error, error,
@ -91,6 +93,7 @@ const ListComponentEvents: React.FC<ListComponentApplicationProps> = ({
pagination, pagination,
showFields, showFields,
gridCols, gridCols,
size,
handleQueryChange, handleQueryChange,
updatePagination, updatePagination,
handleCardClick, handleCardClick,
@ -151,6 +154,57 @@ const ListComponentEvents: React.FC<ListComponentApplicationProps> = ({
gridCols={gridCols} gridCols={gridCols}
showViewIcon={true} showViewIcon={true}
onViewClick={handleViewClick} onViewClick={handleViewClick}
size={size}
/>
</div>
</>
);
};
const ListComponentAppenders: React.FC<ListComponentEventsProps> = ({
lang,
loading,
error,
data,
pagination,
showFields,
gridCols,
size,
handleQueryChange,
updatePagination,
handleCardClick,
handleViewClick,
}) => {
return (
<>
{/* Pagination Tools Component */}
<Card className="my-4">
<CardContent className="pt-6">
<PaginationToolsComponent
pagination={pagination}
updatePagination={updatePagination}
loading={loading}
lang={lang}
translations={translations}
/>
</CardContent>
</Card>
{/* Card Display Component */}
<div className="mt-6">
<CardDisplay
showFields={showFields}
data={data}
lang={lang}
translations={translations}
error={error}
loading={loading}
titleField="name"
onCardClick={handleCardClick}
gridCols={gridCols}
showViewIcon={true}
onViewClick={handleViewClick}
size={size}
/> />
</div> </div>
</> </>
@ -160,4 +214,5 @@ const ListComponentEvents: React.FC<ListComponentApplicationProps> = ({
export { export {
ListComponentEvents, ListComponentEvents,
ListComponentServices, ListComponentServices,
ListComponentAppenders,
}; };

View File

@ -7,9 +7,15 @@ import {
serviceViewFieldDefinitions, serviceViewFieldDefinitions,
ServiceFieldDefinitionsType, ServiceFieldDefinitionsType,
ServiceSchema, ServiceSchema,
serviceFieldsByMode,
} from "./schemaList/services"; } from "./schemaList/services";
import * as schema from "./schemaList/schema"; import {
EventData,
eventFlatFieldDefinitions,
EventFieldDefinitionsType,
EventSchema,
eventFieldsByMode
} from "./schemaList/events";
import { ListComponentEvents, ListComponentServices } from "./listComponent"; import { ListComponentEvents, ListComponentServices } from "./listComponent";
import { translations, translationsServices, translationsAppenders, translationsEvents } from "./language"; import { translations, translationsServices, translationsAppenders, translationsEvents } from "./language";
@ -21,6 +27,7 @@ import { useApiData } from "@/components/common";
import { Language } from "@/components/common/schemas"; import { Language } from "@/components/common/schemas";
import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { CardSize } from "@/components/common/CardDisplay/schema";
const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language }) => { const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language }) => {
@ -39,7 +46,7 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
error: errorEvents, error: errorEvents,
updatePagination: updatePaginationEvents, updatePagination: updatePaginationEvents,
refetch: refetchEvents refetch: refetchEvents
} = useApiData<schema.ApplicationData>('/api/events'); } = useApiData<EventData>('/api/events');
const { const {
data: dataAppenders, data: dataAppenders,
pagination: paginationAppenders, pagination: paginationAppenders,
@ -47,18 +54,18 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
error: errorAppenders, error: errorAppenders,
updatePagination: updatePaginationAppenders, updatePagination: updatePaginationAppenders,
refetch: refetchAppenders refetch: refetchAppenders
} = useApiData<schema.ApplicationData>('/api/appenders'); } = useApiData<EventData>('/api/appenders');
const [mode, setMode] = useState<FormModeView | FormMode>("list"); const [mode, setMode] = useState<FormModeView | FormMode>("list");
const [gridCols, setGridCols] = useState<GridSize>(3); const [gridCols, setGridCols] = useState<GridSize>(3);
const [selectedItemServices, setSelectedItemServices] = useState<ServiceData | null>(null); const [selectedItemServices, setSelectedItemServices] = useState<ServiceData | null>(null);
const [selectedItemEvents, setSelectedItemEvents] = useState<schema.ApplicationData | null>(null); const [selectedItemEvents, setSelectedItemEvents] = useState<EventData | null>(null);
const [selectedItemAppenders, setSelectedItemAppenders] = useState<schema.ApplicationData | null>(null); const [selectedItemAppenders, setSelectedItemAppenders] = useState<EventData | null>(null);
const [fieldDefinitionsServices, setFieldDefinitionsServices] = useState<ServiceFieldDefinitionsType | null>(null); const [fieldDefinitionsServices, setFieldDefinitionsServices] = useState<ServiceFieldDefinitionsType | null>(null);
const [fieldDefinitionsEvents, setFieldDefinitionsEvents] = useState<schema.FieldDefinitionsType | null>(null); const [fieldDefinitionsEvents, setFieldDefinitionsEvents] = useState<EventFieldDefinitionsType | null>(null);
const [fieldDefinitionsAppenders, setFieldDefinitionsAppenders] = useState<schema.FieldDefinitionsType | null>(null); const [fieldDefinitionsAppenders, setFieldDefinitionsAppenders] = useState<EventFieldDefinitionsType | null>(null);
const [validationSchemaServices, setValidationSchemaServices] = useState<z.ZodSchema | null>(null); const [validationSchemaServices, setValidationSchemaServices] = useState<z.ZodSchema | null>(null);
const [validationSchemaEvents, setValidationSchemaEvents] = useState<z.ZodSchema | null>(null); const [validationSchemaEvents, setValidationSchemaEvents] = useState<z.ZodSchema | null>(null);
@ -69,23 +76,127 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
useEffect(() => { useEffect(() => {
setFieldDefinitionsServices(serviceViewFieldDefinitions); setValidationSchemaServices(ServiceSchema); setFieldDefinitionsServices(serviceViewFieldDefinitions); setValidationSchemaServices(ServiceSchema);
setFieldDefinitionsEvents(schema.viewFieldDefinitions); setValidationSchemaEvents(schema.ViewApplicationSchema); setFieldDefinitionsEvents(eventFlatFieldDefinitions); setValidationSchemaEvents(EventSchema);
setFieldDefinitionsAppenders(schema.viewFieldDefinitions); setValidationSchemaAppenders(schema.ViewApplicationSchema); setFieldDefinitionsAppenders(eventFlatFieldDefinitions); setValidationSchemaAppenders(EventSchema);
}, [lang]); }, [lang]);
const handleQueryChange = (key: string, value: string | null) => { useEffect(() => {
if (selectedItemServices && selectedItemServices?.uu_id) {
updatePaginationAppenders({
page: paginationAppenders.page,
size: paginationAppenders.size,
orderField: paginationAppenders.orderField,
orderType: paginationAppenders.orderType,
query: {
...paginationAppenders.query,
service_uu_id: selectedItemServices.uu_id
}
});
updatePaginationEvents({
page: paginationEvents.page,
size: paginationEvents.size,
orderField: paginationEvents.orderField,
orderType: paginationEvents.orderType,
query: {
...paginationEvents.query,
service_uu_id: selectedItemServices.uu_id
}
});
}
}, [selectedItemServices]);
const handleQueryChangeServices = (key: string, value: string | null) => {
const newQuery = { ...paginationServices.query }; const newQuery = { ...paginationServices.query };
if (value === null) { delete newQuery[key] } else if (value.trim() === "") { delete newQuery[key] } else { newQuery[key] = value } if (value === null) { delete newQuery[key] } else if (value.trim() === "") { delete newQuery[key] } else { newQuery[key] = value }
updatePaginationServices({ page: 1, query: newQuery }) updatePaginationServices({ page: 1, query: newQuery })
}; };
const handleQueryChangeEvents = (key: string, value: string | null) => {
const newQuery = { ...paginationEvents.query };
if (value === null) { delete newQuery[key] } else if (value.trim() === "") { delete newQuery[key] } else { newQuery[key] = value }
if (selectedItemServices?.uu_id) {
newQuery.service_uu_id = selectedItemServices.uu_id;
}
updatePaginationEvents({ page: 1, query: newQuery });
};
const handleQueryChangeAppenders = (key: string, value: string | null) => {
const newQuery = { ...paginationAppenders.query };
if (value === null) { delete newQuery[key] } else if (value.trim() === "") { delete newQuery[key] } else { newQuery[key] = value }
if (selectedItemServices?.uu_id) {
newQuery.service_uu_id = selectedItemServices.uu_id;
}
updatePaginationAppenders({ page: 1, query: newQuery });
};
const appendServiceEvent = async (event: EventData) => {
if (!selectedItemServices?.uu_id) {
throw new Error("Service not selected");
}
const payload = { event_uu_id: event.uu_id, service_uu_id: selectedItemServices.uu_id }
fetch('/api/appenders/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
}).then((res) => {
if (res.ok) {
updatePaginationAppenders({
...paginationAppenders,
query: {
...paginationAppenders.query,
service_uu_id: selectedItemServices.uu_id
}
});
updatePaginationEvents({
...paginationEvents,
query: {
...paginationEvents.query,
service_uu_id: selectedItemServices.uu_id
}
});
}
})
}
const removeServiceEvent = async (event: EventData) => {
if (!selectedItemServices?.uu_id) {
throw new Error("Service not selected");
}
const payload = { event_uu_id: event.uu_id, service_uu_id: selectedItemServices.uu_id }
fetch('/api/appenders/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
}).then((res) => {
if (res.ok) {
updatePaginationAppenders({
...paginationAppenders,
query: {
...paginationAppenders.query,
service_uu_id: selectedItemServices.uu_id
}
});
updatePaginationEvents({
...paginationEvents,
query: {
...paginationEvents.query,
service_uu_id: selectedItemServices.uu_id
}
});
}
})
}
const handleServicesCardClick = (item: ServiceData) => { setSelectedItemServices(item); setMode("list"); }; const handleServicesCardClick = (item: ServiceData) => { setSelectedItemServices(item); setMode("list"); };
const handleServicesViewClick = (item: ServiceData) => { setSelectedItemServices(item); setMode("view"); }; const handleServicesViewClick = (item: ServiceData) => { setSelectedItemServices(item); setMode("view"); };
const handleEventsCardClick = (item: schema.ApplicationData) => { console.log("Events Card clicked:", item) }; const handleEventsCardClick = (item: EventData) => { appendServiceEvent(item); };
const handleEventsViewClick = (item: schema.ApplicationData) => { setSelectedItemEvents(item); setMode("view"); }; const handleEventsViewClick = (item: EventData) => { setSelectedItemEvents(item); setMode("view"); };
const handleAppendersCardClick = (item: schema.ApplicationData) => { console.log("Appenders Card clicked:", item) }; const handleAppendersCardClick = (item: EventData) => { removeServiceEvent(item); };
const handleAppendersViewClick = (item: schema.ApplicationData) => { setSelectedItemAppenders(item); setMode("view"); }; const handleAppendersViewClick = (item: EventData) => { setSelectedItemAppenders(item); setMode("view"); };
const cancelAllSelections = () => { const cancelAllSelections = () => {
setMode("list"); setMode("list");
@ -103,7 +214,8 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
showFields: showFieldsServices, showFields: showFieldsServices,
gridCols: gridCols, gridCols: gridCols,
titleField: "service_name", titleField: "service_name",
handleQueryChange: handleQueryChange, size: "lg" as CardSize,
handleQueryChange: handleQueryChangeServices,
updatePagination: updatePaginationServices, updatePagination: updatePaginationServices,
handleCardClick: handleServicesCardClick, handleCardClick: handleServicesCardClick,
handleViewClick: handleServicesViewClick handleViewClick: handleServicesViewClick
@ -118,7 +230,8 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
showFields: showFieldsEvents, showFields: showFieldsEvents,
gridCols: 1, gridCols: 1,
titleField: "description", titleField: "description",
handleQueryChange: handleQueryChange, size: "sm" as CardSize,
handleQueryChange: handleQueryChangeEvents,
updatePagination: updatePaginationEvents, updatePagination: updatePaginationEvents,
handleCardClick: handleEventsCardClick, handleCardClick: handleEventsCardClick,
handleViewClick: handleEventsViewClick handleViewClick: handleEventsViewClick
@ -133,7 +246,8 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
showFields: showFieldsEvents, showFields: showFieldsEvents,
gridCols: 1, gridCols: 1,
titleField: "description", titleField: "description",
handleQueryChange: handleQueryChange, size: "sm" as CardSize,
handleQueryChange: handleQueryChangeAppenders,
updatePagination: updatePaginationAppenders, updatePagination: updatePaginationAppenders,
handleCardClick: handleAppendersCardClick, handleCardClick: handleAppendersCardClick,
handleViewClick: handleAppendersViewClick handleViewClick: handleAppendersViewClick
@ -152,7 +266,7 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
formProps: { formProps: {
fieldDefinitions: fieldDefinitionsServices, fieldDefinitions: fieldDefinitionsServices,
validationSchema: validationSchemaServices, validationSchema: validationSchemaServices,
fieldsByMode: schema.fieldsByMode fieldsByMode: serviceFieldsByMode
} }
}; };
@ -169,14 +283,10 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
formProps: { formProps: {
fieldDefinitions: fieldDefinitionsEvents, fieldDefinitions: fieldDefinitionsEvents,
validationSchema: validationSchemaEvents, validationSchema: validationSchemaEvents,
fieldsByMode: schema.fieldsByMode fieldsByMode: eventFieldsByMode,
} }
}; };
const removeAppendersFromEvents = (events: schema.ApplicationData[], appenders: schema.ApplicationData[]) => {
}
const appendersFormProps = { const appendersFormProps = {
initialData: selectedItemAppenders || undefined, initialData: selectedItemAppenders || undefined,
mode: mode, mode: mode,
@ -190,7 +300,7 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
formProps: { formProps: {
fieldDefinitions: fieldDefinitionsAppenders, fieldDefinitions: fieldDefinitionsAppenders,
validationSchema: validationSchemaAppenders, validationSchema: validationSchemaAppenders,
fieldsByMode: schema.fieldsByMode fieldsByMode: eventFieldsByMode,
} }
}; };
@ -202,25 +312,26 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
</div> </div>
{mode === "list" ? ( {mode === "list" ? (
<div className="flex flex-col space-y-4"> <div className="flex flex-col space-y-4">
{!selectedItemServices ? <div className="w-full h-1/2"><ListComponentServices {...serviceListProps} /></div> : {
<div className="w-full h-1/2"> !selectedItemServices ? <div className="w-full h-1/2"><ListComponentServices {...serviceListProps} /></div> :
<Button onClick={cancelAllSelections}>{translations[lang].cancel}</Button> <div className="w-full h-1/2">
<Card className="my-5"> <Button onClick={cancelAllSelections}>{translations[lang].cancel}</Button>
<CardHeader>{translations[lang].serviceSelectedTitle}</CardHeader> <Card className="my-5">
<CardContent>{selectedItemServices?.service_name}{" "}{translations[lang].serviceSelectedContent}</CardContent> <CardHeader>{translations[lang].serviceSelectedTitle}</CardHeader>
</Card> <CardContent>{selectedItemServices.uu_id}{" "}{selectedItemServices?.service_name}{" "}{translations[lang].serviceSelectedContent}</CardContent>
<div className="flex flex-row space-x-4"> </Card>
<div className="flex-1"><ListComponentEvents {...eventsListProps} /></div> <div className="flex flex-row space-x-4">
<div className="flex-1"><ListComponentEvents {...appendersListProps} /></div> <div className="flex-1"><ListComponentEvents {...eventsListProps} /></div>
<div className="flex-1"><ListComponentEvents {...appendersListProps} /></div>
</div>
</div> </div>
</div>
} }
</div> </div>
) : ( ) : (
<div className="flex flex-col space-y-4"> <div className="flex flex-col space-y-4">
{selectedItemServices && <FormDisplay<ServiceData> {...serviceFormProps} />} {selectedItemServices && <FormDisplay<ServiceData> {...serviceFormProps} />}
{selectedItemEvents && <FormDisplay<schema.ApplicationData> {...eventsFormProps} />} {selectedItemEvents && <FormDisplay<EventData> {...eventsFormProps} />}
{selectedItemAppenders && <FormDisplay<schema.ApplicationData> {...appendersFormProps} />} {selectedItemAppenders && <FormDisplay<EventData> {...appendersFormProps} />}
</div> </div>
)} )}
</div> </div>

View File

@ -161,6 +161,9 @@ const appenderFlatFieldDefinitions = flattenFieldDefinitions(
serviceBaseFieldDefinitions serviceBaseFieldDefinitions
); );
type AppenderFieldDefinitionsType = typeof appenderFlatFieldDefinitions; type AppenderFieldDefinitionsType = typeof appenderFlatFieldDefinitions;
const appenderFieldsByMode = {
view: Object.keys(serviceBaseFieldDefinitions),
};
export type { AppendersData, AppenderFieldDefinitionsType }; export type { AppendersData, AppenderFieldDefinitionsType };
export { export {
@ -168,4 +171,5 @@ export {
appenderFlatFieldDefinitions, appenderFlatFieldDefinitions,
AppenderBaseTranslationEn, AppenderBaseTranslationEn,
AppenderBaseTranslationTr, AppenderBaseTranslationTr,
appenderFieldsByMode,
}; };

View File

@ -0,0 +1,270 @@
import { z } from "zod";
import { flattenFieldDefinitions } from "@/eventRouters/schemas/zodSchemas";
interface EventData {
uu_id: string;
function_code: string;
function_class: string;
description?: string;
property_description?: string;
marketing_layer?: string;
cost?: number;
unit_price?: number;
endpoint_code: string;
endpoint_uu_id: string;
is_confirmed: boolean;
active: boolean;
deleted?: boolean;
created_at?: string;
updated_at?: string;
}
const errorMessages = {
en: {
function_code: "Function code is required",
function_class: "Function class is required",
endpoint_code: "Endpoint code is required",
endpoint_uu_id: "Endpoint UUID is required",
},
tr: {
function_code: "Fonksiyon kodu gereklidir",
function_class: "Fonksiyon sınıfı gereklidir",
endpoint_code: "Endpoint kodu gereklidir",
endpoint_uu_id: "Endpoint UUID gereklidir",
},
};
const getEventBaseSchema = (lang: "en" | "tr" = "en") =>
z.object({
uu_id: z.string().optional(),
function_code: z.string().min(1, errorMessages[lang].function_code),
function_class: z.string().min(1, errorMessages[lang].function_class),
description: z.string().optional(),
property_description: z.string().optional(),
marketing_layer: z.string().optional(),
cost: z.number().optional(),
unit_price: z.number().optional(),
endpoint_code: z.string().min(1, errorMessages[lang].endpoint_code),
endpoint_uu_id: z.string().min(1, errorMessages[lang].endpoint_uu_id),
is_confirmed: z.boolean().default(false),
active: z.boolean().default(true),
deleted: z.boolean().default(false),
created_at: z.string().optional(),
updated_at: z.string().optional(),
});
const EventBaseSchema = getEventBaseSchema("en");
const EventBaseTranslationTr = {
uu_id: "UUID",
function_code: "Fonksiyon kodu",
function_class: "Fonksiyon sınıfı",
description: "Açıklama",
property_description: "Özellik açıklama",
marketing_layer: "Pazarlama katmanı",
cost: "Maliyet",
unit_price: "Birim fiyatı",
endpoint_code: "Endpoint kodu",
endpoint_uu_id: "Endpoint UUID",
is_confirmed: "Onaylandı",
active: "Active",
deleted: "Deleted",
created_at: "Created At",
updated_at: "Updated At",
};
const EventBaseTranslationEn = {
uu_id: "UUID",
function_code: "Function code",
function_class: "Function class",
description: "Description",
property_description: "Property description",
marketing_layer: "Marketing layer",
cost: "Cost",
unit_price: "Unit price",
endpoint_code: "Endpoint code",
endpoint_uu_id: "Endpoint UUID",
is_confirmed: "Confirmed",
active: "Active",
deleted: "Deleted",
created_at: "Created At",
updated_at: "Updated At",
};
const eventBaseFieldDefinitions = {
identificationInfo: {
title: "Event Information",
order: 1,
fields: {
uu_id: {
type: "text",
label: {
tr: EventBaseTranslationTr.uu_id,
en: EventBaseTranslationEn.uu_id,
},
readOnly: true,
required: false,
},
function_code: {
type: "text",
label: {
tr: EventBaseTranslationTr.function_code,
en: EventBaseTranslationEn.function_code,
},
readOnly: false,
required: true,
},
function_class: {
type: "text",
label: {
tr: EventBaseTranslationTr.function_class,
en: EventBaseTranslationEn.function_class,
},
readOnly: false,
required: true,
},
description: {
type: "text",
label: {
tr: EventBaseTranslationTr.description,
en: EventBaseTranslationEn.description,
},
readOnly: false,
required: false,
},
property_description: {
type: "text",
label: {
tr: EventBaseTranslationTr.property_description,
en: EventBaseTranslationEn.property_description,
},
readOnly: false,
required: false,
},
marketing_layer: {
type: "text",
label: {
tr: EventBaseTranslationTr.marketing_layer,
en: EventBaseTranslationEn.marketing_layer,
},
readOnly: false,
required: false,
},
cost: {
type: "number",
label: {
tr: EventBaseTranslationTr.cost,
en: EventBaseTranslationEn.cost,
},
readOnly: false,
required: false,
},
unit_price: {
type: "number",
label: {
tr: EventBaseTranslationTr.unit_price,
en: EventBaseTranslationEn.unit_price,
},
readOnly: false,
required: false,
},
endpoint_code: {
type: "text",
label: {
tr: EventBaseTranslationTr.endpoint_code,
en: EventBaseTranslationEn.endpoint_code,
},
readOnly: false,
required: true,
},
endpoint_uu_id: {
type: "text",
label: {
tr: EventBaseTranslationTr.endpoint_uu_id,
en: EventBaseTranslationEn.endpoint_uu_id,
},
readOnly: false,
required: true,
},
},
},
statusInfo: {
title: "Status Information",
order: 3,
fields: {
active: {
type: "checkbox",
label: {
tr: EventBaseTranslationTr.active,
en: EventBaseTranslationEn.active,
},
readOnly: false,
required: false,
defaultValue: true,
},
deleted: {
type: "checkbox",
label: {
tr: EventBaseTranslationTr.deleted,
en: EventBaseTranslationEn.deleted,
},
readOnly: true,
required: false,
defaultValue: false,
},
is_confirmed: {
type: "checkbox",
label: {
tr: EventBaseTranslationTr.is_confirmed,
en: EventBaseTranslationEn.is_confirmed,
},
readOnly: false,
required: true,
},
},
},
systemInfo: {
title: "System Information",
order: 4,
fields: {
created_at: {
type: "date",
label: {
tr: EventBaseTranslationTr.created_at,
en: EventBaseTranslationEn.created_at,
},
readOnly: true,
required: false,
},
updated_at: {
type: "date",
label: {
tr: EventBaseTranslationTr.updated_at,
en: EventBaseTranslationEn.updated_at,
},
readOnly: true,
required: false,
},
},
},
};
const ViewEventSchema = EventBaseSchema;
const EventSchema = EventBaseSchema;
const eventFlatFieldDefinitions = flattenFieldDefinitions(
eventBaseFieldDefinitions
);
type EventFieldDefinitionsType = typeof eventFlatFieldDefinitions;
const eventFieldsByMode = {
view: Object.keys(eventBaseFieldDefinitions),
};
export type { EventData, EventFieldDefinitionsType };
export {
EventSchema,
eventFlatFieldDefinitions,
EventBaseTranslationEn,
EventBaseTranslationTr,
eventFieldsByMode,
};

View File

@ -291,4 +291,5 @@ export {
serviceViewFieldDefinitions, serviceViewFieldDefinitions,
ServiceBaseTranslationEn, ServiceBaseTranslationEn,
ServiceBaseTranslationTr, ServiceBaseTranslationTr,
serviceFieldsByMode,
}; };

View File

@ -1,24 +1,10 @@
import { Language } from "@/components/common/schemas"; import { Language } from "@/components/common/schemas";
import { GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent"; import { GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
import * as schema from "./schemaList/schema"; import { CardSize } from "@/components/common/CardDisplay/schema";
import * as schemaServices from "./schemaList/services"; import * as schemaServices from "./schemaList/services";
import * as schemaEvents from "./schemaList/events";
export interface ListComponentApplicationProps { export interface ListComponentServicesProps {
lang: Language;
loading: boolean;
error: any;
data: schema.ApplicationData[];
pagination: any;
showFields: string[];
gridCols: GridSize | number;
titleField: string;
handleQueryChange: (key: string, value: string | null) => void;
updatePagination: (pagination: any) => void;
handleCardClick: (item: schema.ApplicationData) => void;
handleViewClick: (item: schema.ApplicationData) => void;
}
export interface ListComponentServiceProps {
lang: Language; lang: Language;
loading: boolean; loading: boolean;
error: any; error: any;
@ -27,8 +13,25 @@ export interface ListComponentServiceProps {
showFields: string[]; showFields: string[];
gridCols: GridSize | number; gridCols: GridSize | number;
titleField: string; titleField: string;
size: CardSize;
handleQueryChange: (key: string, value: string | null) => void; handleQueryChange: (key: string, value: string | null) => void;
updatePagination: (pagination: any) => void; updatePagination: (pagination: any) => void;
handleCardClick: (item: schemaServices.ServiceData) => void; handleCardClick: (item: schemaServices.ServiceData) => void;
handleViewClick: (item: schemaServices.ServiceData) => void; handleViewClick: (item: schemaServices.ServiceData) => void;
} }
export interface ListComponentEventsProps {
lang: Language;
loading: boolean;
error: any;
data: schemaEvents.EventData[];
pagination: any;
showFields: string[];
gridCols: GridSize | number;
titleField: string;
size: CardSize;
handleQueryChange: (key: string, value: string | null) => void;
updatePagination: (pagination: any) => void;
handleCardClick: (item: schemaEvents.EventData) => void;
handleViewClick: (item: schemaEvents.EventData) => void;
}

View File

@ -152,17 +152,17 @@ services:
# mem_limit: 512m # mem_limit: 512m
# cpus: 0.5 # cpus: 0.5
# dealer_service: dealer_service:
# container_name: dealer_service container_name: dealer_service
# build: build:
# context: . context: .
# dockerfile: ApiServices/DealerService/Dockerfile dockerfile: ApiServices/DealerService/Dockerfile
# networks: networks:
# - wag-services - wag-services
# env_file: env_file:
# - api_env.env - api_env.env
# mem_limit: 512m mem_limit: 512m
# cpus: 0.5 cpus: 0.5
networks: networks:
wag-services: wag-services: