tested appender service to events
This commit is contained in:
parent
0cde34a9bc
commit
dd707b2463
|
|
@ -49,6 +49,17 @@ 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,
|
||||||
|
)
|
||||||
|
event_found = Events.filter_one(
|
||||||
|
Events.function_code == event_dict_to_save["function_code"],
|
||||||
|
db=db_session,
|
||||||
|
).data
|
||||||
|
if event_found:
|
||||||
|
event_found.update(**event_dict_to_save)
|
||||||
|
event_found.save(db=db_session)
|
||||||
|
else:
|
||||||
|
event_to_save_database = Events.find_or_create(
|
||||||
|
**event_dict_to_save,
|
||||||
include_args=[
|
include_args=[
|
||||||
Events.function_code,
|
Events.function_code,
|
||||||
Events.function_class,
|
Events.function_class,
|
||||||
|
|
@ -56,13 +67,8 @@ class EventCluster:
|
||||||
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}")
|
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)
|
||||||
|
|
||||||
def match_event(self, event_key: str) -> "Event":
|
def match_event(self, event_key: str) -> "Event":
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,22 @@ 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,
|
|
||||||
)
|
)
|
||||||
|
endpoint_restriction_found = EndpointRestriction.filter_one_system(
|
||||||
|
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:
|
if restriction.meta_data.created:
|
||||||
restriction.save(db=db_session)
|
restriction.save(db=db_session)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -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 response = await fetchDataWithToken(
|
const requestBody = {
|
||||||
eventsListEndpoint,
|
|
||||||
{
|
|
||||||
page: payload.page,
|
page: payload.page,
|
||||||
size: payload.size,
|
size: payload.size,
|
||||||
order_field: payload.orderField,
|
order_field: payload.orderField,
|
||||||
order_type: payload.orderType,
|
order_type: payload.orderType,
|
||||||
query: payload.query,
|
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(
|
||||||
|
eventsListAvailableEndpoint,
|
||||||
|
requestBody,
|
||||||
"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
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
async function list_events_appended(payload: PaginationParams): Promise<PaginatedApiResponse<any>> {
|
||||||
listEvents,
|
|
||||||
|
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 {
|
||||||
|
list_events_available,
|
||||||
|
list_events_appended,
|
||||||
|
appendEventToService,
|
||||||
|
removeEventFromService,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -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 key={index} className="w-full p-1">
|
||||||
<Card
|
<div
|
||||||
className={`h-full ${onCardClick ? 'cursor-pointer hover:shadow-md transition-shadow' : ''}`}
|
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}
|
onClick={onCardClick ? () => onCardClick(item) : undefined}
|
||||||
>
|
>
|
||||||
<CardHeader className="p-3 pb-0 flex justify-between items-start">
|
{showViewIcon && (
|
||||||
<h3 className="text-lg font-semibold">{getFieldValue(item, titleField)}</h3>
|
<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">
|
||||||
|
<Card
|
||||||
|
className={`${getCardHeight()} ${onCardClick ? 'cursor-pointer hover:shadow-md transition-shadow' : ''} overflow-hidden flex flex-col`}
|
||||||
|
onClick={onCardClick ? () => onCardClick(item) : undefined}
|
||||||
|
>
|
||||||
|
<CardHeader className={`${getContentPadding()} pb-0 flex justify-between items-start`}>
|
||||||
|
<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 (
|
return (
|
||||||
<div className="flex">
|
<div className={`flex items-center ${getTextSize()} py-0 my-0 leading-tight`}>
|
||||||
<span className="font-medium mr-2 min-w-[80px]">
|
<span className={`${getLabelWidth()} font-medium truncate mr-1`}>{label}:</span>
|
||||||
{translations[field]?.[lang] || field}:
|
<span className="truncate max-w-[60%]">{value}</span>
|
||||||
</span>
|
</div>
|
||||||
<span className="flex-1">
|
);
|
||||||
{renderCustomField
|
}
|
||||||
? renderCustomField(item, field)
|
|
||||||
: getFieldValue(item, field)}
|
return (
|
||||||
</span>
|
<div className={`flex items-center ${getTextSize()}`}>
|
||||||
|
<span className={`${getLabelWidth()} font-medium truncate`}>{label}:</span>
|
||||||
|
<span className="truncate">{value}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
@ -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,12 +312,13 @@ 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> :
|
{
|
||||||
|
!selectedItemServices ? <div className="w-full h-1/2"><ListComponentServices {...serviceListProps} /></div> :
|
||||||
<div className="w-full h-1/2">
|
<div className="w-full h-1/2">
|
||||||
<Button onClick={cancelAllSelections}>{translations[lang].cancel}</Button>
|
<Button onClick={cancelAllSelections}>{translations[lang].cancel}</Button>
|
||||||
<Card className="my-5">
|
<Card className="my-5">
|
||||||
<CardHeader>{translations[lang].serviceSelectedTitle}</CardHeader>
|
<CardHeader>{translations[lang].serviceSelectedTitle}</CardHeader>
|
||||||
<CardContent>{selectedItemServices?.service_name}{" "}{translations[lang].serviceSelectedContent}</CardContent>
|
<CardContent>{selectedItemServices.uu_id}{" "}{selectedItemServices?.service_name}{" "}{translations[lang].serviceSelectedContent}</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="flex flex-row space-x-4">
|
<div className="flex flex-row space-x-4">
|
||||||
<div className="flex-1"><ListComponentEvents {...eventsListProps} /></div>
|
<div className="flex-1"><ListComponentEvents {...eventsListProps} /></div>
|
||||||
|
|
@ -219,8 +330,8 @@ const AppendersServicePage: React.FC<PageProps> = ({ lang }: { lang: Language })
|
||||||
) : (
|
) : (
|
||||||
<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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
@ -291,4 +291,5 @@ export {
|
||||||
serviceViewFieldDefinitions,
|
serviceViewFieldDefinitions,
|
||||||
ServiceBaseTranslationEn,
|
ServiceBaseTranslationEn,
|
||||||
ServiceBaseTranslationTr,
|
ServiceBaseTranslationTr,
|
||||||
|
serviceFieldsByMode,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue