diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore
new file mode 100644
index 0000000..9de0f16
--- /dev/null
+++ b/.codegraph/.gitignore
@@ -0,0 +1,16 @@
+# CodeGraph data files
+# These are local to each machine and should not be committed
+
+# Database
+*.db
+*.db-wal
+*.db-shm
+
+# Cache
+cache/
+
+# Logs
+*.log
+
+# Hook markers
+.dirty
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..d990996
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,7 @@
+KEYCLOAK_CLIENT_SECRET=your_client_secret_here
+KEYCLOAK_TOKEN_URI=http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token
+LOYALTY_CORE_BASE_URL=http://192.168.99.242:8081
+MCP_SERVER_URL=http://localhost:9331
+
+VITE_AGENT_HTTP_URL=http://localhost:9332
+VITE_AGENT_WS_URL=ws://localhost:9332
diff --git a/.gitignore b/.gitignore
index 74da55f..3883524 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,39 +1,67 @@
-HELP.md
-target/
-!.mvn/wrapper/maven-wrapper.jar
-!**/src/main/**/target/
-!**/src/test/**/target/
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-build/
-!**/src/main/**/build/
-!**/src/test/**/build/
-
-### VS Code ###
-.vscode/
-
-### Mac OS ###
-.DS_Store
-
-### Others ###
+# Logs
+logs
*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+# Node & Frontend
+node_modules
+dist
+dist-ssr
+*.local
+coverage/
+
+# Environment files
+.env
+.env.*.local
+!.env.example
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+# Java / Maven
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+.mvn/wrapper/maven-wrapper.jar
+
+# Compiled class file
+*.class
+
+# Package Files
+*.jar
+*.war
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# Virtual machine crash logs
+hs_err_pid*
+replay_pid*
+
+# Spring Boot
+HELP.md
+token.json
+
+openspec
+.agent
+codegraph
+log
\ No newline at end of file
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..6fa991d
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "plugins": ["react", "typescript", "oxc"],
+ "rules": {
+ "react/rules-of-hooks": "error",
+ "react/only-export-components": ["warn", { "allowConstantExport": true }]
+ }
+}
diff --git a/README.md b/README.md
index 08dd5d2..479b9ec 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,31 @@
- ./mvnw package -Pgen
+# Loyalty Agent Service
-
- ./mvnw compile spring-boot:run
\ No newline at end of file
+This project consists of a 3-module architecture:
+1. `loyalty-mcp-server`: Domain MCP tools and operations
+2. `loyalty-agent`: Orchestration and BFF layer
+3. Frontend (React/Vite in the root directory)
+
+## Startup Instructions
+
+### 1. Prerequisites
+- Java 21+
+- Node.js & pnpm
+- `.env` file created (see `.env.example`)
+
+### 2. Start Backend Modules
+You can use the provided startup script:
+```bash
+./start-all.sh
+```
+Or start them individually in separate terminals:
+```bash
+./mvnw spring-boot:run -pl loyalty-mcp-server
+./mvnw spring-boot:run -pl loyalty-agent
+```
+
+### 3. Start Frontend
+In a new terminal:
+```bash
+pnpm install
+pnpm dev
+```
diff --git a/components.json b/components.json
new file mode 100644
index 0000000..c988e70
--- /dev/null
+++ b/components.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.js",
+ "css": "src/index.css",
+ "baseColor": "slate",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/context.md b/context.md
new file mode 100644
index 0000000..6113c49
--- /dev/null
+++ b/context.md
@@ -0,0 +1,153 @@
+---
+trigger: always_on
+---
+
+# Project Context: Loyalty Agent Service
+
+This repository is a local development workspace for a loyalty AI assistant. It has a React chat UI at the repository root and two Spring Boot backend modules managed by a parent Maven project.
+
+## Architecture
+
+### Frontend: root directory
+
+- Stack: React 19, Vite, TypeScript, Tailwind CSS, shadcn-style UI primitives, Zustand, `@stomp/stompjs`, `react-markdown`, `remark-gfm`.
+- Entry point: `src/main.tsx` renders `src/App.tsx`, which renders `ChatLayout`.
+- Dev port: `9333` from `vite.config.ts`.
+- Main UI flow:
+ - `src/store/useChatStore.ts` owns chat state and STOMP connection.
+ - WebSocket URL is currently hard-coded as `ws://localhost:9332/ws`.
+ - User messages are published to `/app/chat`.
+ - Agent events are received from `/user/queue/chat-events`.
+ - `CampaignList` fetches campaign data from `http://localhost:9332/api/v1/agent/campaigns` and campaign rules from `http://localhost:9332/api/v1/agent/campaigns/{campaignId}/rules`.
+
+### `loyalty-agent`
+
+- Spring Boot application: `dev.sonpx.loyalty.agent.LoyaltyAgentApplication`.
+- Runtime port: `9332`.
+- Role: BFF and AI orchestration layer for the frontend.
+- Key files:
+ - `controller/AgentController.java`: STOMP chat endpoint and REST campaign proxy endpoints.
+ - `service/LoyaltyAgentService.java`: builds a Spring AI `ChatClient`, streams LLM output, wraps tool calls, and emits tool status events.
+ - `config/WebSocketConfig.java`: STOMP endpoint `/ws`, application prefix `/app`, user destination prefix `/user`, broker prefixes `/topic` and `/queue`.
+ - `config/AgentConfig.java`: OAuth2-enabled `RestClient` for the loyalty core API.
+ - `controller/ConversationController.java`: simple in-memory conversation/message REST endpoints.
+- AI configuration:
+ - Uses Ollama at `http://localhost:11434`.
+ - Default model in `application.yml`: `qwen3.5:2b`.
+ - MCP client connects to `${MCP_SERVER_URL:http://localhost:9331}`.
+- Agent event types sent to the UI: `TOKEN`, `TOOL_STATUS`, `DONE`, `ERROR`.
+
+### `loyalty-mcp-server`
+
+- Spring Boot application: `dev.sonpx.loyalty.mcp.McpServerApplication`.
+- Runtime port: `9331`.
+- Role: MCP tool server for loyalty operations. The agent calls this service through Spring AI MCP SSE.
+- MCP endpoints from `application.yml`:
+ - SSE endpoint: `/sse`
+ - Message endpoint: `/message`
+- Key files:
+ - `config/McpServerConfig.java`: registers MCP tool objects.
+ - `tool/LoyaltyAgentTools.java`: member tiers, point pools, campaign creation, static attributes.
+ - `tool/reward/CampaignTools.java`: campaign listing and campaign draft lifecycle.
+ - `tool/reward/CampaignRuleTools.java`: campaign rule listing, creation, editing, and draft rule attachment.
+ - `tool/pool/PointPoolTools.java`: pool and SOP draft tools.
+ - `draft/DraftSessionManager.java`: in-memory draft sessions keyed by `conversationId`, with a 1-hour TTL.
+ - `draft/DependencyValidator.java`: validates draft dependencies before tools mutate or submit drafts.
+- API clients call the loyalty core service with OAuth2 client credentials.
+
+## External Services And Environment
+
+Create `.env` from `.env.example` for local development.
+
+Important variables:
+
+- `KEYCLOAK_CLIENT_SECRET`
+- `KEYCLOAK_TOKEN_URI`
+- `LOYALTY_CORE_BASE_URL`
+- `MCP_SERVER_URL`
+- `VITE_AGENT_HTTP_URL`
+- `VITE_AGENT_WS_URL`
+
+Current defaults:
+
+- Keycloak token URI: `http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token`
+- Loyalty core base URL: `http://192.168.99.242:8081`
+- MCP server URL: `http://localhost:9331`
+- Agent HTTP URL: `http://localhost:9332`
+- Agent WS URL: `ws://localhost:9332`
+
+Note: some frontend code still uses hard-coded localhost URLs instead of the `VITE_*` variables.
+
+## Local Startup
+
+Use the root script for the full local stack:
+
+```bash
+./start-all.sh
+```
+
+What the script does:
+
+1. Loads `.env` if present.
+2. Kills processes on ports `9331`, `9332`, and `9333`.
+3. Starts `loyalty-mcp-server` and waits for port `9331`.
+4. Starts a `nodemon` compile watcher for the MCP server.
+5. Starts `loyalty-agent` and waits for port `9332`.
+6. Starts a `nodemon` compile watcher for the agent.
+7. Starts the frontend with `pnpm dev` on port `9333`.
+8. Writes logs to `log/`.
+
+Manual startup:
+
+```bash
+./mvnw spring-boot:run -pl loyalty-mcp-server
+./mvnw spring-boot:run -pl loyalty-agent
+pnpm dev
+```
+
+The script currently invokes `-Pgen`, but the checked-in POM files do not define a `gen` Maven profile. Do not assume OpenAPI generation is wired unless you verify or add that profile.
+
+## Build, Lint, And Tests
+
+Frontend:
+
+```bash
+pnpm install
+pnpm lint
+pnpm build
+```
+
+Backend:
+
+```bash
+./mvnw test
+./mvnw test -pl loyalty-agent
+./mvnw test -pl loyalty-mcp-server
+```
+
+Relevant tests:
+
+- `loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/controller/AgentControllerTest.java`
+- `loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.java`
+- `loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java`
+- `loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java`
+- `loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java`
+
+`LoyaltyAgentToolsRealApiTest` may depend on real external API availability and credentials.
+
+## Development Guidelines For Agents
+
+- Keep the boundary clear:
+ - `loyalty-agent` owns frontend-facing chat orchestration and REST proxy endpoints.
+ - `loyalty-mcp-server` owns tool implementations and loyalty core API access.
+ - The frontend owns STOMP state, message rendering, chat layout, and campaign context panels.
+- Preserve the STOMP contract unless intentionally changing both sides:
+ - Client publishes to `/app/chat`.
+ - Server handles `@MessageMapping("/chat")`.
+ - Server sends events to `/user/queue/chat-events`.
+- Preserve `conversationId` propagation. MCP draft tools depend on it to keep draft campaign/pool/rule state in `DraftSessionManager`.
+- Tool responses may contain `presentation.content`; the agent system prompt requires displaying that content exactly.
+- Reuse existing UI primitives in `src/components/ui` and existing Tailwind conventions.
+- Do not introduce persistent storage assumptions. Conversation repositories and draft sessions are currently in memory.
+- Do not hard-code new service URLs when a matching environment variable already exists.
+- Be careful with ports: MCP is `9331`, agent is `9332`, frontend is `9333`.
diff --git a/download-specs.ps1 b/download-specs.ps1
deleted file mode 100644
index 8aa8c6e..0000000
--- a/download-specs.ps1
+++ /dev/null
@@ -1,38 +0,0 @@
-$baseUrl = "http://192.168.99.242:8081"
-$specs = @(
- @{ name="attribute"; url="/attribute/v3/api-docs" },
- @{ name="batch-scheduler"; url="/batch-scheduler/v3/api-docs" },
- @{ name="catalogue"; url="/catalogue/v3/api-docs" },
- @{ name="customer"; url="/customer/v3/api-docs" },
- @{ name="identity"; url="/identity/v3/api-docs" },
- @{ name="marketing"; url="/marketing/v3/api-docs" },
- @{ name="master"; url="/master/v3/api-docs" },
- @{ name="notification"; url="/notification/v3/api-docs" },
- @{ name="reward"; url="/reward/v3/api-docs" },
- @{ name="transaction"; url="/transaction/v3/api-docs" }
-)
-
-$targetDir = "src/main/resources/specs"
-if (-not (Test-Path $targetDir)) {
- New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
-}
-
-foreach ($spec in $specs) {
- $fullUrl = $baseUrl + $spec.url
- $outputPath = Join-Path $targetDir ($spec.name + ".json")
- Write-Host "Downloading $($spec.name) from $fullUrl ..."
- try {
- Invoke-WebRequest -Uri $fullUrl -OutFile $outputPath -UseBasicParsing
- Write-Host "[OK] $($spec.name) saved to $outputPath"
- } catch {
- Write-Host "[WARNING] Failed to download from $fullUrl, trying alternative path..."
- $altUrl = $baseUrl + "/v3/api-docs/" + $spec.name
- try {
- Invoke-WebRequest -Uri $altUrl -OutFile $outputPath -UseBasicParsing
- Write-Host "[OK] $($spec.name) saved to $outputPath (alternative path)"
- } catch {
- Write-Host "[ERROR] Failed to download $($spec.name)"
- }
- }
-}
-Write-Host "Done downloading specs."
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..2c255ef
--- /dev/null
+++ b/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ agent-ui-temp
+
+
+
+
+
+
diff --git a/loyalty-agent/Dockerfile b/loyalty-agent/Dockerfile
new file mode 100644
index 0000000..7ef433a
--- /dev/null
+++ b/loyalty-agent/Dockerfile
@@ -0,0 +1,6 @@
+FROM eclipse-temurin:25-jre-alpine
+VOLUME /tmp
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} app.jar
+EXPOSE 8080
+ENTRYPOINT ["java","-jar","/app.jar"]
diff --git a/loyalty-agent/pom.xml b/loyalty-agent/pom.xml
new file mode 100644
index 0000000..eb7ed25
--- /dev/null
+++ b/loyalty-agent/pom.xml
@@ -0,0 +1,151 @@
+
+
+ 4.0.0
+
+
+ dev.sonpx.loyalty
+ loyalty-agent-service-parent
+ 0.0.1-SNAPSHOT
+
+
+ loyalty-agent
+ loyalty-agent-service
+ AI Agent Service to orchestrate Loyalty core system
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-starter-mcp-client
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+
+
+ org.springframework.ai
+ spring-ai-starter-model-ollama
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ true
+
+
+
+
+ org.mapstruct
+ mapstruct
+ 1.6.2
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.7.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.6.2
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.5.0
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ ${project.build.directory}/generated-sources/openapi/src/main/java
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/dev/sonpx/loyalty/agent/LoyaltyAgentApplication.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/LoyaltyAgentApplication.java
similarity index 100%
rename from src/main/java/dev/sonpx/loyalty/agent/LoyaltyAgentApplication.java
rename to loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/LoyaltyAgentApplication.java
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java
new file mode 100644
index 0000000..6d12218
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java
@@ -0,0 +1,42 @@
+package dev.sonpx.loyalty.agent.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
+import org.springframework.web.client.RestClient;
+
+@Configuration
+public class AgentConfig {
+
+ @Value("${loyalty.core.base-url}")
+ private String coreBaseUrl;
+
+ @Bean
+ public RestClient coreRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
+ return RestClient.builder()
+ .baseUrl(coreBaseUrl)
+ .requestInterceptor((request, body, execution) -> {
+ org.springframework.security.core.Authentication principal =
+ new org.springframework.security.authentication.AnonymousAuthenticationToken(
+ "key", "loyalty-agent-service",
+ org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+ OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
+ .withClientRegistrationId("keycloak")
+ .principal(principal)
+ .build();
+ try {
+ OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
+ if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
+ request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return execution.execute(request, body);
+ })
+ .build();
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java
new file mode 100644
index 0000000..641c0f3
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java
@@ -0,0 +1,27 @@
+package dev.sonpx.loyalty.agent.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ .csrf(csrf -> csrf.disable())
+ .authorizeHttpRequests(authz -> authz
+ .requestMatchers("/api/v1/agent/**", "/api/v1/conversations/**", "/ws/**", "/error").permitAll()
+ .anyRequest().authenticated()
+ )
+ .oauth2Login(Customizer.withDefaults())
+ .oauth2Client(Customizer.withDefaults());
+ return http.build();
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java
new file mode 100644
index 0000000..228e0ba
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java
@@ -0,0 +1,37 @@
+package dev.sonpx.loyalty.agent.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.messaging.simp.config.MessageBrokerRegistry;
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
+import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
+
+@Configuration
+@EnableWebSocketMessageBroker
+public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
+
+ @Override
+ public void configureMessageBroker(MessageBrokerRegistry config) {
+ config.enableSimpleBroker("/topic", "/queue");
+ config.setApplicationDestinationPrefixes("/app");
+ config.setUserDestinationPrefix("/user");
+ }
+
+ @Override
+ public void registerStompEndpoints(StompEndpointRegistry registry) {
+ registry.addEndpoint("/ws")
+ .setAllowedOriginPatterns("*")
+ .setHandshakeHandler(new org.springframework.web.socket.server.support.DefaultHandshakeHandler() {
+ @Override
+ protected java.security.Principal determineUser(org.springframework.http.server.ServerHttpRequest request, org.springframework.web.socket.WebSocketHandler wsHandler, java.util.Map attributes) {
+ return new java.security.Principal() {
+ private final String id = java.util.UUID.randomUUID().toString();
+ @Override
+ public String getName() {
+ return id;
+ }
+ };
+ }
+ });
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java
new file mode 100644
index 0000000..af99af6
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java
@@ -0,0 +1,71 @@
+package dev.sonpx.loyalty.agent.controller;
+
+import dev.sonpx.loyalty.agent.service.LoyaltyAgentService;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.messaging.handler.annotation.MessageMapping;
+import org.springframework.messaging.handler.annotation.Payload;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.CrossOrigin;
+
+@RestController
+@CrossOrigin(origins = "*")
+public class AgentController {
+
+ private final LoyaltyAgentService agentService;
+
+ private final org.springframework.web.client.RestClient coreRestClient;
+
+ public AgentController(LoyaltyAgentService agentService, org.springframework.web.client.RestClient coreRestClient) {
+ this.agentService = agentService;
+ this.coreRestClient = coreRestClient;
+ }
+
+ public record ChatRequest(String conversationId, String messageId, String prompt, String activeCampaignId) {}
+
+ @MessageMapping("/chat")
+ public void chat(@Payload ChatRequest request, java.security.Principal principal) {
+ agentService.chat(request, principal.getName());
+ }
+
+ @GetMapping("/api/v1/agent/campaigns")
+ public Object getCampaigns(
+ @org.springframework.web.bind.annotation.RequestParam(defaultValue = "0") int page,
+ @org.springframework.web.bind.annotation.RequestParam(defaultValue = "50") int size,
+ @org.springframework.web.bind.annotation.RequestParam(required = false) String status,
+ @org.springframework.web.bind.annotation.RequestParam(required = false) String name) {
+
+ String url = String.format("/svc/reward/api/campaign/csr/list?page=%d&size=%d", page, size);
+ if (status != null && !status.isEmpty()) url += "&status=" + status;
+ if (name != null && !name.isEmpty()) url += "&name=" + name;
+
+ String response = coreRestClient.get()
+ .uri(url)
+ .retrieve()
+ .body(String.class);
+ return standardizeResponse(response);
+ }
+
+ @GetMapping("/api/v1/agent/campaigns/{campaignId}/rules")
+ public Object getCampaignRules(@org.springframework.web.bind.annotation.PathVariable("campaignId") String campaignId) {
+ String response = coreRestClient.get()
+ .uri("/svc/reward/api/campaign-rule/csr/list?page=0&size=100&campaignId.equals=" + campaignId)
+ .retrieve()
+ .body(String.class);
+ return standardizeResponse(response);
+ }
+
+ Object standardizeResponse(String jsonStr) {
+ try {
+ com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
+ com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonStr);
+ com.fasterxml.jackson.databind.JsonNode arrayNode = root;
+ if (root.has("content") && root.get("content").isArray()) arrayNode = root.get("content");
+ else if (root.has("data") && root.get("data").isArray()) arrayNode = root.get("data");
+
+ return mapper.convertValue(arrayNode, new com.fasterxml.jackson.core.type.TypeReference>>() {});
+ } catch (Exception e) {
+ return java.util.List.of();
+ }
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/ConversationController.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/ConversationController.java
new file mode 100644
index 0000000..05654d0
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/ConversationController.java
@@ -0,0 +1,42 @@
+package dev.sonpx.loyalty.agent.controller;
+
+import dev.sonpx.loyalty.agent.domain.Conversation;
+import dev.sonpx.loyalty.agent.domain.Message;
+import dev.sonpx.loyalty.agent.repository.ConversationRepository;
+import dev.sonpx.loyalty.agent.repository.MessageRepository;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/api/v1/conversations")
+@CrossOrigin(origins = "*")
+public class ConversationController {
+
+ private final ConversationRepository conversationRepository;
+ private final MessageRepository messageRepository;
+
+ public ConversationController(ConversationRepository conversationRepository, MessageRepository messageRepository) {
+ this.conversationRepository = conversationRepository;
+ this.messageRepository = messageRepository;
+ }
+
+ @PostMapping
+ public ResponseEntity createConversation() {
+ Conversation conversation = new Conversation();
+ conversation.setId(UUID.randomUUID().toString());
+ conversation.setCreatedAt(Instant.now());
+ conversation.setUpdatedAt(Instant.now());
+ Conversation saved = conversationRepository.save(conversation);
+ return ResponseEntity.ok(saved);
+ }
+
+ @GetMapping("/{id}/messages")
+ public ResponseEntity> getMessages(@PathVariable("id") String id) {
+ List messages = messageRepository.findByConversationIdOrderByCreatedAtAsc(id);
+ return ResponseEntity.ok(messages);
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/AgentEvent.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/AgentEvent.java
new file mode 100644
index 0000000..17372e3
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/AgentEvent.java
@@ -0,0 +1,19 @@
+package dev.sonpx.loyalty.agent.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public record AgentEvent(String type, String messageId, String content, String tool, String status) {
+ public static AgentEvent token(String messageId, String content) {
+ return new AgentEvent("TOKEN", messageId, content, null, null);
+ }
+ public static AgentEvent toolStatus(String messageId, String tool, String status) {
+ return new AgentEvent("TOOL_STATUS", messageId, null, tool, status);
+ }
+ public static AgentEvent done(String messageId) {
+ return new AgentEvent("DONE", messageId, null, null, null);
+ }
+ public static AgentEvent error(String messageId, String error) {
+ return new AgentEvent("ERROR", messageId, error, null, null);
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/Conversation.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/Conversation.java
new file mode 100644
index 0000000..1d1a4a4
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/Conversation.java
@@ -0,0 +1,18 @@
+package dev.sonpx.loyalty.agent.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.Instant;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Conversation {
+ private String id;
+ private Instant createdAt;
+ private Instant updatedAt;
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/Message.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/Message.java
new file mode 100644
index 0000000..28d6dbb
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/domain/Message.java
@@ -0,0 +1,20 @@
+package dev.sonpx.loyalty.agent.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.Instant;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Message {
+ private String id;
+ private String conversationId;
+ private String role;
+ private String content;
+ private Instant createdAt;
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/ConversationRepository.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/ConversationRepository.java
new file mode 100644
index 0000000..290c003
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/ConversationRepository.java
@@ -0,0 +1,12 @@
+package dev.sonpx.loyalty.agent.repository;
+
+import dev.sonpx.loyalty.agent.domain.Conversation;
+
+import java.util.Optional;
+import java.util.List;
+
+public interface ConversationRepository {
+ Conversation save(Conversation conversation);
+ Optional findById(String id);
+ List findAll();
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/InMemoryConversationRepository.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/InMemoryConversationRepository.java
new file mode 100644
index 0000000..c097f22
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/InMemoryConversationRepository.java
@@ -0,0 +1,31 @@
+package dev.sonpx.loyalty.agent.repository;
+
+import dev.sonpx.loyalty.agent.domain.Conversation;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Repository
+public class InMemoryConversationRepository implements ConversationRepository {
+ private final Map store = new ConcurrentHashMap<>();
+
+ @Override
+ public Conversation save(Conversation conversation) {
+ store.put(conversation.getId(), conversation);
+ return conversation;
+ }
+
+ @Override
+ public Optional findById(String id) {
+ return Optional.ofNullable(store.get(id));
+ }
+
+ @Override
+ public List findAll() {
+ return new ArrayList<>(store.values());
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/InMemoryMessageRepository.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/InMemoryMessageRepository.java
new file mode 100644
index 0000000..2394883
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/InMemoryMessageRepository.java
@@ -0,0 +1,30 @@
+package dev.sonpx.loyalty.agent.repository;
+
+import dev.sonpx.loyalty.agent.domain.Message;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@Repository
+public class InMemoryMessageRepository implements MessageRepository {
+ private final Map store = new ConcurrentHashMap<>();
+
+ @Override
+ public Message save(Message message) {
+ store.put(message.getId(), message);
+ return message;
+ }
+
+ @Override
+ public List findByConversationIdOrderByCreatedAtAsc(String conversationId) {
+ return store.values().stream()
+ .filter(m -> conversationId.equals(m.getConversationId()))
+ .sorted(Comparator.comparing(Message::getCreatedAt))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/MessageRepository.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/MessageRepository.java
new file mode 100644
index 0000000..e9cb12c
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/repository/MessageRepository.java
@@ -0,0 +1,10 @@
+package dev.sonpx.loyalty.agent.repository;
+
+import dev.sonpx.loyalty.agent.domain.Message;
+
+import java.util.List;
+
+public interface MessageRepository {
+ Message save(Message message);
+ List findByConversationIdOrderByCreatedAtAsc(String conversationId);
+}
diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java
new file mode 100644
index 0000000..6ca63ef
--- /dev/null
+++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java
@@ -0,0 +1,83 @@
+package dev.sonpx.loyalty.agent.service;
+
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.tool.ToolCallbackProvider;
+import org.springframework.stereotype.Service;
+import java.util.List;
+
+@Service
+public class LoyaltyAgentService {
+
+ private final org.springframework.ai.chat.model.ChatModel chatModel;
+ private final List toolProviders;
+ private final org.springframework.messaging.simp.SimpMessagingTemplate messagingTemplate;
+
+ public LoyaltyAgentService(org.springframework.ai.chat.model.ChatModel chatModel, List toolProviders, org.springframework.messaging.simp.SimpMessagingTemplate messagingTemplate) {
+ this.chatModel = chatModel;
+ this.toolProviders = toolProviders;
+ this.messagingTemplate = messagingTemplate;
+ }
+
+ public void chat(dev.sonpx.loyalty.agent.controller.AgentController.ChatRequest request, String sessionId) {
+ String prompt = request.prompt();
+ if (request.activeCampaignId() != null && !request.activeCampaignId().isEmpty()) {
+ prompt = "Context: Active Campaign ID is " + request.activeCampaignId() + ".\n\n" + prompt;
+ }
+ if (request.conversationId() != null && !request.conversationId().isEmpty()) {
+ prompt = "System Context: The current conversationId (session ID) is " + request.conversationId() + ". You MUST provide this conversationId as a parameter to any tool that requires it.\n\n" + prompt;
+ }
+
+ List wrappedTools = new java.util.ArrayList<>();
+ for (ToolCallbackProvider provider : toolProviders) {
+ for (org.springframework.ai.tool.ToolCallback tool : provider.getToolCallbacks()) {
+ wrappedTools.add(new org.springframework.ai.tool.ToolCallback() {
+ @Override
+ public org.springframework.ai.tool.definition.ToolDefinition getToolDefinition() {
+ return tool.getToolDefinition();
+ }
+ @Override
+ public String call(String toolInput) {
+ messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "running"));
+ try {
+ return tool.call(toolInput);
+ } finally {
+ messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "done"));
+ }
+ }
+ });
+ }
+ }
+
+ ChatClient chatClient = ChatClient.builder(chatModel)
+ .defaultSystem("""
+ You are an expert Loyalty System AI Assistant.
+ Your primary job is to orchestrate operations for our Loyalty core system.
+
+ CRITICAL RULES:
+ 1. DO NOT create a model.client.dev.sonpx.loyalty.agent.Campaign unless you verify that Member Tiers and Point Pools exist first.
+ 2. Use the `listMemberTiers` tool to check for valid tier IDs.
+ 3. Use the `listPointPools` tool to check for valid pool IDs and ensure they have a sufficient balance.
+ 4. Use the `listStaticAttributes` tool when asked about configurations, properties, or static values.
+ 5. Only use the `createCampaign` tool once verification is complete.
+ 6. Always answer the user in the language they used (e.g., Vietnamese).
+ 7. If a tool response contains a `presentation.content` field, you MUST display its content exactly as provided to the user. Do not summarize or alter its formatting. Use the `data` field only for your internal reasoning.
+ 8. When asked for information, details, or status of an existing campaign, ALWAYS use the `listCampaigns` tool. DO NOT use `createCampaign` unless explicitly asked to create or add a new one.
+ """)
+ .defaultTools(wrappedTools)
+ .build();
+
+ chatClient.prompt()
+ .user(prompt)
+ .stream()
+ .content()
+ .doOnComplete(() -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.done(request.messageId())))
+ .subscribe(
+ content -> {
+ if (content != null && !content.isEmpty()) {
+ messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.token(request.messageId(), content));
+ }
+ },
+ error -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.error(request.messageId(), error.getMessage()))
+ );
+ }
+}
diff --git a/loyalty-agent/src/main/resources/application.yml b/loyalty-agent/src/main/resources/application.yml
new file mode 100644
index 0000000..e63bf22
--- /dev/null
+++ b/loyalty-agent/src/main/resources/application.yml
@@ -0,0 +1,47 @@
+server:
+ port: 9332
+
+spring:
+ mvc:
+ async:
+ request-timeout: 3600000
+ application:
+ name: loyalty-agent-service
+ ai:
+ ollama:
+ base-url: http://localhost:11434
+ chat:
+ options:
+ model: qwen3.5:2b
+ temperature: 0.3
+ mcp:
+ client:
+ request-timeout: 60000
+ enabled: true
+ type: sync
+ sse:
+ connections:
+ loyalty:
+ url: ${MCP_SERVER_URL:http://localhost:9331}
+ security:
+ oauth2:
+ client:
+ registration:
+ keycloak:
+ client-id: ols-cli
+ client-secret: ${KEYCLOAK_CLIENT_SECRET:1jV8NSAeybIqmUK0AWhI8hgdo1q5itj7}
+ authorization-grant-type: client_credentials
+ provider:
+ keycloak:
+ token-uri: ${KEYCLOAK_TOKEN_URI:http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token}
+
+loyalty:
+ core:
+ base-url: ${LOYALTY_CORE_BASE_URL:http://192.168.99.242:8081}
+
+logging:
+ level:
+ root: warn
+ org.springframework.ai: INFO
+ org.springframework.web: INFO
+ dev.sonpx.loyalty: DEBUG
diff --git a/loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/controller/AgentControllerTest.java b/loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/controller/AgentControllerTest.java
new file mode 100644
index 0000000..07684e4
--- /dev/null
+++ b/loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/controller/AgentControllerTest.java
@@ -0,0 +1,34 @@
+package dev.sonpx.loyalty.agent.controller;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+class AgentControllerTest {
+
+ private final AgentController controller = new AgentController(null, null);
+
+ @Test
+ void testStandardizeResponse_Array() {
+ String json = "[{\"id\":\"1\"}]";
+ Object result = controller.standardizeResponse(json);
+ assertTrue(result instanceof java.util.List);
+ assertEquals(1, ((java.util.List>) result).size());
+ }
+
+ @Test
+ void testStandardizeResponse_ContentWrapped() {
+ String json = "{\"content\":[{\"id\":\"1\"}], \"page\":0}";
+ Object result = controller.standardizeResponse(json);
+ assertTrue(result instanceof java.util.List);
+ assertEquals(1, ((java.util.List>) result).size());
+ }
+
+ @Test
+ void testStandardizeResponse_DataWrapped() {
+ String json = "{\"data\":[{\"id\":\"1\"}]}";
+ Object result = controller.standardizeResponse(json);
+ assertTrue(result instanceof java.util.List);
+ assertEquals(1, ((java.util.List>) result).size());
+ }
+}
diff --git a/loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.java b/loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.java
new file mode 100644
index 0000000..93aeff3
--- /dev/null
+++ b/loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.java
@@ -0,0 +1,44 @@
+package dev.sonpx.loyalty.agent.domain;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+class AgentEventTest {
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ @Test
+ void testTokenEventSerialization() throws Exception {
+ AgentEvent event = AgentEvent.token("msg-123", "Hello");
+ String json = objectMapper.writeValueAsString(event);
+
+ assertTrue(json.contains("\"type\":\"TOKEN\""));
+ assertTrue(json.contains("\"messageId\":\"msg-123\""));
+ assertTrue(json.contains("\"content\":\"Hello\""));
+ assertFalse(json.contains("\"tool\""));
+ assertFalse(json.contains("\"status\""));
+ }
+
+ @Test
+ void testToolStatusEventSerialization() throws Exception {
+ AgentEvent event = AgentEvent.toolStatus("msg-123", "searchTool", "running");
+ String json = objectMapper.writeValueAsString(event);
+
+ assertTrue(json.contains("\"type\":\"TOOL_STATUS\""));
+ assertTrue(json.contains("\"messageId\":\"msg-123\""));
+ assertTrue(json.contains("\"tool\":\"searchTool\""));
+ assertTrue(json.contains("\"status\":\"running\""));
+ assertFalse(json.contains("\"content\""));
+ }
+
+ @Test
+ void testDoneEventSerialization() throws Exception {
+ AgentEvent event = AgentEvent.done("msg-123");
+ String json = objectMapper.writeValueAsString(event);
+
+ assertTrue(json.contains("\"type\":\"DONE\""));
+ assertTrue(json.contains("\"messageId\":\"msg-123\""));
+ assertFalse(json.contains("\"content\""));
+ }
+}
diff --git a/loyalty-mcp-server/Dockerfile b/loyalty-mcp-server/Dockerfile
new file mode 100644
index 0000000..1c5293a
--- /dev/null
+++ b/loyalty-mcp-server/Dockerfile
@@ -0,0 +1,6 @@
+FROM eclipse-temurin:25-jre-alpine
+VOLUME /tmp
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} app.jar
+EXPOSE 8081
+ENTRYPOINT ["java","-jar","/app.jar"]
diff --git a/loyalty-mcp-server/pom.xml b/loyalty-mcp-server/pom.xml
new file mode 100644
index 0000000..f218feb
--- /dev/null
+++ b/loyalty-mcp-server/pom.xml
@@ -0,0 +1,120 @@
+
+
+ 4.0.0
+
+
+ dev.sonpx.loyalty
+ loyalty-agent-service-parent
+ 0.0.1-SNAPSHOT
+
+
+ loyalty-mcp-server
+ loyalty-mcp-server
+ MCP Server for Loyalty API Tools
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.ai
+ spring-ai-starter-mcp-server-webmvc
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ true
+
+
+
+
+ org.mapstruct
+ mapstruct
+ 1.6.2
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.7.0
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.6.2
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
+
+
+
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/McpServerApplication.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/McpServerApplication.java
new file mode 100644
index 0000000..e4f2701
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/McpServerApplication.java
@@ -0,0 +1,12 @@
+package dev.sonpx.loyalty.mcp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class McpServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(McpServerApplication.class, args);
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/CampaignApiClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/CampaignApiClient.java
new file mode 100644
index 0000000..47aa5cb
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/CampaignApiClient.java
@@ -0,0 +1,16 @@
+package dev.sonpx.loyalty.mcp.client.api;
+
+import dev.sonpx.loyalty.mcp.client.model.Campaign;
+import dev.sonpx.loyalty.mcp.client.model.OperationResult;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.service.annotation.GetExchange;
+import org.springframework.web.service.annotation.PostExchange;
+
+public interface CampaignApiClient {
+
+ @GetExchange("/api/campaign/csr/id/generate")
+ String generateCampaignId();
+
+ @PostExchange("/api/campaign/csr/create-draft")
+ OperationResult createCampaignDraft(@RequestBody Campaign request);
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/MemberTierApiClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/MemberTierApiClient.java
new file mode 100644
index 0000000..1292d79
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/MemberTierApiClient.java
@@ -0,0 +1,12 @@
+package dev.sonpx.loyalty.mcp.client.api;
+
+import dev.sonpx.loyalty.mcp.client.model.MemberTier;
+import org.springframework.web.service.annotation.GetExchange;
+
+import java.util.List;
+
+public interface MemberTierApiClient {
+
+ @GetExchange("/svc/reward/api/decide-tier-view/list?page=0&size=100&status.in=A")
+ List listMemberTiers();
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/PointPoolApiClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/PointPoolApiClient.java
new file mode 100644
index 0000000..ee8f0bb
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/PointPoolApiClient.java
@@ -0,0 +1,12 @@
+package dev.sonpx.loyalty.mcp.client.api;
+
+import dev.sonpx.loyalty.mcp.client.model.PointPool;
+import org.springframework.web.service.annotation.GetExchange;
+
+import java.util.List;
+
+public interface PointPoolApiClient {
+
+ @GetExchange("/api/pool-definition/all")
+ List listPointPools();
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/Campaign.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/Campaign.java
new file mode 100644
index 0000000..6c1c90f
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/Campaign.java
@@ -0,0 +1,7 @@
+package dev.sonpx.loyalty.mcp.client.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record Campaign(String campaignId, String name, String targetTierId, String rewardPoolId, String status) {
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/CreateCampaignRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/CreateCampaignRequest.java
new file mode 100644
index 0000000..f481fb5
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/CreateCampaignRequest.java
@@ -0,0 +1,8 @@
+package dev.sonpx.loyalty.mcp.client.model;
+
+public record CreateCampaignRequest(String campaignId, String name, String targetTierId, String rewardPoolId) {
+
+ public CreateCampaignRequest(String name, String targetTierId, String rewardPoolId) {
+ this(null, name, targetTierId, rewardPoolId);
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/MemberTier.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/MemberTier.java
new file mode 100644
index 0000000..94d2b97
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/MemberTier.java
@@ -0,0 +1,12 @@
+package dev.sonpx.loyalty.mcp.client.model;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record MemberTier(
+ @JsonProperty("key") @JsonAlias("tierId") String tierId,
+ @JsonAlias("name") String tierName,
+ Integer requiredPoints) {
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/OperationResult.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/OperationResult.java
new file mode 100644
index 0000000..e2a3904
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/OperationResult.java
@@ -0,0 +1,7 @@
+package dev.sonpx.loyalty.mcp.client.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record OperationResult(String errorCode, String message, T entity) {
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/PointPool.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/PointPool.java
new file mode 100644
index 0000000..a780d4a
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/PointPool.java
@@ -0,0 +1,9 @@
+package dev.sonpx.loyalty.mcp.client.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import java.math.BigDecimal;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record PointPool(String poolId, String poolName, BigDecimal balance) {
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/attribute/StaticAttributeDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/attribute/StaticAttributeDto.java
new file mode 100644
index 0000000..0a4f1fc
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/attribute/StaticAttributeDto.java
@@ -0,0 +1,13 @@
+package dev.sonpx.loyalty.mcp.client.model.attribute;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class StaticAttributeDto {
+ private String attributeId;
+ private String attributeType;
+ private String attributeValue;
+ private String status;
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignDto.java
new file mode 100644
index 0000000..b93fe64
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignDto.java
@@ -0,0 +1,26 @@
+package dev.sonpx.loyalty.mcp.client.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CampaignDto {
+ private String campaignId;
+ private String name;
+ private String targetTierId;
+ private String rewardPoolId;
+ private String status;
+ private String description;
+
+ private Map extraData = new LinkedHashMap<>();
+
+ @JsonAnySetter
+ public void addExtraData(String key, Object value) {
+ this.extraData.put(key, value);
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignFormulaOneDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignFormulaOneDto.java
new file mode 100644
index 0000000..9194c2e
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignFormulaOneDto.java
@@ -0,0 +1,11 @@
+package dev.sonpx.loyalty.mcp.client.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CampaignFormulaOneDto {
+ private Double awardPerTxnAmt;
+ private Double txnAmt;
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignRuleDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignRuleDto.java
new file mode 100644
index 0000000..5e64954
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignRuleDto.java
@@ -0,0 +1,57 @@
+package dev.sonpx.loyalty.mcp.client.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Data;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CampaignRuleDto {
+ private ReferenceData campaignId;
+ private String ruleId;
+ private String ruleName;
+ private RuleTypeEnum ruleType;
+ private String description;
+ private OffsetDateTime effectiveFrom;
+ private OffsetDateTime effectiveTo;
+ private ReferenceData poolId;
+ private CampaignFormulaOneDto campaignFormulaOne;
+ private List formulaSequence;
+
+ public enum RuleTypeEnum {
+ AWD("AWD"),
+ RED("RED"),
+ ADJ("ADJ"),
+ REP("REP"),
+ MAWD("MAWD"),
+ CEP("CEP");
+
+ private String value;
+
+ RuleTypeEnum(String value) {
+ this.value = value;
+ }
+
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ public static RuleTypeEnum fromValue(String value) {
+ for (RuleTypeEnum b : RuleTypeEnum.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ throw new IllegalArgumentException("Unexpected value '" + value + "'");
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/OperationResultCampaignRuleDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/OperationResultCampaignRuleDto.java
new file mode 100644
index 0000000..4ed1a43
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/OperationResultCampaignRuleDto.java
@@ -0,0 +1,13 @@
+package dev.sonpx.loyalty.mcp.client.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OperationResultCampaignRuleDto {
+ private Boolean success;
+ private String errorCode;
+ private String errorMessage;
+ private CampaignRuleDto data;
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/ReferenceData.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/ReferenceData.java
new file mode 100644
index 0000000..ee060f6
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/ReferenceData.java
@@ -0,0 +1,10 @@
+package dev.sonpx.loyalty.mcp.client.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ReferenceData {
+ private String code;
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ApiClientConfig.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ApiClientConfig.java
new file mode 100644
index 0000000..40b11f7
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ApiClientConfig.java
@@ -0,0 +1,105 @@
+package dev.sonpx.loyalty.mcp.config;
+
+import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient;
+import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient;
+import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.support.RestClientAdapter;
+import org.springframework.web.service.invoker.HttpServiceProxyFactory;
+
+/**
+ * Configures OAuth2-enabled RestClient instances and Spring HTTP interface clients.
+ */
+@Configuration
+public class ApiClientConfig {
+
+ @Value("${loyalty.core.base-url}")
+ private String coreBaseUrl;
+
+
+
+ @Bean
+ public RestClient loyaltyRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
+ return RestClient.builder()
+ .baseUrl(coreBaseUrl)
+ .requestInterceptor((request, body, execution) -> {
+ Authentication principal =
+ new AnonymousAuthenticationToken(
+ "key", "loyalty-agent-service",
+ AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+ OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
+ .withClientRegistrationId("keycloak")
+ .principal(principal)
+ .build();
+ try {
+ OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
+ if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
+ request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to authorize loyalty API request", e);
+ }
+ return execution.execute(request, body);
+ })
+ .build();
+ }
+
+ @Bean
+ public RestClient rewardRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
+ return RestClient.builder()
+ .baseUrl(coreBaseUrl + "/svc/reward")
+ .requestInterceptor((request, body, execution) -> {
+ org.springframework.security.core.Authentication principal =
+ new org.springframework.security.authentication.AnonymousAuthenticationToken(
+ "key", "loyalty-agent-service",
+ org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+ OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
+ .withClientRegistrationId("keycloak")
+ .principal(principal)
+ .build();
+ try {
+ OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
+ if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
+ request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to authorize reward API request", e);
+ }
+ return execution.execute(request, body);
+ })
+ .build();
+ }
+
+ @Bean
+ public MemberTierApiClient memberTierApiClient(@Qualifier("loyaltyRestClient") RestClient loyaltyRestClient) {
+ return createClient(loyaltyRestClient, MemberTierApiClient.class);
+ }
+
+ @Bean
+ public PointPoolApiClient pointPoolApiClient(@Qualifier("rewardRestClient") RestClient rewardRestClient) {
+ return createClient(rewardRestClient, PointPoolApiClient.class);
+ }
+
+ @Bean
+ public CampaignApiClient campaignApiClient(@Qualifier("rewardRestClient") RestClient rewardRestClient) {
+ return createClient(rewardRestClient, CampaignApiClient.class);
+ }
+
+ private T createClient(RestClient restClient, Class clientType) {
+ HttpServiceProxyFactory factory = HttpServiceProxyFactory
+ .builderFor(RestClientAdapter.create(restClient))
+ .build();
+ return factory.createClient(clientType);
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/AsyncTimeoutFilter.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/AsyncTimeoutFilter.java
new file mode 100644
index 0000000..18c6e74
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/AsyncTimeoutFilter.java
@@ -0,0 +1,24 @@
+package dev.sonpx.loyalty.mcp.config;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+@Component
+public class AsyncTimeoutFilter extends OncePerRequestFilter {
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+ filterChain.doFilter(request, response);
+ if (request.isAsyncStarted()) {
+ // Set async timeout to 24 hours (86400000 ms) to prevent SSE connections from dropping
+ request.getAsyncContext().setTimeout(86400000L);
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/McpServerConfig.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/McpServerConfig.java
new file mode 100644
index 0000000..b2032ef
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/McpServerConfig.java
@@ -0,0 +1,34 @@
+package dev.sonpx.loyalty.mcp.config;
+
+import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient;
+import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient;
+import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient;
+
+import dev.sonpx.loyalty.mcp.tool.LoyaltyAgentTools;
+import dev.sonpx.loyalty.mcp.tool.reward.CampaignRuleTools;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestClient;
+
+@Configuration
+public class McpServerConfig {
+
+ @Bean
+ public LoyaltyAgentTools loyaltyAgentTools(
+ MemberTierApiClient memberTierApiClient,
+ PointPoolApiClient pointPoolApiClient,
+ CampaignApiClient campaignApiClient,
+ @org.springframework.beans.factory.annotation.Qualifier("loyaltyRestClient") RestClient restClient) {
+ return new LoyaltyAgentTools(memberTierApiClient, pointPoolApiClient, campaignApiClient, restClient);
+ }
+
+ @Bean
+ public org.springframework.ai.tool.ToolCallbackProvider loyaltyToolsProvider(
+ LoyaltyAgentTools loyaltyAgentTools,
+ CampaignRuleTools campaignRuleTools,
+ dev.sonpx.loyalty.mcp.tool.reward.CampaignTools campaignTools) {
+ return org.springframework.ai.tool.method.MethodToolCallbackProvider.builder()
+ .toolObjects(loyaltyAgentTools, campaignRuleTools, campaignTools)
+ .build();
+ }
+}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/SecurityConfig.java
similarity index 98%
rename from src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java
rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/SecurityConfig.java
index b15bd90..63cbd7e 100644
--- a/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/SecurityConfig.java
@@ -1,4 +1,4 @@
-package dev.sonpx.loyalty.agent.config;
+package dev.sonpx.loyalty.mcp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DependencyValidator.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DependencyValidator.java
new file mode 100644
index 0000000..a7f9d6f
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DependencyValidator.java
@@ -0,0 +1,44 @@
+package dev.sonpx.loyalty.mcp.draft;
+
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto;
+import dev.sonpx.loyalty.mcp.draft.model.DraftSession;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DependencyValidator {
+
+ public void validateCampaignRequiredFields(CampaignDto campaign) {
+ if (campaign == null) {
+ throw new IllegalArgumentException("Campaign draft is empty");
+ }
+ if (campaign.getCampaignId() == null || campaign.getCampaignId().isBlank()) {
+ throw new IllegalArgumentException("Campaign ID is required");
+ }
+ if (campaign.getName() == null || campaign.getName().isBlank()) {
+ throw new IllegalArgumentException("Campaign name is required");
+ }
+ }
+
+ public void validateRulePrerequisites(DraftSession session, String targetCampaignId, String transactionCode, boolean needsPoolId, String poolId) {
+ if (targetCampaignId == null || targetCampaignId.isBlank()) {
+ throw new IllegalArgumentException("Target Campaign ID is required to add a rule");
+ }
+
+ // In a real system, we might check if targetCampaignId matches the one in draft,
+ // or check the Core API if the campaign exists.
+
+ if (transactionCode == null || transactionCode.isBlank()) {
+ throw new IllegalArgumentException("Transaction Code is required");
+ }
+
+ if (needsPoolId && (poolId == null || poolId.isBlank())) {
+ throw new IllegalArgumentException("Pool ID is required for the selected rule flow");
+ }
+ }
+
+ public void validateSopPrerequisites(String poolId) {
+ if (poolId == null || poolId.isBlank()) {
+ throw new IllegalArgumentException("Existing Pool ID is required to create a Statement Output Pool");
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DraftSessionManager.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DraftSessionManager.java
new file mode 100644
index 0000000..311e31c
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DraftSessionManager.java
@@ -0,0 +1,58 @@
+package dev.sonpx.loyalty.mcp.draft;
+
+import dev.sonpx.loyalty.mcp.draft.model.DraftSession;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+@Component
+public class DraftSessionManager {
+
+ private final Map sessions = new ConcurrentHashMap<>();
+ private static final Duration TTL = Duration.ofHours(1);
+
+ public DraftSession getSession(String conversationId) {
+ if (conversationId == null) {
+ throw new IllegalArgumentException("conversationId cannot be null");
+ }
+ cleanupExpiredSessions();
+ return sessions.compute(conversationId, (id, session) -> {
+ if (session == null || isExpired(session)) {
+ return new DraftSession(id);
+ }
+ session.setLastUpdated(Instant.now());
+ return session;
+ });
+ }
+
+ public void updateSession(String conversationId, Consumer updater) {
+ if (conversationId == null) {
+ throw new IllegalArgumentException("conversationId cannot be null");
+ }
+ cleanupExpiredSessions();
+ sessions.compute(conversationId, (id, session) -> {
+ if (session == null || isExpired(session)) {
+ session = new DraftSession(id);
+ }
+ updater.accept(session);
+ session.setLastUpdated(Instant.now());
+ return session;
+ });
+ }
+
+ public void removeSession(String conversationId) {
+ sessions.remove(conversationId);
+ }
+
+ private boolean isExpired(DraftSession session) {
+ return Instant.now().isAfter(session.getLastUpdated().plus(TTL));
+ }
+
+ private void cleanupExpiredSessions() {
+ sessions.entrySet().removeIf(entry -> isExpired(entry.getValue()));
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/CampaignDraft.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/CampaignDraft.java
new file mode 100644
index 0000000..574ccc5
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/CampaignDraft.java
@@ -0,0 +1,14 @@
+package dev.sonpx.loyalty.mcp.draft.model;
+
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto;
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class CampaignDraft {
+ private CampaignDto campaign = new CampaignDto();
+ private List rules = new ArrayList<>();
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/DraftSession.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/DraftSession.java
new file mode 100644
index 0000000..b89d45d
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/DraftSession.java
@@ -0,0 +1,20 @@
+package dev.sonpx.loyalty.mcp.draft.model;
+
+import lombok.Data;
+
+import java.time.Instant;
+
+@Data
+public class DraftSession {
+ private String conversationId;
+ private Instant lastUpdated;
+ private CampaignDraft campaignDraft;
+ private PoolDraft poolDraft;
+
+ public DraftSession(String conversationId) {
+ this.conversationId = conversationId;
+ this.lastUpdated = Instant.now();
+ this.campaignDraft = new CampaignDraft();
+ this.poolDraft = new PoolDraft();
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/PoolDraft.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/PoolDraft.java
new file mode 100644
index 0000000..866fe41
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/PoolDraft.java
@@ -0,0 +1,11 @@
+package dev.sonpx.loyalty.mcp.draft.model;
+
+import lombok.Data;
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class PoolDraft {
+ private Map poolDefinition = new HashMap<>();
+ private Map statementOutputPool = new HashMap<>();
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/McpResponseWrapper.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/McpResponseWrapper.java
new file mode 100644
index 0000000..bc9e5d0
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/McpResponseWrapper.java
@@ -0,0 +1,15 @@
+package dev.sonpx.loyalty.mcp.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class McpResponseWrapper {
+ private T data;
+ private Presentation presentation;
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Presentation.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Presentation.java
new file mode 100644
index 0000000..027c808
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Presentation.java
@@ -0,0 +1,15 @@
+package dev.sonpx.loyalty.mcp.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Presentation {
+ private String type; // e.g. "markdown"
+ private String content; // The formatted string
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentTools.java
new file mode 100644
index 0000000..14bd9c9
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentTools.java
@@ -0,0 +1,77 @@
+package dev.sonpx.loyalty.mcp.tool;
+
+import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient;
+import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient;
+import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient;
+import dev.sonpx.loyalty.mcp.client.model.attribute.StaticAttributeDto;
+
+import dev.sonpx.loyalty.mcp.client.model.Campaign;
+import dev.sonpx.loyalty.mcp.client.model.CreateCampaignRequest;
+import dev.sonpx.loyalty.mcp.client.model.MemberTier;
+import dev.sonpx.loyalty.mcp.client.model.OperationResult;
+import dev.sonpx.loyalty.mcp.client.model.PointPool;
+import org.springframework.ai.tool.annotation.Tool;
+
+import java.util.List;
+import org.springframework.core.ParameterizedTypeReference;
+
+public class LoyaltyAgentTools {
+
+ private final MemberTierApiClient memberTierApiClient;
+ private final PointPoolApiClient pointPoolApiClient;
+ private final CampaignApiClient campaignApiClient;
+ private final org.springframework.web.client.RestClient restClient;
+
+ public LoyaltyAgentTools(MemberTierApiClient memberTierApiClient, PointPoolApiClient pointPoolApiClient, CampaignApiClient campaignApiClient, org.springframework.web.client.RestClient restClient) {
+ this.memberTierApiClient = memberTierApiClient;
+ this.pointPoolApiClient = pointPoolApiClient;
+ this.campaignApiClient = campaignApiClient;
+ this.restClient = restClient;
+ }
+
+ @Tool(description = "List all available Member Tiers. Always use this to verify a Tier exists before creating a Campaign.")
+ public List listMemberTiers() {
+ return memberTierApiClient.listMemberTiers();
+ }
+
+ @Tool(description = "List all available Point Pools. Always use this to verify a Pool exists and has sufficient balance before creating a Campaign.")
+ public List listPointPools() {
+ return pointPoolApiClient.listPointPools();
+ }
+
+ @Tool(description = "Create a new Campaign. ONLY use this when explicitly asked to create or add a new campaign. Do not use this to fetch information about existing campaigns. This requires a valid targetTierId and rewardPoolId, which MUST be verified beforehand using listMemberTiers and listPointPools.")
+ public Campaign createCampaign(CreateCampaignRequest request) {
+ String campaignId = request.campaignId();
+ if (campaignId == null || campaignId.isBlank()) {
+ campaignId = campaignApiClient.generateCampaignId();
+ }
+
+ Campaign draft = new Campaign(
+ campaignId,
+ request.name(),
+ request.targetTierId(),
+ request.rewardPoolId(),
+ null
+ );
+ OperationResult result = campaignApiClient.createCampaignDraft(draft);
+ if (result == null || result.entity() == null) {
+ String message = result != null ? result.message() : "empty response";
+ throw new IllegalStateException("Campaign create-draft did not return an entity: " + message);
+ }
+ return result.entity();
+ }
+
+ @Tool(description = "List all available Static Attributes. Use this tool when you need to find configurations, properties, or static values like tier types or currencies.")
+ public List listStaticAttributes() {
+ try {
+ String url = "/svc/attribute/api/static-attribute/csr/view/list?sort=lastApproveDate,desc&sort=lastUpdateDate,desc&page=0&size=10&status.in=A";
+ return restClient.get()
+ .uri(url)
+ .retrieve()
+ .body(new ParameterizedTypeReference<>() {
+ });
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to fetch static attributes", e);
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreatePoolDefinitionRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreatePoolDefinitionRequest.java
new file mode 100644
index 0000000..c945e5b
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreatePoolDefinitionRequest.java
@@ -0,0 +1,20 @@
+package dev.sonpx.loyalty.mcp.tool.model.pool;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+
+public record CreatePoolDefinitionRequest(
+ @JsonPropertyDescription("Conversation ID from the agent")
+ String conversationId,
+
+ @JsonPropertyDescription("Pool ID (max 10 chars)")
+ String poolId,
+
+ @JsonPropertyDescription("Pool Name")
+ String poolName,
+
+ @JsonPropertyDescription("Pool Type (e.g. STANDARD, PROMOTIONAL)")
+ String poolType,
+
+ @JsonPropertyDescription("Currency Code")
+ String currencyCode
+) {}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreateSopRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreateSopRequest.java
new file mode 100644
index 0000000..d4952b0
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreateSopRequest.java
@@ -0,0 +1,20 @@
+package dev.sonpx.loyalty.mcp.tool.model.pool;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+
+public record CreateSopRequest(
+ @JsonPropertyDescription("Conversation ID from the agent")
+ String conversationId,
+
+ @JsonPropertyDescription("Statement Output Pool ID")
+ String sopId,
+
+ @JsonPropertyDescription("Reference Pool ID (must exist)")
+ String poolId,
+
+ @JsonPropertyDescription("SOP Name")
+ String name,
+
+ @JsonPropertyDescription("Statement Type")
+ String statementType
+) {}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/AddRuleToCampaignRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/AddRuleToCampaignRequest.java
new file mode 100644
index 0000000..75c8655
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/AddRuleToCampaignRequest.java
@@ -0,0 +1,23 @@
+package dev.sonpx.loyalty.mcp.tool.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+
+public record AddRuleToCampaignRequest(
+ @JsonPropertyDescription("Conversation ID from the agent")
+ String conversationId,
+
+ @JsonPropertyDescription("Target Campaign ID")
+ String targetCampaignId,
+
+ @JsonPropertyDescription("Transaction Code to trigger the rule")
+ String transactionCode,
+
+ @JsonPropertyDescription("Whether this rule flow requires a Pool ID")
+ boolean needsPoolId,
+
+ @JsonPropertyDescription("Pool ID for the rule (if needed)")
+ String poolId,
+
+ @JsonPropertyDescription("Rule details")
+ CreateCampaignRuleToolRequest ruleDetails
+) {}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/CreateCampaignRuleToolRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/CreateCampaignRuleToolRequest.java
new file mode 100644
index 0000000..460597c
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/CreateCampaignRuleToolRequest.java
@@ -0,0 +1,41 @@
+package dev.sonpx.loyalty.mcp.tool.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import java.time.OffsetDateTime;
+
+public record CreateCampaignRuleToolRequest(
+ @JsonPropertyDescription("ID of the campaign this rule belongs to")
+ String campaignId,
+
+ @JsonPropertyDescription("Unique identifier for the rule (max 10 chars)")
+ String ruleId,
+
+ @JsonPropertyDescription("Name of the rule")
+ String ruleName,
+
+ @JsonPropertyDescription("Type of the rule (e.g., AWD for Award, RED for Redemption, ADJ for Adjustment)")
+ String ruleType,
+
+ @JsonPropertyDescription("Description of the rule")
+ String description,
+
+ @JsonPropertyDescription("Effective start date of the rule")
+ OffsetDateTime effectiveFrom,
+
+ @JsonPropertyDescription("Effective end date of the rule")
+ OffsetDateTime effectiveTo,
+
+ @JsonPropertyDescription("ID of the Point Pool to be updated by this rule")
+ String poolId,
+
+ @JsonPropertyDescription("Formula One configuration for basic points awarding (points per txn amount)")
+ FormulaOneConfig formulaOne
+) {
+ public record FormulaOneConfig(
+ @JsonPropertyDescription("Amount of points awarded per transaction amount")
+ Double awardPerTxnAmt,
+
+ @JsonPropertyDescription("Required transaction amount to receive the award")
+ Double txnAmt
+ ) {}
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/ListCampaignRuleToolRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/ListCampaignRuleToolRequest.java
new file mode 100644
index 0000000..a9341df
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/ListCampaignRuleToolRequest.java
@@ -0,0 +1,11 @@
+package dev.sonpx.loyalty.mcp.tool.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public record ListCampaignRuleToolRequest(
+ String campaignId,
+ Integer page,
+ Integer size
+) {
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/SubmitCampaignDraftRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/SubmitCampaignDraftRequest.java
new file mode 100644
index 0000000..72de595
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/SubmitCampaignDraftRequest.java
@@ -0,0 +1,8 @@
+package dev.sonpx.loyalty.mcp.tool.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+
+public record SubmitCampaignDraftRequest(
+ @JsonPropertyDescription("Conversation ID from the agent")
+ String conversationId
+) {}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignDraftRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignDraftRequest.java
new file mode 100644
index 0000000..6757e52
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignDraftRequest.java
@@ -0,0 +1,17 @@
+package dev.sonpx.loyalty.mcp.tool.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+
+public record UpdateCampaignDraftRequest(
+ @JsonPropertyDescription("Conversation ID from the agent")
+ String conversationId,
+
+ @JsonPropertyDescription("Campaign ID")
+ String campaignId,
+
+ @JsonPropertyDescription("Campaign Name")
+ String name,
+
+ @JsonPropertyDescription("Target Tier ID")
+ String targetTierId
+) {}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignRuleToolRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignRuleToolRequest.java
new file mode 100644
index 0000000..b67cec2
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignRuleToolRequest.java
@@ -0,0 +1,34 @@
+package dev.sonpx.loyalty.mcp.tool.model.reward;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import java.time.OffsetDateTime;
+
+public record UpdateCampaignRuleToolRequest(
+ @JsonPropertyDescription("ID of the campaign this rule belongs to")
+ String campaignId,
+
+ @JsonPropertyDescription("Unique identifier for the rule (max 10 chars)")
+ String ruleId,
+
+ @JsonPropertyDescription("Name of the rule")
+ String ruleName,
+
+ @JsonPropertyDescription("Type of the rule (e.g., AWD for Award, RED for Redemption, ADJ for Adjustment)")
+ String ruleType,
+
+ @JsonPropertyDescription("Description of the rule")
+ String description,
+
+ @JsonPropertyDescription("Effective start date of the rule")
+ OffsetDateTime effectiveFrom,
+
+ @JsonPropertyDescription("Effective end date of the rule")
+ OffsetDateTime effectiveTo,
+
+ @JsonPropertyDescription("ID of the Point Pool to be updated by this rule")
+ String poolId,
+
+ @JsonPropertyDescription("Formula One configuration for basic points awarding (points per txn amount)")
+ CreateCampaignRuleToolRequest.FormulaOneConfig formulaOne
+) {
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/pool/PointPoolTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/pool/PointPoolTools.java
new file mode 100644
index 0000000..8ab5e93
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/pool/PointPoolTools.java
@@ -0,0 +1,86 @@
+package dev.sonpx.loyalty.mcp.tool.pool;
+
+import dev.sonpx.loyalty.mcp.draft.DependencyValidator;
+import dev.sonpx.loyalty.mcp.draft.DraftSessionManager;
+import dev.sonpx.loyalty.mcp.tool.model.pool.CreatePoolDefinitionRequest;
+import dev.sonpx.loyalty.mcp.tool.model.pool.CreateSopRequest;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestClient;
+
+import java.util.Map;
+
+@Component
+public class PointPoolTools {
+
+ private final DraftSessionManager draftSessionManager;
+ private final DependencyValidator dependencyValidator;
+ private final RestClient restClient;
+
+ public PointPoolTools(DraftSessionManager draftSessionManager, DependencyValidator dependencyValidator, @Qualifier("rewardRestClient") RestClient restClient) {
+ this.draftSessionManager = draftSessionManager;
+ this.dependencyValidator = dependencyValidator;
+ this.restClient = restClient;
+ }
+
+ @Tool(description = "Create a Pool Definition. This builds a PoolDefinitionDto and submits it via Core API.")
+ public Map createPoolDefinition(CreatePoolDefinitionRequest request) {
+ if (request.conversationId() == null || request.conversationId().isBlank()) {
+ throw new IllegalArgumentException("conversationId is required");
+ }
+
+ // Store in draft
+ draftSessionManager.updateSession(request.conversationId(), session -> {
+ Map poolDef = session.getPoolDraft().getPoolDefinition();
+ poolDef.put("poolId", request.poolId());
+ poolDef.put("poolName", request.poolName());
+ poolDef.put("poolType", request.poolType());
+ poolDef.put("currencyCode", request.currencyCode());
+ });
+
+ // Submit via Core API
+ try {
+ return restClient.post()
+ .uri("/api/pool-definition/csr/create-draft")
+ .body(request)
+ .retrieve()
+ .body(new ParameterizedTypeReference<>() {});
+ } catch (Exception e) {
+ e.printStackTrace();
+ return Map.of("error", "Failed to create pool definition draft via Core API: " + e.getMessage());
+ }
+ }
+
+ @Tool(description = "Create a Statement Output Pool (SOP). This builds a StatementOutputPoolDto and requires an existing Pool ID.")
+ public Map createSop(CreateSopRequest request) {
+ if (request.conversationId() == null || request.conversationId().isBlank()) {
+ throw new IllegalArgumentException("conversationId is required");
+ }
+
+ // Validate prerequisite: existing Pool ID
+ dependencyValidator.validateSopPrerequisites(request.poolId());
+
+ // Store in draft
+ draftSessionManager.updateSession(request.conversationId(), session -> {
+ Map sop = session.getPoolDraft().getStatementOutputPool();
+ sop.put("sopId", request.sopId());
+ sop.put("poolId", request.poolId());
+ sop.put("name", request.name());
+ sop.put("statementType", request.statementType());
+ });
+
+ // Submit via Core API
+ try {
+ return restClient.post()
+ .uri("/api/sop/csr/create-draft")
+ .body(request)
+ .retrieve()
+ .body(new ParameterizedTypeReference<>() {});
+ } catch (Exception e) {
+ e.printStackTrace();
+ return Map.of("error", "Failed to create Statement Output Pool draft via Core API: " + e.getMessage());
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapper.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapper.java
new file mode 100644
index 0000000..3fcc441
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapper.java
@@ -0,0 +1,77 @@
+package dev.sonpx.loyalty.mcp.tool.reward;
+
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto;
+import dev.sonpx.loyalty.mcp.client.model.reward.ReferenceData;
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignFormulaOneDto;
+import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignRuleToolRequest;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+
+@Component
+public class CampaignRuleMapper {
+
+ public CampaignRuleDto toDto(CreateCampaignRuleToolRequest request) {
+ CampaignRuleDto dto = new CampaignRuleDto();
+
+ dto.setCampaignId(toReferenceData(request.campaignId()));
+ dto.setRuleId(request.ruleId());
+ dto.setRuleName(request.ruleName());
+ if (request.ruleType() != null) {
+ dto.setRuleType(CampaignRuleDto.RuleTypeEnum.fromValue(request.ruleType()));
+ }
+ dto.setDescription(request.description());
+ dto.setEffectiveFrom(request.effectiveFrom());
+ dto.setEffectiveTo(request.effectiveTo());
+ dto.setPoolId(toReferenceData(request.poolId()));
+
+ if (request.formulaOne() != null) {
+ CampaignFormulaOneDto formulaOne = new CampaignFormulaOneDto();
+ formulaOne.setAwardPerTxnAmt(request.formulaOne().awardPerTxnAmt());
+ formulaOne.setTxnAmt(request.formulaOne().txnAmt());
+ dto.setCampaignFormulaOne(formulaOne);
+
+ dto.setFormulaSequence(new ArrayList<>());
+ dto.getFormulaSequence().add("1");
+ }
+
+ return dto;
+ }
+
+ public CampaignRuleDto toDto(UpdateCampaignRuleToolRequest request) {
+ CampaignRuleDto dto = new CampaignRuleDto();
+
+ dto.setCampaignId(toReferenceData(request.campaignId()));
+ dto.setRuleId(request.ruleId());
+ dto.setRuleName(request.ruleName());
+ if (request.ruleType() != null) {
+ dto.setRuleType(CampaignRuleDto.RuleTypeEnum.fromValue(request.ruleType()));
+ }
+ dto.setDescription(request.description());
+ dto.setEffectiveFrom(request.effectiveFrom());
+ dto.setEffectiveTo(request.effectiveTo());
+ dto.setPoolId(toReferenceData(request.poolId()));
+
+ if (request.formulaOne() != null) {
+ CampaignFormulaOneDto formulaOne = new CampaignFormulaOneDto();
+ formulaOne.setAwardPerTxnAmt(request.formulaOne().awardPerTxnAmt());
+ formulaOne.setTxnAmt(request.formulaOne().txnAmt());
+ dto.setCampaignFormulaOne(formulaOne);
+
+ dto.setFormulaSequence(new ArrayList<>());
+ dto.getFormulaSequence().add("1");
+ }
+
+ return dto;
+ }
+
+ private ReferenceData toReferenceData(String code) {
+ if (code == null) {
+ return null;
+ }
+ ReferenceData ref = new ReferenceData();
+ ref.setCode(code);
+ return ref;
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleTools.java
new file mode 100644
index 0000000..2b07104
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleTools.java
@@ -0,0 +1,211 @@
+package dev.sonpx.loyalty.mcp.tool.reward;
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto;
+import dev.sonpx.loyalty.mcp.client.model.reward.OperationResultCampaignRuleDto;
+import dev.sonpx.loyalty.mcp.draft.DependencyValidator;
+import dev.sonpx.loyalty.mcp.draft.DraftSessionManager;
+import dev.sonpx.loyalty.mcp.tool.model.reward.AddRuleToCampaignRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignRuleToolRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.ListCampaignRuleToolRequest;
+import dev.sonpx.loyalty.mcp.model.McpResponseWrapper;
+import dev.sonpx.loyalty.mcp.model.Presentation;
+import dev.sonpx.loyalty.mcp.util.MarkdownGenerator;
+
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Component
+public class CampaignRuleTools {
+
+ private static final Logger log = LoggerFactory.getLogger(CampaignRuleTools.class);
+
+ private final CampaignRuleMapper campaignRuleMapper;
+ private final RestClient rewardRestClient;
+ private final DraftSessionManager draftSessionManager;
+ private final DependencyValidator dependencyValidator;
+
+ public CampaignRuleTools(
+ CampaignRuleMapper campaignRuleMapper,
+ @Qualifier("rewardRestClient") RestClient rewardRestClient,
+ DraftSessionManager draftSessionManager,
+ DependencyValidator dependencyValidator) {
+ this.campaignRuleMapper = campaignRuleMapper;
+ this.rewardRestClient = rewardRestClient;
+ this.draftSessionManager = draftSessionManager;
+ this.dependencyValidator = dependencyValidator;
+ }
+
+ @Tool(description = "Add Rule to local Campaign Draft. Validates prerequisites (Transaction Code, Pool ID) before adding.")
+ public Map addRuleToCampaign(AddRuleToCampaignRequest request) {
+ if (request.conversationId() == null || request.conversationId().isBlank()) {
+ throw new IllegalArgumentException("conversationId is required");
+ }
+
+ // Validate dependencies
+ dependencyValidator.validateRulePrerequisites(
+ draftSessionManager.getSession(request.conversationId()),
+ request.targetCampaignId(),
+ request.transactionCode(),
+ request.needsPoolId(),
+ request.poolId()
+ );
+
+ // Update local draft
+ draftSessionManager.updateSession(request.conversationId(), session -> {
+ CampaignRuleDto rule = campaignRuleMapper.toDto(request.ruleDetails());
+ dev.sonpx.loyalty.mcp.client.model.reward.ReferenceData ref = new dev.sonpx.loyalty.mcp.client.model.reward.ReferenceData();
+ ref.setCode(request.targetCampaignId());
+ rule.setCampaignId(ref);
+ session.getCampaignDraft().getRules().add(rule);
+ });
+
+ return Map.of("status", "Rule added to Campaign draft successfully", "ruleId", request.ruleDetails().ruleId());
+ }
+
+ @Tool(description = "Create a new Campaign Rule for an existing Campaign. Supports basic properties and Formula One configuration for points per transaction amount.")
+ public OperationResultCampaignRuleDto createCampaignRule(CreateCampaignRuleToolRequest request) {
+ CampaignRuleDto dto = campaignRuleMapper.toDto(request);
+ try {
+ return rewardRestClient.post()
+ .uri("/api/campaign-rule/csr/create")
+ .body(dto)
+ .retrieve()
+ .body(OperationResultCampaignRuleDto.class);
+ } catch (Exception e) {
+ log.error("Failed to create Campaign Rule", e);
+ throw new RuntimeException("Failed to create Campaign Rule: " + e.getMessage(), e);
+ }
+ }
+
+ @Tool(description = "Edit an existing Campaign Rule. You must provide all required fields (campaignId, ruleId, ruleName, etc) as this will update the entire rule configuration.")
+ public OperationResultCampaignRuleDto editCampaignRule(UpdateCampaignRuleToolRequest request) {
+ CampaignRuleDto dto = campaignRuleMapper.toDto(request);
+ try {
+ return rewardRestClient.patch()
+ .uri("/api/campaign-rule/csr/edit")
+ .body(dto)
+ .retrieve()
+ .body(OperationResultCampaignRuleDto.class);
+ } catch (Exception e) {
+ log.error("Failed to edit Campaign Rule", e);
+ throw new RuntimeException("Failed to edit Campaign Rule: " + e.getMessage(), e);
+ }
+ }
+
+ @Tool(description = "List Campaign Rules, optionally filtered by campaignId. Use this tool to get a list of campaign rules. If the response contains a `presentation.content` field, display its content exactly as provided to the user. Use `data` only for your internal reasoning.")
+ public McpResponseWrapper> listCampaignRules(
+ @org.springframework.lang.Nullable String campaignId,
+ @org.springframework.lang.Nullable Integer page,
+ @org.springframework.lang.Nullable Integer size) {
+ int pageNum = page != null ? page : 0;
+ int sizeNum = size != null ? size : 20;
+
+ try {
+ String jsonResponse = rewardRestClient.get()
+ .uri(uriBuilder -> {
+ uriBuilder.path("/api/campaign-rule/csr/list")
+ .queryParam("page", pageNum)
+ .queryParam("size", sizeNum);
+
+ if (campaignId != null && !campaignId.isEmpty()) {
+ uriBuilder.queryParam("campaignId.equals", campaignId);
+ }
+ return uriBuilder.build();
+ })
+ .retrieve()
+ .body(String.class);
+
+ // Fix missing timezone in date-time strings from backend (e.g. "2026-06-25T16:16:01" -> "2026-06-25T16:16:01Z")
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\"");
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\"");
+
+ // Try to map it manually
+ com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
+ mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
+ mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ try {
+ CampaignRuleDto[] rules = mapper.readValue(jsonResponse, CampaignRuleDto[].class);
+ return wrapCampaignRules(rules != null ? List.of(rules) : List.of());
+ } catch (Exception parseEx) {
+ System.out.println("Failed to parse as array. Trying to extract from pageable format...");
+ com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse);
+ if (root.has("content")) {
+ CampaignRuleDto[] rules = mapper.treeToValue(root.get("content"), CampaignRuleDto[].class);
+ return wrapCampaignRules(rules != null ? List.of(rules) : List.of());
+ } else if (root.has("data")) {
+ CampaignRuleDto[] rules = mapper.treeToValue(root.get("data"), CampaignRuleDto[].class);
+ return wrapCampaignRules(rules != null ? List.of(rules) : List.of());
+ }
+ throw parseEx;
+ }
+ } catch (Exception e) {
+ log.error("Failed to list Campaign Rules", e);
+ throw new RuntimeException("Failed to list Campaign Rules: " + e.getMessage(), e);
+ }
+ }
+ @Tool(description = "List Campaign Rules by Campaign Name. It finds the campaign by name and returns its rules. Use this tool when the user provides a campaign name and asks for its campaign rules. If the response contains a `presentation.content` field, display its content exactly as provided to the user. Use `data` only for your internal reasoning.")
+ public McpResponseWrapper> listCampaignRulesByCampaignName(String campaignName) {
+ try {
+ String jsonResponse = rewardRestClient.get()
+ .uri(uriBuilder -> uriBuilder
+ .path("/api/campaign/csr/list")
+ .queryParam("name.contains", campaignName)
+ .queryParam("page", 0)
+ .queryParam("size", 1)
+ .build()
+ )
+ .retrieve()
+ .body(String.class);
+
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\"");
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\"");
+
+ com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
+ mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
+ mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[] campaigns = null;
+ try {
+ campaigns = mapper.readValue(jsonResponse, dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[].class);
+ } catch (Exception parseEx) {
+ com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse);
+ if (root.has("content")) {
+ campaigns = mapper.treeToValue(root.get("content"), dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[].class);
+ } else if (root.has("data")) {
+ campaigns = mapper.treeToValue(root.get("data"), dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[].class);
+ } else {
+ throw parseEx;
+ }
+ }
+
+ if (campaigns == null || campaigns.length == 0) {
+ return wrapCampaignRules(List.of());
+ }
+
+ String campaignId = campaigns[0].getCampaignId();
+ return listCampaignRules(campaignId, 0, 100);
+ } catch (Exception e) {
+ log.error("Failed to list Campaign Rules by Campaign Name", e);
+ throw new RuntimeException("Failed to list Campaign Rules by Campaign Name: " + e.getMessage(), e);
+ }
+ }
+
+ private McpResponseWrapper> wrapCampaignRules(List data) {
+ StringBuilder markdown = new StringBuilder();
+ if (data.isEmpty()) {
+ markdown.append("No campaign rules found.");
+ } else {
+ for (CampaignRuleDto r : data) {
+ markdown.append(MarkdownGenerator.generateCampaignRuleMarkdown(r)).append("\n");
+ }
+ }
+ return new McpResponseWrapper<>(data, new Presentation("markdown", markdown.toString()));
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignTools.java
new file mode 100644
index 0000000..d9e5453
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignTools.java
@@ -0,0 +1,150 @@
+package dev.sonpx.loyalty.mcp.tool.reward;
+
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto;
+import dev.sonpx.loyalty.mcp.draft.DependencyValidator;
+import dev.sonpx.loyalty.mcp.draft.DraftSessionManager;
+import dev.sonpx.loyalty.mcp.draft.model.DraftSession;
+import dev.sonpx.loyalty.mcp.tool.model.reward.SubmitCampaignDraftRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignDraftRequest;
+import dev.sonpx.loyalty.mcp.model.McpResponseWrapper;
+import dev.sonpx.loyalty.mcp.model.Presentation;
+import dev.sonpx.loyalty.mcp.util.MarkdownGenerator;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+public class CampaignTools {
+
+ private static final Logger log = LoggerFactory.getLogger(CampaignTools.class);
+
+ private final RestClient rewardRestClient;
+ private final DraftSessionManager draftSessionManager;
+ private final DependencyValidator dependencyValidator;
+
+ public CampaignTools(
+ @Qualifier("rewardRestClient") RestClient rewardRestClient,
+ DraftSessionManager draftSessionManager,
+ DependencyValidator dependencyValidator) {
+ this.rewardRestClient = rewardRestClient;
+ this.draftSessionManager = draftSessionManager;
+ this.dependencyValidator = dependencyValidator;
+ }
+
+ @Tool(description = "List or Search Campaigns, optionally filtered by campaignId, name, or status. ALWAYS use this tool when the user asks for information, details, or status of an existing campaign. If the response contains a `presentation.content` field, display its content exactly as provided to the user. Use `data` only for your internal reasoning.")
+ public McpResponseWrapper> listCampaigns(
+ @org.springframework.lang.Nullable String campaignId,
+ @org.springframework.lang.Nullable String name,
+ @org.springframework.lang.Nullable String status,
+ @org.springframework.lang.Nullable Integer page,
+ @org.springframework.lang.Nullable Integer size) {
+ int pageNum = page != null ? page : 0;
+ int sizeNum = size != null ? size : 20;
+
+ try {
+ String jsonResponse = rewardRestClient.get()
+ .uri(uriBuilder -> {
+ uriBuilder.path("/api/campaign/csr/list")
+ .queryParam("page", pageNum)
+ .queryParam("size", sizeNum);
+
+ if (campaignId != null && !campaignId.isEmpty()) {
+ uriBuilder.queryParam("campaignId.equals", campaignId);
+ }
+ if (name != null && !name.isEmpty()) {
+ uriBuilder.queryParam("name.contains", name);
+ }
+ if (status != null && !status.isEmpty()) {
+ uriBuilder.queryParam("status.equals", status);
+ }
+ return uriBuilder.build();
+ })
+ .retrieve()
+ .body(String.class);
+
+ // Fix missing timezone in date-time strings from backend (e.g. "2026-06-25T16:16:01" -> "2026-06-25T16:16:01Z")
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\"");
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\"");
+
+ com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
+ mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
+ mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ CampaignDto[] campaigns = null;
+ try {
+ campaigns = mapper.readValue(jsonResponse, CampaignDto[].class);
+ } catch (Exception parseEx) {
+ com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse);
+ if (root.has("content")) {
+ campaigns = mapper.treeToValue(root.get("content"), CampaignDto[].class);
+ } else if (root.has("data")) {
+ campaigns = mapper.treeToValue(root.get("data"), CampaignDto[].class);
+ } else {
+ throw parseEx;
+ }
+ }
+
+ List data = campaigns != null ? List.of(campaigns) : List.of();
+ StringBuilder markdown = new StringBuilder();
+ if (data.isEmpty()) {
+ markdown.append("No campaigns found.");
+ } else {
+ for (CampaignDto c : data) {
+ markdown.append(MarkdownGenerator.generateCampaignMarkdown(c)).append("\n");
+ }
+ }
+ return new McpResponseWrapper<>(data, new Presentation("markdown", markdown.toString()));
+ } catch (Exception e) {
+ log.error("Failed to list Campaigns", e);
+ throw new RuntimeException("Failed to list Campaigns: " + e.getMessage(), e);
+ }
+ }
+
+ @Tool(description = "Update local Campaign Draft.")
+ public Map updateCampaignDraft(UpdateCampaignDraftRequest request) {
+ if (request.conversationId() == null || request.conversationId().isBlank()) {
+ throw new IllegalArgumentException("conversationId is required");
+ }
+
+ draftSessionManager.updateSession(request.conversationId(), session -> {
+ CampaignDto campaign = session.getCampaignDraft().getCampaign();
+ if (request.campaignId() != null) campaign.setCampaignId(request.campaignId());
+ if (request.name() != null) campaign.setName(request.name());
+ if (request.targetTierId() != null) campaign.setTargetTierId(request.targetTierId());
+ });
+
+ return Map.of("status", "Campaign draft updated successfully", "campaignId", request.campaignId());
+ }
+
+ @Tool(description = "Submit the completed CampaignDraft to Core API.")
+ public Map submitCampaignDraft(SubmitCampaignDraftRequest request) {
+ if (request.conversationId() == null || request.conversationId().isBlank()) {
+ throw new IllegalArgumentException("conversationId is required");
+ }
+
+ DraftSession session = draftSessionManager.getSession(request.conversationId());
+ CampaignDto campaign = session.getCampaignDraft().getCampaign();
+
+ // Validate
+ dependencyValidator.validateCampaignRequiredFields(campaign);
+
+ // Submit via Core API
+ try {
+ return rewardRestClient.post()
+ .uri("/api/campaign/csr/create-draft")
+ .body(campaign)
+ .retrieve()
+ .body(new ParameterizedTypeReference<>() {});
+ } catch (Exception e) {
+ log.error("Failed to submit Campaign draft via Core API", e);
+ return Map.of("error", "Failed to submit Campaign draft via Core API: " + e.getMessage());
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java
new file mode 100644
index 0000000..624f1d5
--- /dev/null
+++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java
@@ -0,0 +1,60 @@
+package dev.sonpx.loyalty.mcp.util;
+
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto;
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto;
+
+public class MarkdownGenerator {
+
+ public static String generateCampaignMarkdown(CampaignDto campaign) {
+ if (campaign == null) {
+ return "No campaign data available.";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("### 🏆 Campaign Details\n\n");
+ sb.append("| Field | Value |\n");
+ sb.append("| :--- | :--- |\n");
+ sb.append("| **Campaign ID** | ").append(campaign.getCampaignId()).append(" |\n");
+ sb.append("| **Name** | ").append(campaign.getName()).append(" |\n");
+ sb.append("| **Status** | ").append(campaign.getStatus() != null ? campaign.getStatus() : "N/A").append(" |\n");
+ sb.append("| **Target Tier ID** | ").append(campaign.getTargetTierId() != null ? campaign.getTargetTierId() : "N/A").append(" |\n");
+ sb.append("| **Reward Pool ID** | ").append(campaign.getRewardPoolId() != null ? campaign.getRewardPoolId() : "N/A").append(" |\n");
+
+ if (campaign.getDescription() != null) {
+ sb.append("| **Description** | ").append(campaign.getDescription()).append(" |\n");
+ }
+
+ if (campaign.getExtraData() != null && !campaign.getExtraData().isEmpty()) {
+ for (java.util.Map.Entry entry : campaign.getExtraData().entrySet()) {
+ String key = entry.getKey();
+ String formattedKey = key.substring(0, 1).toUpperCase() + key.substring(1).replaceAll("([A-Z])", " $1");
+ sb.append("| **").append(formattedKey).append("** | ").append(entry.getValue()).append(" |\n");
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String generateCampaignRuleMarkdown(CampaignRuleDto rule) {
+ if (rule == null) {
+ return "No campaign rule data available.";
+ }
+
+ String campaignCodeStr = (rule.getCampaignId() != null && rule.getCampaignId().getCode() != null) ? rule.getCampaignId().getCode() : "N/A";
+ String poolCodeStr = (rule.getPoolId() != null && rule.getPoolId().getCode() != null) ? rule.getPoolId().getCode() : "N/A";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("### 📜 Campaign Rule Details\n\n");
+ sb.append("| Field | Value |\n");
+ sb.append("| :--- | :--- |\n");
+ sb.append("| **Rule ID** | ").append(rule.getRuleId()).append(" |\n");
+ sb.append("| **Rule Name** | ").append(rule.getRuleName()).append(" |\n");
+ sb.append("| **Rule Type** | ").append(rule.getRuleType() != null ? rule.getRuleType().getValue() : "N/A").append(" |\n");
+ sb.append("| **Campaign ID** | ").append(campaignCodeStr).append(" |\n");
+ sb.append("| **Pool ID** | ").append(poolCodeStr).append(" |\n");
+ sb.append("| **Description** | ").append(rule.getDescription() != null ? rule.getDescription() : "N/A").append(" |\n");
+ sb.append("| **Effective From** | ").append(rule.getEffectiveFrom() != null ? rule.getEffectiveFrom().toString() : "N/A").append(" |\n");
+ sb.append("| **Effective To** | ").append(rule.getEffectiveTo() != null ? rule.getEffectiveTo().toString() : "N/A").append(" |\n");
+
+ return sb.toString();
+ }
+}
diff --git a/loyalty-mcp-server/src/main/resources/application.yml b/loyalty-mcp-server/src/main/resources/application.yml
new file mode 100644
index 0000000..b46e5c6
--- /dev/null
+++ b/loyalty-mcp-server/src/main/resources/application.yml
@@ -0,0 +1,37 @@
+server:
+ port: 9331
+ shutdown: immediate
+
+spring:
+ mvc:
+ async:
+ request-timeout: 3600000
+ application:
+ name: loyalty-mcp-server
+ ai:
+ mcp:
+ server:
+ sse-endpoint: /sse
+ sse-message-endpoint: /message
+ security:
+ oauth2:
+ client:
+ registration:
+ keycloak:
+ client-id: ols-cli
+ client-secret: ${KEYCLOAK_CLIENT_SECRET:1jV8NSAeybIqmUK0AWhI8hgdo1q5itj7}
+ authorization-grant-type: client_credentials
+ provider:
+ keycloak:
+ token-uri: ${KEYCLOAK_TOKEN_URI:http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token}
+
+loyalty:
+ core:
+ base-url: ${LOYALTY_CORE_BASE_URL:http://192.168.99.242:8081}
+
+logging:
+ level:
+ root: warn
+ org.springframework.ai: INFO
+ org.springframework.web: INFO
+ dev.sonpx.loyalty: DEBUG
diff --git a/src/main/resources/specs/attribute.json b/loyalty-mcp-server/src/main/resources/specs/attribute.json
similarity index 97%
rename from src/main/resources/specs/attribute.json
rename to loyalty-mcp-server/src/main/resources/specs/attribute.json
index cfc0af1..6189eb7 100644
--- a/src/main/resources/specs/attribute.json
+++ b/loyalty-mcp-server/src/main/resources/specs/attribute.json
@@ -1,5622 +1,5622 @@
-{
- "openapi": "3.1.0",
- "info": {
- "title": "OpenAPI definition",
- "version": "v0"
- },
- "servers": [
- {
- "url": "http://attribute-service:8080",
- "description": "Generated server url"
- }
- ],
- "tags": [
- {
- "name": "Mapping Matrix",
- "description": "Context Mapping Matrix APIs"
- }
- ],
- "paths": {
- "/api/static-attribute/csr/create": {
- "post": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "create",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/create-draft": {
- "post": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "createDraft",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/create": {
- "post": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "create_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/create-draft": {
- "post": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "createDraft_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/edit": {
- "post": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "editCustomerAttributeValue",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeRequestVo"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/dynamic/update": {
- "post": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "updateAttributeValue",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UpdateAttributeValueVo"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/UpdateAttributeValueVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/create": {
- "post": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "create_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/create-draft": {
- "post": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "createDraft_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/reject": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "reject",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RejectRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/edit": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "edit",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/edit-draft": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "editDraft",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/bulk-upload": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "bulkUpload",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkUploadRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/static-attribute/csr/bulk-reject": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "bulkReject",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/static-attribute/csr/bulk-approve": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "bulkApprove",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/static-attribute/csr/approve": {
- "patch": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "approve",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ApproveRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/reject": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "reject_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RejectRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/edit": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "edit_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/edit-draft": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "editDraft_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/bulk-upload": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "bulkUpload_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkUploadRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/bulk-reject": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "bulkReject_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/bulk-approve": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "bulkApprove_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/approve": {
- "patch": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "approve_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ApproveRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/reject": {
- "patch": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "rejectCustomerAttributeValue",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RejectRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/approve": {
- "patch": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "approveCustomerAttributeValue",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ApproveRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/reject": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "reject_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RejectRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/edit": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "edit_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/edit-draft": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "editDraft_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/bulk-upload": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "bulkUpload_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkUploadRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/attribute-group/csr/bulk-reject": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "bulkReject_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/attribute-group/csr/bulk-approve": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "bulkApprove_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BulkRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- }
- }
- },
- "/api/attribute-group/csr/approve": {
- "patch": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "approve_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ApproveRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/{attributeId}": {
- "get": {
- "tags": [
- "static-attribute-controller"
- ],
- "operationId": "findStaticAttributeById",
- "parameters": [
- {
- "name": "attributeId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/{recordNo}": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "findByRecordNo",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/{group}/list": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "listByGroupCode",
- "parameters": [
- {
- "name": "group",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/view/list": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "findViewByCriteria",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeViewCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/view/count": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "findViewByCriteria_1",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeViewCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/rejection-reason/{recordNo}": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "rejectionReason",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/RejectionReasonVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/list": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "getAll",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/id": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "findActiveByIds",
- "parameters": [
- {
- "name": "params",
- "in": "query",
- "required": true,
- "schema": {
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/id/{id}": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "findActiveById",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/id/generate": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "generateId",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/id/check/{id}": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "checkId",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "pattern": "^(?!.*__)[A-Z0-9][A-Z0-9_]*$"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/history/{recordNo}": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "findHistoryByRecordNo",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/history/list": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "getAllHistory",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/count": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "count",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/StaticAttributeCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/context/{contextId}/list": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "getAllByContextId",
- "parameters": [
- {
- "name": "contextId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- },
- {
- "name": "search",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/attribute-type/list": {
- "get": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "getAllAttributeType",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ReferenceData"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/mapping-matrix": {
- "get": {
- "tags": [
- "Mapping Matrix"
- ],
- "summary": "Get mapping matrix",
- "description": "Returns mapping matrix based on object type (Static Attribute or Entity)",
- "operationId": "getMappingMatrix",
- "parameters": [
- {
- "name": "objectType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "pattern": "STATIC_ATTRIBUTE|ENTITY"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/MappingMatrixResponseDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/{attributeId}": {
- "get": {
- "tags": [
- "dynamic-attribute-controller"
- ],
- "operationId": "findAttributeById",
- "parameters": [
- {
- "name": "attributeId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/list-by-data-type": {
- "get": {
- "tags": [
- "dynamic-attribute-controller"
- ],
- "operationId": "getListDynamicAttributeByDataType",
- "parameters": [
- {
- "name": "dataType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "STRING",
- "NUMBER",
- "DATE",
- "BOOLEAN",
- "TIME"
- ]
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeVo"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/list-active": {
- "get": {
- "tags": [
- "dynamic-attribute-controller"
- ],
- "operationId": "getListActive",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeVo"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/{recordNo}": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "findByRecordNo_1",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/view/list": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "findViewByCriteria_2",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeViewCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/view/count": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "countViewByCriteria",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeViewCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/rejection-reason/{recordNo}": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "rejectionReason_1",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/RejectionReasonVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/list": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "getAll_1",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/id": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "findActiveByIds_1",
- "parameters": [
- {
- "name": "params",
- "in": "query",
- "required": true,
- "schema": {
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/id/{id}": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "findActiveById_1",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/id/generate": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "generateId_1",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/id/check/{id}": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "checkId_1",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "pattern": "^(?!.*__)[A-Z0-9][A-Z0-9_]*$"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/history/{recordNo}": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "findHistoryByRecordNo_1",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/history/list": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "getAllHistory_1",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/count": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "count_1",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/context/{contextId}/list": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "getAllByContextId_1",
- "parameters": [
- {
- "name": "contextId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- },
- {
- "name": "search",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/check": {
- "get": {
- "tags": [
- "dynamic-attribute-controller"
- ],
- "operationId": "checkAttribute",
- "parameters": [
- {
- "name": "attributeId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/check-with-data-type": {
- "get": {
- "tags": [
- "dynamic-attribute-controller"
- ],
- "operationId": "checkAttributeWithDataType",
- "parameters": [
- {
- "name": "attributeId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "dataType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "STRING",
- "NUMBER",
- "DATE",
- "BOOLEAN",
- "TIME"
- ]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/attribute-type/list": {
- "get": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "getAllAttributeType_1",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ReferenceData"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/{recordNo}": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "getCustomerAttributeValueChange",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/{recordNo}/csn/{csn}": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "getCustomerAttributeValueChange_1",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- },
- {
- "name": "csn",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/list": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "listCustomerAttributeValueChanges",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/history/{recordNo}": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "findHistoryByRecordNo_2",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/history/list": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "getAllHistory_2",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/detail": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "getActiveDetail",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeValueDetailCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeValueDetailVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/detail/add-on/list": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "listActiveDetailAddOns",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/DynamicAttributeValueAddOnCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/DynamicAttributeValueAddOnVo"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/count": {
- "get": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "count_2",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/value": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "findByEntityAndEntityIdAndAttrIdAndDate",
- "parameters": [
- {
- "name": "attributeId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "entityId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "baseDate",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "format": "date-time"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeValueVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/reference/values": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "getAttributeReferenceValues",
- "parameters": [
- {
- "name": "attributeId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeReferenceValue"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/find-by-id": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "findByAttributeId",
- "parameters": [
- {
- "name": "attributeId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/dynamic/list": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "listAllDynamicAttributes",
- "parameters": [
- {
- "name": "attrGroupId",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/dynamic/list-by-data-type": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "listDynamicAttributesByDataType",
- "parameters": [
- {
- "name": "dataType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "STRING",
- "NUMBER",
- "DATE",
- "BOOLEAN",
- "TIME"
- ]
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/dynamic/check/{attributeId}": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "checkAttributeValueByAttributeId",
- "parameters": [
- {
- "name": "attributeId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/criteria/{attrGroupId}/list": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "findCriteriaAttributesByAttrGroupId",
- "parameters": [
- {
- "name": "attrGroupId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/criteria/tree": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "getRuleCriteriaAttributes",
- "parameters": [
- {
- "name": "attributeType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/FolderAttributeDto"
- }
- }
- }
- }
- }
- },
- "deprecated": true
- }
- },
- "/api/attribute/criteria/non-group/list": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "getNonGroupAttributes",
- "parameters": [
- {
- "name": "attributeType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- },
- "deprecated": true
- }
- },
- "/api/attribute/criteria/list": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "listCriteriaAttributes",
- "parameters": [
- {
- "name": "attrGroupId",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "contextId",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/criteria/context/{contextId}/tree": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "getAllRuleCriteriaTreeAttribute",
- "parameters": [
- {
- "name": "attributeType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- }
- },
- {
- "name": "contextId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/FolderAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/criteria/context/{contextId}/non-group/list": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "getAllNonGroupAttribute",
- "parameters": [
- {
- "name": "attributeType",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- }
- },
- {
- "name": "contextId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/criteria/config/find": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "getAttributeCriteriaConfig",
- "parameters": [
- {
- "name": "attributeId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeCriteriaConfigDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/criteria/config/all": {
- "get": {
- "tags": [
- "attribute-criteria-controller"
- ],
- "operationId": "findAllCriteriaConfig",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeCriteriaConfigDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/context/{contextId}/audience/tree": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "getAudienceAttributes",
- "parameters": [
- {
- "name": "contextId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/FolderAttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/context/{contextId}/audience/non-group/list": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "getAudienceNonGroupAttributes",
- "parameters": [
- {
- "name": "contextId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/audience/{attrGroupId}/list": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "findAudienceAttributesByAttrGroupId",
- "parameters": [
- {
- "name": "attrGroupId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute/audience/tree": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "getAudienceAttributes_1",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/FolderAttributeDto"
- }
- }
- }
- }
- }
- },
- "deprecated": true
- }
- },
- "/api/attribute/audience/non-group/list": {
- "get": {
- "tags": [
- "attribute-controller"
- ],
- "operationId": "getAudienceNonGroupAttributes_1",
- "parameters": [
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- }
- }
- },
- "deprecated": true
- }
- },
- "/api/attribute-group/list-parent-group": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getParentGroupByLevel",
- "parameters": [
- {
- "name": "level",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "searchComponent",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/list-for-static-attribute": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getListAttGroupForStaticAttribute",
- "parameters": [
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/list-child-group-code": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getListChildGroupForBasicAttribute",
- "parameters": [
- {
- "name": "code",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/list-by-level": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getByLevel",
- "parameters": [
- {
- "name": "level",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/list-all-display-tree": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getAllDisplayTree",
- "parameters": [
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/{recordNo}": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "findByRecordNo_2",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/rejection-reason/{recordNo}": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "rejectionReason_2",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/RejectionReasonVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/list": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getAll_2",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/id": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "findActiveByIds_2",
- "parameters": [
- {
- "name": "params",
- "in": "query",
- "required": true,
- "schema": {
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/id/{id}": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "findActiveById_2",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/id/generate": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "generateId_2",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/id/check/{id}": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "checkId_2",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "pattern": "^(?!.*__)[A-Z0-9][A-Z0-9_]*$"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/history/{recordNo}": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "findHistoryByRecordNo_3",
- "parameters": [
- {
- "name": "recordNo",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/history/list": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "getAllHistory_3",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupCriteria"
- }
- },
- {
- "name": "pageable",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/Pageable"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/count": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "count_3",
- "parameters": [
- {
- "name": "criteria",
- "in": "query",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupCriteria"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/check": {
- "get": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "existsAttributeGroup",
- "parameters": [
- {
- "name": "groupId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/by-group-id": {
- "get": {
- "tags": [
- "attribute-group-controller"
- ],
- "operationId": "findAttributeGroupById",
- "parameters": [
- {
- "name": "groupId",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/AttributeGroupVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/static-attribute/csr/delete": {
- "delete": {
- "tags": [
- "static-attribute-resource"
- ],
- "operationId": "delete",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DeleteRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute/csr/delete": {
- "delete": {
- "tags": [
- "dynamic-attribute-resource"
- ],
- "operationId": "delete_1",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DeleteRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
- }
- }
- }
- }
- }
- }
- },
- "/api/dynamic-attribute-value/csr/delete": {
- "delete": {
- "tags": [
- "dynamic-attribute-value-resource"
- ],
- "operationId": "deleteCustomerAttributeValue",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DeleteRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
- }
- }
- }
- }
- }
- }
- },
- "/api/attribute-group/csr/delete": {
- "delete": {
- "tags": [
- "attribute-group-manage-resource"
- ],
- "operationId": "delete_2",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/DeleteRequest"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "*/*": {
- "schema": {
- "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
- }
- }
- }
- }
- }
- }
- }
- },
- "components": {
- "schemas": {
- "ReferenceData": {
- "type": "object",
- "properties": {
- "code": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "notFound": {
- "type": "boolean"
- },
- "recordStatus": {
- "type": "string"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": {}
- }
- }
- },
- "StaticAttributeDto": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "status": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateBy": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateDate": {
- "type": "string",
- "format": "date-time",
- "readOnly": true
- },
- "lastApproveBy": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveDate": {
- "type": "string",
- "format": "date-time",
- "readOnly": true
- },
- "lastUpdateByName": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveByName": {
- "type": "string",
- "readOnly": true
- },
- "attributeId": {
- "type": "string"
- },
- "fieldName": {
- "type": "string"
- },
- "dataType": {
- "type": "string",
- "enum": [
- "STRING",
- "INT",
- "DECIMAL",
- "DATE",
- "TIME",
- "BOOLEAN",
- "SEGMENT",
- "AUDIENCE"
- ]
- },
- "attributeName": {
- "type": "string"
- },
- "attributeGroups": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ReferenceData"
- }
- },
- "audienceAttribute": {
- "type": "boolean"
- },
- "audienceIdentified": {
- "type": "boolean"
- },
- "attributeValuePredefined": {
- "type": "boolean"
- },
- "tableReferenceData": {
- "$ref": "#/components/schemas/ReferenceData"
- },
- "contentParam": {
- "type": "boolean"
- },
- "maxLength": {
- "type": "integer",
- "format": "int64"
- },
- "description": {
- "type": "string",
- "maxLength": 500,
- "minLength": 0
- }
- }
- },
- "OperationResultStaticAttributeDto": {
- "type": "object",
- "properties": {
- "errorCode": {
- "type": "string"
- },
- "message": {
- "type": "string"
- },
- "entity": {
- "$ref": "#/components/schemas/StaticAttributeDto"
- }
- }
- },
- "DynamicAttributeDto": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "status": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateBy": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateDate": {
- "type": "string",
- "format": "date-time",
- "readOnly": true
- },
- "lastApproveBy": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveDate": {
- "type": "string",
- "format": "date-time",
- "readOnly": true
- },
- "lastUpdateByName": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveByName": {
- "type": "string",
- "readOnly": true
- },
- "entity": {
- "type": "string",
- "enum": [
- "CLIENT",
- "PRODUCT_ACCOUNT",
- "CARD",
- "CORPORATION",
- "CHAIN",
- "STORE",
- "CAMPAIGN",
- "ITEM"
- ]
- },
- "attributeId": {
- "type": "string",
- "maxLength": 30,
- "minLength": 1
- },
- "attributeName": {
- "type": "string",
- "maxLength": 100,
- "minLength": 1
- },
- "description": {
- "type": "string",
- "maxLength": 100,
- "minLength": 0
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date"
- },
- "dataType": {
- "type": "string",
- "enum": [
- "STRING",
- "NUMBER",
- "DATE",
- "BOOLEAN",
- "TIME"
- ]
- },
- "semanticType": {
- "type": "string"
- },
- "defaultValue": {
- "type": "string",
- "maxLength": 50,
- "minLength": 0
- },
- "preDefineAttributeValue": {
- "type": "boolean"
- },
- "listOfValue": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "attributeGroups": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ReferenceData"
- }
- }
- },
- "required": [
- "attributeId",
- "attributeName",
- "effectiveFrom",
- "effectiveTo"
- ]
- },
- "OperationResultDynamicAttributeDto": {
- "type": "object",
- "properties": {
- "errorCode": {
- "type": "string"
- },
- "message": {
- "type": "string"
- },
- "entity": {
- "$ref": "#/components/schemas/DynamicAttributeDto"
- }
- }
- },
- "CustomerAttributeValueChangeRequestVo": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "isSpecific": {
- "type": "boolean"
- },
- "attributeId": {
- "type": "string"
- },
- "entity": {
- "type": "string",
- "enum": [
- "CLIENT",
- "PRODUCT_ACCOUNT",
- "CARD",
- "CORPORATION",
- "CHAIN",
- "STORE",
- "CAMPAIGN",
- "ITEM"
- ]
- },
- "entityId": {
- "type": "string"
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "reason": {
- "type": "string",
- "minLength": 1
- },
- "newAttributeValue": {
- "type": "string",
- "minLength": 1
- },
- "newEffectiveFromMode": {
- "type": "string",
- "enum": [
- "FIXED_DATE",
- "DURATION",
- "NON_EXPIRY"
- ]
- },
- "newEffectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "newEffectiveFromDuration": {
- "type": "integer",
- "format": "int32"
- },
- "newEffectiveFromPeriod": {
- "type": "string",
- "enum": [
- "DAY",
- "WEEK",
- "MONTH",
- "QUARTER",
- "YEAR"
- ]
- },
- "newEffectiveToMode": {
- "type": "string",
- "enum": [
- "FIXED_DATE",
- "DURATION",
- "NON_EXPIRY"
- ]
- },
- "newEffectiveTo": {
- "type": "string",
- "format": "date-time"
- },
- "newEffectiveToDuration": {
- "type": "integer",
- "format": "int32"
- },
- "newEffectiveToPeriod": {
- "type": "string",
- "enum": [
- "DAY",
- "WEEK",
- "MONTH",
- "QUARTER",
- "YEAR"
- ]
- },
- "newEffectFromDesc": {
- "type": "string"
- },
- "newEffectToDesc": {
- "type": "string"
- }
- },
- "required": [
- "newAttributeValue",
- "reason"
- ]
- },
- "CustomerAttributeValueChangeVo": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "definitionRecordNo": {
- "type": "integer",
- "format": "int64"
- },
- "recordNoRef": {
- "type": "integer",
- "format": "int64"
- },
- "isSpecific": {
- "type": "boolean"
- },
- "valueSource": {
- "type": "string"
- },
- "entity": {
- "type": "string",
- "enum": [
- "CLIENT",
- "PRODUCT_ACCOUNT",
- "CARD",
- "CORPORATION",
- "CHAIN",
- "STORE",
- "CAMPAIGN",
- "ITEM"
- ]
- },
- "entityName": {
- "type": "string"
- },
- "entityId": {
- "type": "string"
- },
- "cifNbr": {
- "type": "string"
- },
- "entityValue": {
- "type": "string"
- },
- "attributeId": {
- "type": "string"
- },
- "attributeName": {
- "type": "string"
- },
- "attributeValue": {
- "type": "string"
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date-time"
- },
- "newAttributeValue": {
- "type": "string"
- },
- "newEffectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "newEffectiveTo": {
- "type": "string",
- "format": "date-time"
- },
- "newEffectFromDesc": {
- "type": "string"
- },
- "newEffectToDesc": {
- "type": "string"
- },
- "newEffectiveFromMode": {
- "type": "string",
- "enum": [
- "FIXED_DATE",
- "DURATION",
- "NON_EXPIRY"
- ]
- },
- "newEffectiveFromDuration": {
- "type": "integer",
- "format": "int32"
- },
- "newEffectiveFromPeriod": {
- "type": "string",
- "enum": [
- "DAY",
- "WEEK",
- "MONTH",
- "QUARTER",
- "YEAR"
- ]
- },
- "newEffectiveToMode": {
- "type": "string",
- "enum": [
- "FIXED_DATE",
- "DURATION",
- "NON_EXPIRY"
- ]
- },
- "newEffectiveToDuration": {
- "type": "integer",
- "format": "int32"
- },
- "newEffectiveToPeriod": {
- "type": "string",
- "enum": [
- "DAY",
- "WEEK",
- "MONTH",
- "QUARTER",
- "YEAR"
- ]
- },
- "reason": {
- "type": "string"
- },
- "rejectReason": {
- "type": "string"
- },
- "status": {
- "type": "string"
- },
- "lastUpdateBy": {
- "type": "string"
- },
- "lastUpdateDate": {
- "type": "string",
- "format": "date-time"
- },
- "lastApproveBy": {
- "type": "string"
- },
- "lastApproveDate": {
- "type": "string",
- "format": "date-time"
- },
- "lastUpdateByName": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveByName": {
- "type": "string",
- "readOnly": true
- }
- }
- },
- "OperationResultCustomerAttributeValueChangeVo": {
- "type": "object",
- "properties": {
- "errorCode": {
- "type": "string"
- },
- "message": {
- "type": "string"
- },
- "entity": {
- "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
- }
- }
- },
- "UpdateAttributeValueVo": {
- "type": "object",
- "properties": {
- "loyaltyAccountNo": {
- "type": "string"
- },
- "referenceNo": {
- "type": "string"
- },
- "attributeId": {
- "type": "string"
- },
- "entity": {
- "type": "string",
- "enum": [
- "CLIENT",
- "PRODUCT_ACCOUNT",
- "CARD",
- "CORPORATION",
- "CHAIN",
- "STORE",
- "CAMPAIGN",
- "ITEM"
- ]
- },
- "entityId": {
- "type": "string"
- },
- "attrValue": {
- "type": "string"
- },
- "baseDate": {
- "type": "string",
- "format": "date-time"
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date-time"
- },
- "recordNoRef": {
- "type": "integer",
- "format": "int64"
- },
- "oldAttrValue": {
- "type": "string"
- },
- "success": {
- "type": "boolean"
- },
- "entityTable": {
- "type": "boolean"
- }
- }
- },
- "AttributeGroupDto": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "status": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateBy": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateDate": {
- "type": "string",
- "format": "date-time",
- "readOnly": true
- },
- "lastApproveBy": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveDate": {
- "type": "string",
- "format": "date-time",
- "readOnly": true
- },
- "lastUpdateByName": {
- "type": "string",
- "readOnly": true
- },
- "lastApproveByName": {
- "type": "string",
- "readOnly": true
- },
- "groupId": {
- "type": "string",
- "maxLength": 10,
- "minLength": 1
- },
- "groupName": {
- "type": "string",
- "maxLength": 200,
- "minLength": 1
- },
- "level": {
- "type": "string",
- "enum": [
- "L1",
- "L2",
- "L3"
- ]
- },
- "parentGroup": {
- "$ref": "#/components/schemas/ReferenceData"
- },
- "description": {
- "type": "string",
- "maxLength": 500,
- "minLength": 0
- },
- "subGroups": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- },
- "required": [
- "groupId",
- "groupName"
- ]
- },
- "OperationResultAttributeGroupDto": {
- "type": "object",
- "properties": {
- "errorCode": {
- "type": "string"
- },
- "message": {
- "type": "string"
- },
- "entity": {
- "$ref": "#/components/schemas/AttributeGroupDto"
- }
- }
- },
- "RejectRequest": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "rejectReason": {
- "type": "string",
- "minLength": 1
- }
- },
- "required": [
- "recordNo",
- "rejectReason"
- ]
- },
- "BulkUploadRequest": {
- "type": "object",
- "properties": {
- "file": {
- "type": "string",
- "format": "binary"
- }
- }
- },
- "BulkRequest": {
- "type": "object",
- "properties": {
- "recordNos": {
- "type": "array",
- "items": {
- "type": "integer",
- "format": "int64"
- },
- "minItems": 1
- },
- "rejectReason": {
- "type": "string"
- }
- },
- "required": [
- "recordNos"
- ]
- },
- "ApproveRequest": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- }
- },
- "required": [
- "recordNo"
- ]
- },
- "StaticAttributeVo": {
- "type": "object",
- "properties": {
- "attributeType": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- },
- "attributeId": {
- "type": "string"
- },
- "attributeName": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "dataType": {
- "type": "string"
- },
- "semanticType": {
- "type": "string"
- },
- "attributeValuePredefined": {
- "type": "boolean"
- },
- "attributeGroups": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "maxLength": {
- "type": "integer",
- "format": "int64"
- },
- "fieldName": {
- "type": "string"
- },
- "audienceAttribute": {
- "type": "boolean"
- },
- "criteriaAttribute": {
- "type": "boolean"
- },
- "audienceIdentified": {
- "type": "boolean"
- },
- "tableReferenceData": {
- "type": "string"
- },
- "contentParam": {
- "type": "boolean"
- },
- "metaProperty": {
- "type": "string"
- }
- }
- },
- "Pageable": {
- "type": "object",
- "properties": {
- "page": {
- "type": "integer",
- "format": "int32",
- "minimum": 0
- },
- "size": {
- "type": "integer",
- "format": "int32",
- "minimum": 1
- },
- "sort": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "RangeFilterInstant": {
- "type": "object",
- "properties": {
- "equals": {
- "type": "string",
- "format": "date-time"
- },
- "notEquals": {
- "type": "string",
- "format": "date-time"
- },
- "specified": {
- "type": "boolean"
- },
- "in": {
- "type": "array",
- "items": {
- "type": "string",
- "format": "date-time"
- }
- },
- "notIn": {
- "type": "array",
- "items": {
- "type": "string",
- "format": "date-time"
- }
- },
- "greaterThan": {
- "type": "string",
- "format": "date-time"
- },
- "lessThan": {
- "type": "string",
- "format": "date-time"
- },
- "greaterThanOrEqual": {
- "type": "string",
- "format": "date-time"
- },
- "lessThanOrEqual": {
- "type": "string",
- "format": "date-time"
- }
- }
- },
- "StaticAttributeViewCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "lastUpdateDate": {
- "$ref": "#/components/schemas/RangeFilterInstant"
- },
- "search": {
- "type": "string"
- },
- "searchComponent": {
- "type": "string"
- },
- "attributeId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "fieldName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "dataType": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "audienceAttribute": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "criteriaAttribute": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "contentParam": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "description": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "componentColumns": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "attributeGroups": {
- "$ref": "#/components/schemas/StringFilter"
- }
- }
- },
- "StringFilter": {
- "type": "object",
- "properties": {
- "equals": {
- "type": "string"
- },
- "notEquals": {
- "type": "string"
- },
- "specified": {
- "type": "boolean"
- },
- "in": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "notIn": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "contains": {
- "type": "string"
- },
- "doesNotContain": {
- "type": "string"
- }
- }
- },
- "RejectionReasonVo": {
- "type": "object",
- "properties": {
- "moduleName": {
- "type": "string"
- },
- "moduleRecordNo": {
- "type": "integer",
- "format": "int64"
- },
- "moduleStatus": {
- "type": "string"
- },
- "rejectionReason": {
- "type": "string"
- },
- "createdBy": {
- "type": "string"
- },
- "createdDate": {
- "type": "string",
- "format": "date-time"
- },
- "lastUpdateBy": {
- "type": "string"
- },
- "lastUpdateDate": {
- "type": "string",
- "format": "date-time"
- },
- "createdByName": {
- "type": "string",
- "readOnly": true
- },
- "lastUpdateByName": {
- "type": "string",
- "readOnly": true
- }
- }
- },
- "StaticAttributeCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "lastUpdateDate": {
- "$ref": "#/components/schemas/RangeFilterInstant"
- },
- "search": {
- "type": "string"
- },
- "searchComponent": {
- "type": "string"
- },
- "attributeId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "fieldName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "dataType": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "audienceAttribute": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "criteriaAttribute": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "contentParam": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "description": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "componentColumns": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "MappingMatrixResponseDto": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string"
- },
- "firstColumnHeader": {
- "type": "string"
- },
- "columns": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/MatrixColumnDto"
- }
- },
- "rows": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/MatrixRowDto"
- }
- }
- }
- },
- "MatrixCellDto": {
- "type": "object",
- "properties": {
- "contextId": {
- "type": "string"
- },
- "isEnabled": {
- "type": "boolean"
- }
- }
- },
- "MatrixColumnDto": {
- "type": "object",
- "properties": {
- "contextId": {
- "type": "string"
- },
- "contextType": {
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "description": {
- "type": "string"
- }
- }
- },
- "MatrixRowDto": {
- "type": "object",
- "properties": {
- "objectId": {
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "cells": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/MatrixCellDto"
- }
- }
- }
- },
- "DynamicAttributeVo": {
- "type": "object",
- "properties": {
- "attributeType": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- },
- "attributeId": {
- "type": "string"
- },
- "attributeName": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "dataType": {
- "type": "string"
- },
- "semanticType": {
- "type": "string"
- },
- "attributeValuePredefined": {
- "type": "boolean"
- },
- "attributeGroups": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "maxLength": {
- "type": "integer",
- "format": "int64"
- },
- "entity": {
- "type": "string",
- "enum": [
- "CLIENT",
- "PRODUCT_ACCOUNT",
- "CARD",
- "CORPORATION",
- "CHAIN",
- "STORE",
- "CAMPAIGN",
- "ITEM"
- ]
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date"
- },
- "defaultValue": {
- "type": "string"
- },
- "attrGroupId": {
- "type": "string"
- }
- }
- },
- "DynamicAttributeViewCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "lastUpdateDate": {
- "$ref": "#/components/schemas/RangeFilterInstant"
- },
- "search": {
- "type": "string"
- },
- "searchComponent": {
- "type": "string"
- },
- "entity": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "description": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attrGroupId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "dataType": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "effectiveFrom": {
- "$ref": "#/components/schemas/LocalDateFilter"
- },
- "effectiveTo": {
- "$ref": "#/components/schemas/LocalDateFilter"
- },
- "componentColumns": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "attributeGroups": {
- "$ref": "#/components/schemas/StringFilter"
- }
- }
- },
- "LocalDateFilter": {
- "type": "object",
- "properties": {
- "equals": {
- "type": "string",
- "format": "date"
- },
- "notEquals": {
- "type": "string",
- "format": "date"
- },
- "specified": {
- "type": "boolean"
- },
- "in": {
- "type": "array",
- "items": {
- "type": "string",
- "format": "date"
- }
- },
- "notIn": {
- "type": "array",
- "items": {
- "type": "string",
- "format": "date"
- }
- },
- "greaterThan": {
- "type": "string",
- "format": "date"
- },
- "lessThan": {
- "type": "string",
- "format": "date"
- },
- "greaterThanOrEqual": {
- "type": "string",
- "format": "date"
- },
- "lessThanOrEqual": {
- "type": "string",
- "format": "date"
- }
- }
- },
- "DynamicAttributeCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "lastUpdateDate": {
- "$ref": "#/components/schemas/RangeFilterInstant"
- },
- "search": {
- "type": "string"
- },
- "searchComponent": {
- "type": "string"
- },
- "entity": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "description": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attrGroupId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "dataType": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "effectiveFrom": {
- "$ref": "#/components/schemas/LocalDateFilter"
- },
- "effectiveTo": {
- "$ref": "#/components/schemas/LocalDateFilter"
- },
- "componentColumns": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "BooleanFilter": {
- "type": "object",
- "properties": {
- "equals": {
- "type": "boolean"
- },
- "notEquals": {
- "type": "boolean"
- },
- "specified": {
- "type": "boolean"
- },
- "in": {
- "type": "array",
- "items": {
- "type": "boolean"
- }
- },
- "notIn": {
- "type": "array",
- "items": {
- "type": "boolean"
- }
- }
- }
- },
- "CustomerAttributeValueChangeCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "lastUpdateDate": {
- "$ref": "#/components/schemas/RangeFilterInstant"
- },
- "search": {
- "type": "string"
- },
- "searchComponent": {
- "type": "string"
- },
- "attributeId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "attributeName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "entity": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "entityId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "isSpecific": {
- "$ref": "#/components/schemas/BooleanFilter"
- },
- "effectiveFrom": {
- "$ref": "#/components/schemas/LocalDateFilter"
- },
- "effectiveTo": {
- "$ref": "#/components/schemas/LocalDateFilter"
- },
- "effectiveOnly": {
- "type": "boolean"
- },
- "csn": {
- "type": "string"
- },
- "componentColumns": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "DynamicAttributeValueDetailCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "type": "string"
- },
- "valueSource": {
- "type": "string"
- },
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "definitionRecordNo": {
- "type": "integer",
- "format": "int64"
- },
- "entity": {
- "type": "string"
- },
- "entityId": {
- "type": "string"
- },
- "csn": {
- "type": "string"
- }
- }
- },
- "DynamicAttributeValueDetailVo": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "definitionRecordNo": {
- "type": "integer",
- "format": "int64"
- },
- "csn": {
- "type": "string"
- },
- "cifNbr": {
- "type": "string"
- },
- "isSpecific": {
- "type": "boolean"
- },
- "valueSource": {
- "type": "string"
- },
- "entity": {
- "type": "string",
- "enum": [
- "CLIENT",
- "PRODUCT_ACCOUNT",
- "CARD",
- "CORPORATION",
- "CHAIN",
- "STORE",
- "CAMPAIGN",
- "ITEM"
- ]
- },
- "entityName": {
- "type": "string"
- },
- "entityId": {
- "type": "string"
- },
- "entityValue": {
- "type": "string"
- },
- "attributeId": {
- "type": "string"
- },
- "attributeName": {
- "type": "string"
- },
- "attributeValue": {
- "type": "string"
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date-time"
- },
- "status": {
- "type": "string"
- },
- "lastUpdateBy": {
- "type": "string"
- },
- "lastUpdateDate": {
- "type": "string",
- "format": "date-time"
- },
- "lastApproveBy": {
- "type": "string"
- },
- "lastApproveDate": {
- "type": "string",
- "format": "date-time"
- },
- "addOnVisible": {
- "type": "boolean"
- },
- "historyEmpty": {
- "type": "boolean"
- }
- }
- },
- "DynamicAttributeValueAddOnCriteria": {
- "type": "object",
- "properties": {
- "csn": {
- "type": "string"
- },
- "entity": {
- "type": "string"
- },
- "entityId": {
- "type": "string"
- },
- "attributeId": {
- "type": "string"
- },
- "excludeRecordNo": {
- "type": "integer",
- "format": "int64"
- },
- "search": {
- "type": "string"
- }
- }
- },
- "DynamicAttributeValueAddOnVo": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- },
- "attributeValue": {
- "type": "string"
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date-time"
- },
- "currentlyEffective": {
- "type": "boolean"
- },
- "status": {
- "type": "string"
- }
- }
- },
- "AttributeValueVo": {
- "type": "object",
- "properties": {
- "attributeId": {
- "type": "string"
- },
- "attributeType": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- },
- "entity": {
- "type": "string"
- },
- "entityId": {
- "type": "string"
- },
- "attrValue": {
- "type": "string"
- },
- "effectiveFrom": {
- "type": "string",
- "format": "date-time"
- },
- "effectiveTo": {
- "type": "string",
- "format": "date-time"
- }
- }
- },
- "AttributeReferenceValue": {
- "type": "object",
- "properties": {
- "code": {
- "type": "string"
- },
- "description": {
- "type": "string"
- }
- }
- },
- "AttributeVo": {
- "type": "object",
- "properties": {
- "attributeType": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- },
- "attributeId": {
- "type": "string"
- },
- "attributeName": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "dataType": {
- "type": "string"
- },
- "semanticType": {
- "type": "string"
- },
- "attributeValuePredefined": {
- "type": "boolean"
- },
- "attributeGroups": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "maxLength": {
- "type": "integer",
- "format": "int64"
- }
- }
- },
- "AttributeDto": {
- "type": "object",
- "properties": {
- "attributeType": {
- "type": "string",
- "enum": [
- "CUSTOM",
- "DYNAMIC",
- "STATIC"
- ]
- },
- "attributeId": {
- "type": "string"
- },
- "attributeName": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "dataType": {
- "type": "string"
- },
- "semanticType": {
- "type": "string"
- },
- "attributeValuePredefined": {
- "type": "boolean"
- },
- "preDefineAttributeValue": {
- "type": "boolean"
- },
- "listOfValue": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "attributeGroups": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ReferenceData"
- }
- },
- "maxLength": {
- "type": "integer",
- "format": "int64"
- }
- }
- },
- "FolderAttributeDto": {
- "type": "object",
- "properties": {
- "groupId": {
- "type": "string"
- },
- "groupName": {
- "type": "string"
- },
- "parentGroup": {
- "type": "string"
- },
- "level": {
- "type": "string",
- "enum": [
- "L1",
- "L2",
- "L3"
- ]
- },
- "attributes": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeDto"
- }
- }
- }
- },
- "AttributeCriteriaConfigDto": {
- "type": "object",
- "properties": {
- "attributeId": {
- "type": "string"
- },
- "criteriaFieldId": {
- "type": "string"
- },
- "computeInd": {
- "type": "string"
- },
- "compareValue": {
- "type": "string"
- },
- "criteriaType": {
- "type": "string"
- }
- }
- },
- "AttributeGroupCriteria": {
- "type": "object",
- "properties": {
- "status": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "lastUpdateDate": {
- "$ref": "#/components/schemas/RangeFilterInstant"
- },
- "search": {
- "type": "string"
- },
- "searchComponent": {
- "type": "string"
- },
- "groupId": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "groupName": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "parentGroup": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "level": {
- "$ref": "#/components/schemas/StringFilter"
- },
- "componentColumns": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "AttributeGroupVo": {
- "type": "object",
- "properties": {
- "groupId": {
- "type": "string"
- },
- "groupName": {
- "type": "string"
- },
- "level": {
- "type": "string"
- },
- "parentGroup": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "subGroup": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/AttributeGroupVo"
- }
- }
- }
- },
- "DeleteRequest": {
- "type": "object",
- "properties": {
- "recordNo": {
- "type": "integer",
- "format": "int64"
- }
- },
- "required": [
- "recordNo"
- ]
- }
- }
- }
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "OpenAPI definition",
+ "version": "v0"
+ },
+ "servers": [
+ {
+ "url": "http://attribute-service:8080",
+ "description": "Generated server url"
+ }
+ ],
+ "tags": [
+ {
+ "name": "Mapping Matrix",
+ "description": "Context Mapping Matrix APIs"
+ }
+ ],
+ "paths": {
+ "/api/static-attribute/csr/create": {
+ "post": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "create",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/create-draft": {
+ "post": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "createDraft",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/create": {
+ "post": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "create_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/create-draft": {
+ "post": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "createDraft_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/edit": {
+ "post": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "editCustomerAttributeValue",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeRequestVo"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/dynamic/update": {
+ "post": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "updateAttributeValue",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateAttributeValueVo"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateAttributeValueVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/create": {
+ "post": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "create_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/create-draft": {
+ "post": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "createDraft_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/reject": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "reject",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/edit": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "edit",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/edit-draft": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "editDraft",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/bulk-upload": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "bulkUpload",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkUploadRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/bulk-reject": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "bulkReject",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/bulk-approve": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "bulkApprove",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/approve": {
+ "patch": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "approve",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ApproveRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/reject": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "reject_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/edit": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "edit_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/edit-draft": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "editDraft_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/bulk-upload": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "bulkUpload_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkUploadRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/bulk-reject": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "bulkReject_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/bulk-approve": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "bulkApprove_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/approve": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "approve_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ApproveRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/reject": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "rejectCustomerAttributeValue",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/approve": {
+ "patch": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "approveCustomerAttributeValue",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ApproveRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/reject": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "reject_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/edit": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "edit_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/edit-draft": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "editDraft_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/bulk-upload": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "bulkUpload_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkUploadRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/bulk-reject": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "bulkReject_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/bulk-approve": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "bulkApprove_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/BulkRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/approve": {
+ "patch": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "approve_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ApproveRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/{attributeId}": {
+ "get": {
+ "tags": [
+ "static-attribute-controller"
+ ],
+ "operationId": "findStaticAttributeById",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/{recordNo}": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "findByRecordNo",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/{group}/list": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "listByGroupCode",
+ "parameters": [
+ {
+ "name": "group",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/view/list": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "findViewByCriteria",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeViewCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/view/count": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "findViewByCriteria_1",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeViewCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/rejection-reason/{recordNo}": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "rejectionReason",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectionReasonVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/list": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "getAll",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/id": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "findActiveByIds",
+ "parameters": [
+ {
+ "name": "params",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/id/{id}": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "findActiveById",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/id/generate": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "generateId",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/id/check/{id}": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "checkId",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "pattern": "^(?!.*__)[A-Z0-9][A-Z0-9_]*$"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/history/{recordNo}": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "findHistoryByRecordNo",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/history/list": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "getAllHistory",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/count": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "count",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/StaticAttributeCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/context/{contextId}/list": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "getAllByContextId",
+ "parameters": [
+ {
+ "name": "contextId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ },
+ {
+ "name": "search",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/attribute-type/list": {
+ "get": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "getAllAttributeType",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReferenceData"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/mapping-matrix": {
+ "get": {
+ "tags": [
+ "Mapping Matrix"
+ ],
+ "summary": "Get mapping matrix",
+ "description": "Returns mapping matrix based on object type (Static Attribute or Entity)",
+ "operationId": "getMappingMatrix",
+ "parameters": [
+ {
+ "name": "objectType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "pattern": "STATIC_ATTRIBUTE|ENTITY"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/MappingMatrixResponseDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/{attributeId}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-controller"
+ ],
+ "operationId": "findAttributeById",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/list-by-data-type": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-controller"
+ ],
+ "operationId": "getListDynamicAttributeByDataType",
+ "parameters": [
+ {
+ "name": "dataType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "STRING",
+ "NUMBER",
+ "DATE",
+ "BOOLEAN",
+ "TIME"
+ ]
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/list-active": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-controller"
+ ],
+ "operationId": "getListActive",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/{recordNo}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "findByRecordNo_1",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/view/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "findViewByCriteria_2",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeViewCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/view/count": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "countViewByCriteria",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeViewCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/rejection-reason/{recordNo}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "rejectionReason_1",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectionReasonVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "getAll_1",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/id": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "findActiveByIds_1",
+ "parameters": [
+ {
+ "name": "params",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/id/{id}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "findActiveById_1",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/id/generate": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "generateId_1",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/id/check/{id}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "checkId_1",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "pattern": "^(?!.*__)[A-Z0-9][A-Z0-9_]*$"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/history/{recordNo}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "findHistoryByRecordNo_1",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/history/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "getAllHistory_1",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/count": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "count_1",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/context/{contextId}/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "getAllByContextId_1",
+ "parameters": [
+ {
+ "name": "contextId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ },
+ {
+ "name": "search",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/check": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-controller"
+ ],
+ "operationId": "checkAttribute",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/check-with-data-type": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-controller"
+ ],
+ "operationId": "checkAttributeWithDataType",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "dataType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "STRING",
+ "NUMBER",
+ "DATE",
+ "BOOLEAN",
+ "TIME"
+ ]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/attribute-type/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "getAllAttributeType_1",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReferenceData"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/{recordNo}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "getCustomerAttributeValueChange",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/{recordNo}/csn/{csn}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "getCustomerAttributeValueChange_1",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "csn",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "listCustomerAttributeValueChanges",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/history/{recordNo}": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "findHistoryByRecordNo_2",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/history/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "getAllHistory_2",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/detail": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "getActiveDetail",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeValueDetailCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeValueDetailVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/detail/add-on/list": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "listActiveDetailAddOns",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/DynamicAttributeValueAddOnCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/DynamicAttributeValueAddOnVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/count": {
+ "get": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "count_2",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/value": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "findByEntityAndEntityIdAndAttrIdAndDate",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "entityId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "baseDate",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeValueVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/reference/values": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "getAttributeReferenceValues",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeReferenceValue"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/find-by-id": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "findByAttributeId",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/dynamic/list": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "listAllDynamicAttributes",
+ "parameters": [
+ {
+ "name": "attrGroupId",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/dynamic/list-by-data-type": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "listDynamicAttributesByDataType",
+ "parameters": [
+ {
+ "name": "dataType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "STRING",
+ "NUMBER",
+ "DATE",
+ "BOOLEAN",
+ "TIME"
+ ]
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/dynamic/check/{attributeId}": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "checkAttributeValueByAttributeId",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/criteria/{attrGroupId}/list": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "findCriteriaAttributesByAttrGroupId",
+ "parameters": [
+ {
+ "name": "attrGroupId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/criteria/tree": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "getRuleCriteriaAttributes",
+ "parameters": [
+ {
+ "name": "attributeType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/FolderAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "deprecated": true
+ }
+ },
+ "/api/attribute/criteria/non-group/list": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "getNonGroupAttributes",
+ "parameters": [
+ {
+ "name": "attributeType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "deprecated": true
+ }
+ },
+ "/api/attribute/criteria/list": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "listCriteriaAttributes",
+ "parameters": [
+ {
+ "name": "attrGroupId",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "contextId",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/criteria/context/{contextId}/tree": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "getAllRuleCriteriaTreeAttribute",
+ "parameters": [
+ {
+ "name": "attributeType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ }
+ },
+ {
+ "name": "contextId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/FolderAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/criteria/context/{contextId}/non-group/list": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "getAllNonGroupAttribute",
+ "parameters": [
+ {
+ "name": "attributeType",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ }
+ },
+ {
+ "name": "contextId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/criteria/config/find": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "getAttributeCriteriaConfig",
+ "parameters": [
+ {
+ "name": "attributeId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeCriteriaConfigDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/criteria/config/all": {
+ "get": {
+ "tags": [
+ "attribute-criteria-controller"
+ ],
+ "operationId": "findAllCriteriaConfig",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeCriteriaConfigDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/context/{contextId}/audience/tree": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "getAudienceAttributes",
+ "parameters": [
+ {
+ "name": "contextId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/FolderAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/context/{contextId}/audience/non-group/list": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "getAudienceNonGroupAttributes",
+ "parameters": [
+ {
+ "name": "contextId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/audience/{attrGroupId}/list": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "findAudienceAttributesByAttrGroupId",
+ "parameters": [
+ {
+ "name": "attrGroupId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute/audience/tree": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "getAudienceAttributes_1",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/FolderAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "deprecated": true
+ }
+ },
+ "/api/attribute/audience/non-group/list": {
+ "get": {
+ "tags": [
+ "attribute-controller"
+ ],
+ "operationId": "getAudienceNonGroupAttributes_1",
+ "parameters": [
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "deprecated": true
+ }
+ },
+ "/api/attribute-group/list-parent-group": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getParentGroupByLevel",
+ "parameters": [
+ {
+ "name": "level",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "searchComponent",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/list-for-static-attribute": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getListAttGroupForStaticAttribute",
+ "parameters": [
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/list-child-group-code": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getListChildGroupForBasicAttribute",
+ "parameters": [
+ {
+ "name": "code",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/list-by-level": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getByLevel",
+ "parameters": [
+ {
+ "name": "level",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/list-all-display-tree": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getAllDisplayTree",
+ "parameters": [
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/{recordNo}": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "findByRecordNo_2",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/rejection-reason/{recordNo}": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "rejectionReason_2",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/RejectionReasonVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/list": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getAll_2",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/id": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "findActiveByIds_2",
+ "parameters": [
+ {
+ "name": "params",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/id/{id}": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "findActiveById_2",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/id/generate": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "generateId_2",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/id/check/{id}": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "checkId_2",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "pattern": "^(?!.*__)[A-Z0-9][A-Z0-9_]*$"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/history/{recordNo}": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "findHistoryByRecordNo_3",
+ "parameters": [
+ {
+ "name": "recordNo",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/history/list": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "getAllHistory_3",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupCriteria"
+ }
+ },
+ {
+ "name": "pageable",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/Pageable"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/count": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "count_3",
+ "parameters": [
+ {
+ "name": "criteria",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupCriteria"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/check": {
+ "get": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "existsAttributeGroup",
+ "parameters": [
+ {
+ "name": "groupId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/by-group-id": {
+ "get": {
+ "tags": [
+ "attribute-group-controller"
+ ],
+ "operationId": "findAttributeGroupById",
+ "parameters": [
+ {
+ "name": "groupId",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/AttributeGroupVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/static-attribute/csr/delete": {
+ "delete": {
+ "tags": [
+ "static-attribute-resource"
+ ],
+ "operationId": "delete",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeleteRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultStaticAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute/csr/delete": {
+ "delete": {
+ "tags": [
+ "dynamic-attribute-resource"
+ ],
+ "operationId": "delete_1",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeleteRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultDynamicAttributeDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/dynamic-attribute-value/csr/delete": {
+ "delete": {
+ "tags": [
+ "dynamic-attribute-value-resource"
+ ],
+ "operationId": "deleteCustomerAttributeValue",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeleteRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultCustomerAttributeValueChangeVo"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/attribute-group/csr/delete": {
+ "delete": {
+ "tags": [
+ "attribute-group-manage-resource"
+ ],
+ "operationId": "delete_2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeleteRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/OperationResultAttributeGroupDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ReferenceData": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "notFound": {
+ "type": "boolean"
+ },
+ "recordStatus": {
+ "type": "string"
+ },
+ "attributes": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ }
+ },
+ "StaticAttributeDto": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "status": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateBy": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateDate": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true
+ },
+ "lastApproveBy": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveDate": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true
+ },
+ "lastUpdateByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "fieldName": {
+ "type": "string"
+ },
+ "dataType": {
+ "type": "string",
+ "enum": [
+ "STRING",
+ "INT",
+ "DECIMAL",
+ "DATE",
+ "TIME",
+ "BOOLEAN",
+ "SEGMENT",
+ "AUDIENCE"
+ ]
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "attributeGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReferenceData"
+ }
+ },
+ "audienceAttribute": {
+ "type": "boolean"
+ },
+ "audienceIdentified": {
+ "type": "boolean"
+ },
+ "attributeValuePredefined": {
+ "type": "boolean"
+ },
+ "tableReferenceData": {
+ "$ref": "#/components/schemas/ReferenceData"
+ },
+ "contentParam": {
+ "type": "boolean"
+ },
+ "maxLength": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 500,
+ "minLength": 0
+ }
+ }
+ },
+ "OperationResultStaticAttributeDto": {
+ "type": "object",
+ "properties": {
+ "errorCode": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/StaticAttributeDto"
+ }
+ }
+ },
+ "DynamicAttributeDto": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "status": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateBy": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateDate": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true
+ },
+ "lastApproveBy": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveDate": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true
+ },
+ "lastUpdateByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "entity": {
+ "type": "string",
+ "enum": [
+ "CLIENT",
+ "PRODUCT_ACCOUNT",
+ "CARD",
+ "CORPORATION",
+ "CHAIN",
+ "STORE",
+ "CAMPAIGN",
+ "ITEM"
+ ]
+ },
+ "attributeId": {
+ "type": "string",
+ "maxLength": 30,
+ "minLength": 1
+ },
+ "attributeName": {
+ "type": "string",
+ "maxLength": 100,
+ "minLength": 1
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 100,
+ "minLength": 0
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date"
+ },
+ "dataType": {
+ "type": "string",
+ "enum": [
+ "STRING",
+ "NUMBER",
+ "DATE",
+ "BOOLEAN",
+ "TIME"
+ ]
+ },
+ "semanticType": {
+ "type": "string"
+ },
+ "defaultValue": {
+ "type": "string",
+ "maxLength": 50,
+ "minLength": 0
+ },
+ "preDefineAttributeValue": {
+ "type": "boolean"
+ },
+ "listOfValue": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attributeGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReferenceData"
+ }
+ }
+ },
+ "required": [
+ "attributeId",
+ "attributeName",
+ "effectiveFrom",
+ "effectiveTo"
+ ]
+ },
+ "OperationResultDynamicAttributeDto": {
+ "type": "object",
+ "properties": {
+ "errorCode": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/DynamicAttributeDto"
+ }
+ }
+ },
+ "CustomerAttributeValueChangeRequestVo": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "isSpecific": {
+ "type": "boolean"
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "entity": {
+ "type": "string",
+ "enum": [
+ "CLIENT",
+ "PRODUCT_ACCOUNT",
+ "CARD",
+ "CORPORATION",
+ "CHAIN",
+ "STORE",
+ "CAMPAIGN",
+ "ITEM"
+ ]
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1
+ },
+ "newAttributeValue": {
+ "type": "string",
+ "minLength": 1
+ },
+ "newEffectiveFromMode": {
+ "type": "string",
+ "enum": [
+ "FIXED_DATE",
+ "DURATION",
+ "NON_EXPIRY"
+ ]
+ },
+ "newEffectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "newEffectiveFromDuration": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "newEffectiveFromPeriod": {
+ "type": "string",
+ "enum": [
+ "DAY",
+ "WEEK",
+ "MONTH",
+ "QUARTER",
+ "YEAR"
+ ]
+ },
+ "newEffectiveToMode": {
+ "type": "string",
+ "enum": [
+ "FIXED_DATE",
+ "DURATION",
+ "NON_EXPIRY"
+ ]
+ },
+ "newEffectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "newEffectiveToDuration": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "newEffectiveToPeriod": {
+ "type": "string",
+ "enum": [
+ "DAY",
+ "WEEK",
+ "MONTH",
+ "QUARTER",
+ "YEAR"
+ ]
+ },
+ "newEffectFromDesc": {
+ "type": "string"
+ },
+ "newEffectToDesc": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "newAttributeValue",
+ "reason"
+ ]
+ },
+ "CustomerAttributeValueChangeVo": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "definitionRecordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "recordNoRef": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "isSpecific": {
+ "type": "boolean"
+ },
+ "valueSource": {
+ "type": "string"
+ },
+ "entity": {
+ "type": "string",
+ "enum": [
+ "CLIENT",
+ "PRODUCT_ACCOUNT",
+ "CARD",
+ "CORPORATION",
+ "CHAIN",
+ "STORE",
+ "CAMPAIGN",
+ "ITEM"
+ ]
+ },
+ "entityName": {
+ "type": "string"
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "cifNbr": {
+ "type": "string"
+ },
+ "entityValue": {
+ "type": "string"
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "attributeValue": {
+ "type": "string"
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "newAttributeValue": {
+ "type": "string"
+ },
+ "newEffectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "newEffectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "newEffectFromDesc": {
+ "type": "string"
+ },
+ "newEffectToDesc": {
+ "type": "string"
+ },
+ "newEffectiveFromMode": {
+ "type": "string",
+ "enum": [
+ "FIXED_DATE",
+ "DURATION",
+ "NON_EXPIRY"
+ ]
+ },
+ "newEffectiveFromDuration": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "newEffectiveFromPeriod": {
+ "type": "string",
+ "enum": [
+ "DAY",
+ "WEEK",
+ "MONTH",
+ "QUARTER",
+ "YEAR"
+ ]
+ },
+ "newEffectiveToMode": {
+ "type": "string",
+ "enum": [
+ "FIXED_DATE",
+ "DURATION",
+ "NON_EXPIRY"
+ ]
+ },
+ "newEffectiveToDuration": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "newEffectiveToPeriod": {
+ "type": "string",
+ "enum": [
+ "DAY",
+ "WEEK",
+ "MONTH",
+ "QUARTER",
+ "YEAR"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "rejectReason": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "lastUpdateBy": {
+ "type": "string"
+ },
+ "lastUpdateDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "lastApproveBy": {
+ "type": "string"
+ },
+ "lastApproveDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "lastUpdateByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveByName": {
+ "type": "string",
+ "readOnly": true
+ }
+ }
+ },
+ "OperationResultCustomerAttributeValueChangeVo": {
+ "type": "object",
+ "properties": {
+ "errorCode": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/CustomerAttributeValueChangeVo"
+ }
+ }
+ },
+ "UpdateAttributeValueVo": {
+ "type": "object",
+ "properties": {
+ "loyaltyAccountNo": {
+ "type": "string"
+ },
+ "referenceNo": {
+ "type": "string"
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "entity": {
+ "type": "string",
+ "enum": [
+ "CLIENT",
+ "PRODUCT_ACCOUNT",
+ "CARD",
+ "CORPORATION",
+ "CHAIN",
+ "STORE",
+ "CAMPAIGN",
+ "ITEM"
+ ]
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "attrValue": {
+ "type": "string"
+ },
+ "baseDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "recordNoRef": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "oldAttrValue": {
+ "type": "string"
+ },
+ "success": {
+ "type": "boolean"
+ },
+ "entityTable": {
+ "type": "boolean"
+ }
+ }
+ },
+ "AttributeGroupDto": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "status": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateBy": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateDate": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true
+ },
+ "lastApproveBy": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveDate": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true
+ },
+ "lastUpdateByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastApproveByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "groupId": {
+ "type": "string",
+ "maxLength": 10,
+ "minLength": 1
+ },
+ "groupName": {
+ "type": "string",
+ "maxLength": 200,
+ "minLength": 1
+ },
+ "level": {
+ "type": "string",
+ "enum": [
+ "L1",
+ "L2",
+ "L3"
+ ]
+ },
+ "parentGroup": {
+ "$ref": "#/components/schemas/ReferenceData"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 500,
+ "minLength": 0
+ },
+ "subGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ },
+ "required": [
+ "groupId",
+ "groupName"
+ ]
+ },
+ "OperationResultAttributeGroupDto": {
+ "type": "object",
+ "properties": {
+ "errorCode": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/AttributeGroupDto"
+ }
+ }
+ },
+ "RejectRequest": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "rejectReason": {
+ "type": "string",
+ "minLength": 1
+ }
+ },
+ "required": [
+ "recordNo",
+ "rejectReason"
+ ]
+ },
+ "BulkUploadRequest": {
+ "type": "object",
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary"
+ }
+ }
+ },
+ "BulkRequest": {
+ "type": "object",
+ "properties": {
+ "recordNos": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "minItems": 1
+ },
+ "rejectReason": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "recordNos"
+ ]
+ },
+ "ApproveRequest": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ }
+ },
+ "required": [
+ "recordNo"
+ ]
+ },
+ "StaticAttributeVo": {
+ "type": "object",
+ "properties": {
+ "attributeType": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "dataType": {
+ "type": "string"
+ },
+ "semanticType": {
+ "type": "string"
+ },
+ "attributeValuePredefined": {
+ "type": "boolean"
+ },
+ "attributeGroups": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "maxLength": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "fieldName": {
+ "type": "string"
+ },
+ "audienceAttribute": {
+ "type": "boolean"
+ },
+ "criteriaAttribute": {
+ "type": "boolean"
+ },
+ "audienceIdentified": {
+ "type": "boolean"
+ },
+ "tableReferenceData": {
+ "type": "string"
+ },
+ "contentParam": {
+ "type": "boolean"
+ },
+ "metaProperty": {
+ "type": "string"
+ }
+ }
+ },
+ "Pageable": {
+ "type": "object",
+ "properties": {
+ "page": {
+ "type": "integer",
+ "format": "int32",
+ "minimum": 0
+ },
+ "size": {
+ "type": "integer",
+ "format": "int32",
+ "minimum": 1
+ },
+ "sort": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "RangeFilterInstant": {
+ "type": "object",
+ "properties": {
+ "equals": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "notEquals": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "specified": {
+ "type": "boolean"
+ },
+ "in": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "notIn": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "greaterThan": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "lessThan": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "greaterThanOrEqual": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "lessThanOrEqual": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "StaticAttributeViewCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "lastUpdateDate": {
+ "$ref": "#/components/schemas/RangeFilterInstant"
+ },
+ "search": {
+ "type": "string"
+ },
+ "searchComponent": {
+ "type": "string"
+ },
+ "attributeId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "fieldName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "dataType": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "audienceAttribute": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "criteriaAttribute": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "contentParam": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "description": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "componentColumns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attributeGroups": {
+ "$ref": "#/components/schemas/StringFilter"
+ }
+ }
+ },
+ "StringFilter": {
+ "type": "object",
+ "properties": {
+ "equals": {
+ "type": "string"
+ },
+ "notEquals": {
+ "type": "string"
+ },
+ "specified": {
+ "type": "boolean"
+ },
+ "in": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "notIn": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "contains": {
+ "type": "string"
+ },
+ "doesNotContain": {
+ "type": "string"
+ }
+ }
+ },
+ "RejectionReasonVo": {
+ "type": "object",
+ "properties": {
+ "moduleName": {
+ "type": "string"
+ },
+ "moduleRecordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "moduleStatus": {
+ "type": "string"
+ },
+ "rejectionReason": {
+ "type": "string"
+ },
+ "createdBy": {
+ "type": "string"
+ },
+ "createdDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "lastUpdateBy": {
+ "type": "string"
+ },
+ "lastUpdateDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "createdByName": {
+ "type": "string",
+ "readOnly": true
+ },
+ "lastUpdateByName": {
+ "type": "string",
+ "readOnly": true
+ }
+ }
+ },
+ "StaticAttributeCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "lastUpdateDate": {
+ "$ref": "#/components/schemas/RangeFilterInstant"
+ },
+ "search": {
+ "type": "string"
+ },
+ "searchComponent": {
+ "type": "string"
+ },
+ "attributeId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "fieldName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "dataType": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "audienceAttribute": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "criteriaAttribute": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "contentParam": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "description": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "componentColumns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "MappingMatrixResponseDto": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "firstColumnHeader": {
+ "type": "string"
+ },
+ "columns": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/MatrixColumnDto"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/MatrixRowDto"
+ }
+ }
+ }
+ },
+ "MatrixCellDto": {
+ "type": "object",
+ "properties": {
+ "contextId": {
+ "type": "string"
+ },
+ "isEnabled": {
+ "type": "boolean"
+ }
+ }
+ },
+ "MatrixColumnDto": {
+ "type": "object",
+ "properties": {
+ "contextId": {
+ "type": "string"
+ },
+ "contextType": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+ },
+ "MatrixRowDto": {
+ "type": "object",
+ "properties": {
+ "objectId": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "cells": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/MatrixCellDto"
+ }
+ }
+ }
+ },
+ "DynamicAttributeVo": {
+ "type": "object",
+ "properties": {
+ "attributeType": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "dataType": {
+ "type": "string"
+ },
+ "semanticType": {
+ "type": "string"
+ },
+ "attributeValuePredefined": {
+ "type": "boolean"
+ },
+ "attributeGroups": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "maxLength": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "entity": {
+ "type": "string",
+ "enum": [
+ "CLIENT",
+ "PRODUCT_ACCOUNT",
+ "CARD",
+ "CORPORATION",
+ "CHAIN",
+ "STORE",
+ "CAMPAIGN",
+ "ITEM"
+ ]
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date"
+ },
+ "defaultValue": {
+ "type": "string"
+ },
+ "attrGroupId": {
+ "type": "string"
+ }
+ }
+ },
+ "DynamicAttributeViewCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "lastUpdateDate": {
+ "$ref": "#/components/schemas/RangeFilterInstant"
+ },
+ "search": {
+ "type": "string"
+ },
+ "searchComponent": {
+ "type": "string"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "description": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attrGroupId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "dataType": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "effectiveFrom": {
+ "$ref": "#/components/schemas/LocalDateFilter"
+ },
+ "effectiveTo": {
+ "$ref": "#/components/schemas/LocalDateFilter"
+ },
+ "componentColumns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attributeGroups": {
+ "$ref": "#/components/schemas/StringFilter"
+ }
+ }
+ },
+ "LocalDateFilter": {
+ "type": "object",
+ "properties": {
+ "equals": {
+ "type": "string",
+ "format": "date"
+ },
+ "notEquals": {
+ "type": "string",
+ "format": "date"
+ },
+ "specified": {
+ "type": "boolean"
+ },
+ "in": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date"
+ }
+ },
+ "notIn": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date"
+ }
+ },
+ "greaterThan": {
+ "type": "string",
+ "format": "date"
+ },
+ "lessThan": {
+ "type": "string",
+ "format": "date"
+ },
+ "greaterThanOrEqual": {
+ "type": "string",
+ "format": "date"
+ },
+ "lessThanOrEqual": {
+ "type": "string",
+ "format": "date"
+ }
+ }
+ },
+ "DynamicAttributeCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "lastUpdateDate": {
+ "$ref": "#/components/schemas/RangeFilterInstant"
+ },
+ "search": {
+ "type": "string"
+ },
+ "searchComponent": {
+ "type": "string"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "description": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attrGroupId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "dataType": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "effectiveFrom": {
+ "$ref": "#/components/schemas/LocalDateFilter"
+ },
+ "effectiveTo": {
+ "$ref": "#/components/schemas/LocalDateFilter"
+ },
+ "componentColumns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "BooleanFilter": {
+ "type": "object",
+ "properties": {
+ "equals": {
+ "type": "boolean"
+ },
+ "notEquals": {
+ "type": "boolean"
+ },
+ "specified": {
+ "type": "boolean"
+ },
+ "in": {
+ "type": "array",
+ "items": {
+ "type": "boolean"
+ }
+ },
+ "notIn": {
+ "type": "array",
+ "items": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "CustomerAttributeValueChangeCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "lastUpdateDate": {
+ "$ref": "#/components/schemas/RangeFilterInstant"
+ },
+ "search": {
+ "type": "string"
+ },
+ "searchComponent": {
+ "type": "string"
+ },
+ "attributeId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "attributeName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "entity": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "entityId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "isSpecific": {
+ "$ref": "#/components/schemas/BooleanFilter"
+ },
+ "effectiveFrom": {
+ "$ref": "#/components/schemas/LocalDateFilter"
+ },
+ "effectiveTo": {
+ "$ref": "#/components/schemas/LocalDateFilter"
+ },
+ "effectiveOnly": {
+ "type": "boolean"
+ },
+ "csn": {
+ "type": "string"
+ },
+ "componentColumns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "DynamicAttributeValueDetailCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "valueSource": {
+ "type": "string"
+ },
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "definitionRecordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "entity": {
+ "type": "string"
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "csn": {
+ "type": "string"
+ }
+ }
+ },
+ "DynamicAttributeValueDetailVo": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "definitionRecordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "csn": {
+ "type": "string"
+ },
+ "cifNbr": {
+ "type": "string"
+ },
+ "isSpecific": {
+ "type": "boolean"
+ },
+ "valueSource": {
+ "type": "string"
+ },
+ "entity": {
+ "type": "string",
+ "enum": [
+ "CLIENT",
+ "PRODUCT_ACCOUNT",
+ "CARD",
+ "CORPORATION",
+ "CHAIN",
+ "STORE",
+ "CAMPAIGN",
+ "ITEM"
+ ]
+ },
+ "entityName": {
+ "type": "string"
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "entityValue": {
+ "type": "string"
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "attributeValue": {
+ "type": "string"
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "status": {
+ "type": "string"
+ },
+ "lastUpdateBy": {
+ "type": "string"
+ },
+ "lastUpdateDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "lastApproveBy": {
+ "type": "string"
+ },
+ "lastApproveDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "addOnVisible": {
+ "type": "boolean"
+ },
+ "historyEmpty": {
+ "type": "boolean"
+ }
+ }
+ },
+ "DynamicAttributeValueAddOnCriteria": {
+ "type": "object",
+ "properties": {
+ "csn": {
+ "type": "string"
+ },
+ "entity": {
+ "type": "string"
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "excludeRecordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "search": {
+ "type": "string"
+ }
+ }
+ },
+ "DynamicAttributeValueAddOnVo": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "attributeValue": {
+ "type": "string"
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "currentlyEffective": {
+ "type": "boolean"
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ },
+ "AttributeValueVo": {
+ "type": "object",
+ "properties": {
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeType": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ },
+ "entity": {
+ "type": "string"
+ },
+ "entityId": {
+ "type": "string"
+ },
+ "attrValue": {
+ "type": "string"
+ },
+ "effectiveFrom": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "effectiveTo": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "AttributeReferenceValue": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+ },
+ "AttributeVo": {
+ "type": "object",
+ "properties": {
+ "attributeType": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "dataType": {
+ "type": "string"
+ },
+ "semanticType": {
+ "type": "string"
+ },
+ "attributeValuePredefined": {
+ "type": "boolean"
+ },
+ "attributeGroups": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "maxLength": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ },
+ "AttributeDto": {
+ "type": "object",
+ "properties": {
+ "attributeType": {
+ "type": "string",
+ "enum": [
+ "CUSTOM",
+ "DYNAMIC",
+ "STATIC"
+ ]
+ },
+ "attributeId": {
+ "type": "string"
+ },
+ "attributeName": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "dataType": {
+ "type": "string"
+ },
+ "semanticType": {
+ "type": "string"
+ },
+ "attributeValuePredefined": {
+ "type": "boolean"
+ },
+ "preDefineAttributeValue": {
+ "type": "boolean"
+ },
+ "listOfValue": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attributeGroups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReferenceData"
+ }
+ },
+ "maxLength": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ },
+ "FolderAttributeDto": {
+ "type": "object",
+ "properties": {
+ "groupId": {
+ "type": "string"
+ },
+ "groupName": {
+ "type": "string"
+ },
+ "parentGroup": {
+ "type": "string"
+ },
+ "level": {
+ "type": "string",
+ "enum": [
+ "L1",
+ "L2",
+ "L3"
+ ]
+ },
+ "attributes": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeDto"
+ }
+ }
+ }
+ },
+ "AttributeCriteriaConfigDto": {
+ "type": "object",
+ "properties": {
+ "attributeId": {
+ "type": "string"
+ },
+ "criteriaFieldId": {
+ "type": "string"
+ },
+ "computeInd": {
+ "type": "string"
+ },
+ "compareValue": {
+ "type": "string"
+ },
+ "criteriaType": {
+ "type": "string"
+ }
+ }
+ },
+ "AttributeGroupCriteria": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "lastUpdateDate": {
+ "$ref": "#/components/schemas/RangeFilterInstant"
+ },
+ "search": {
+ "type": "string"
+ },
+ "searchComponent": {
+ "type": "string"
+ },
+ "groupId": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "groupName": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "parentGroup": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "level": {
+ "$ref": "#/components/schemas/StringFilter"
+ },
+ "componentColumns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "AttributeGroupVo": {
+ "type": "object",
+ "properties": {
+ "groupId": {
+ "type": "string"
+ },
+ "groupName": {
+ "type": "string"
+ },
+ "level": {
+ "type": "string"
+ },
+ "parentGroup": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "subGroup": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AttributeGroupVo"
+ }
+ }
+ }
+ },
+ "DeleteRequest": {
+ "type": "object",
+ "properties": {
+ "recordNo": {
+ "type": "integer",
+ "format": "int64"
+ }
+ },
+ "required": [
+ "recordNo"
+ ]
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/resources/specs/catalogue.json b/loyalty-mcp-server/src/main/resources/specs/catalogue.json
similarity index 100%
rename from src/main/resources/specs/catalogue.json
rename to loyalty-mcp-server/src/main/resources/specs/catalogue.json
diff --git a/src/main/resources/specs/customer.json b/loyalty-mcp-server/src/main/resources/specs/customer.json
similarity index 100%
rename from src/main/resources/specs/customer.json
rename to loyalty-mcp-server/src/main/resources/specs/customer.json
diff --git a/src/main/resources/specs/identity.json b/loyalty-mcp-server/src/main/resources/specs/identity.json
similarity index 100%
rename from src/main/resources/specs/identity.json
rename to loyalty-mcp-server/src/main/resources/specs/identity.json
diff --git a/src/main/resources/specs/marketing.json b/loyalty-mcp-server/src/main/resources/specs/marketing.json
similarity index 100%
rename from src/main/resources/specs/marketing.json
rename to loyalty-mcp-server/src/main/resources/specs/marketing.json
diff --git a/src/main/resources/specs/master.json b/loyalty-mcp-server/src/main/resources/specs/master.json
similarity index 100%
rename from src/main/resources/specs/master.json
rename to loyalty-mcp-server/src/main/resources/specs/master.json
diff --git a/src/main/resources/specs/reward.json b/loyalty-mcp-server/src/main/resources/specs/reward.json
similarity index 100%
rename from src/main/resources/specs/reward.json
rename to loyalty-mcp-server/src/main/resources/specs/reward.json
diff --git a/src/main/resources/specs/transaction.json b/loyalty-mcp-server/src/main/resources/specs/transaction.json
similarity index 100%
rename from src/main/resources/specs/transaction.json
rename to loyalty-mcp-server/src/main/resources/specs/transaction.json
diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/test/TestParsing.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/test/TestParsing.java
new file mode 100644
index 0000000..18f381e
--- /dev/null
+++ b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/test/TestParsing.java
@@ -0,0 +1,40 @@
+package dev.sonpx.loyalty.mcp.test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+
+public class TestParsing {
+ public static void main(String[] args) throws Exception {
+ String jsonResponse = new String(Files.readAllBytes(Paths.get("rules.json")));
+
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\"");
+ jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\"");
+
+ System.out.println("Transformed JSON: " + jsonResponse.substring(0, Math.min(200, jsonResponse.length())));
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new JavaTimeModule());
+ mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ try {
+ CampaignRuleDto[] rules = mapper.readValue(jsonResponse, CampaignRuleDto[].class);
+ System.out.println("Parsed directly as array, length: " + rules.length);
+ } catch (Exception parseEx) {
+ System.out.println("Failed to parse as array: " + parseEx.getMessage());
+ com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse);
+ if (root.has("content")) {
+ CampaignRuleDto[] rules = mapper.treeToValue(root.get("content"), CampaignRuleDto[].class);
+ System.out.println("Parsed from content, length: " + rules.length);
+ } else if (root.has("data")) {
+ CampaignRuleDto[] rules = mapper.treeToValue(root.get("data"), CampaignRuleDto[].class);
+ System.out.println("Parsed from data, length: " + rules.length);
+ } else {
+ throw parseEx;
+ }
+ }
+ }
+}
diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java
new file mode 100644
index 0000000..295e91a
--- /dev/null
+++ b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java
@@ -0,0 +1,93 @@
+package dev.sonpx.loyalty.mcp.tool;
+
+import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient;
+import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient;
+import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient;
+import dev.sonpx.loyalty.mcp.client.model.Campaign;
+import dev.sonpx.loyalty.mcp.client.model.CreateCampaignRequest;
+import dev.sonpx.loyalty.mcp.client.model.MemberTier;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.support.RestClientAdapter;
+import org.springframework.web.service.invoker.HttpServiceProxyFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.http.HttpMethod.GET;
+import static org.springframework.http.HttpMethod.POST;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+class LoyaltyAgentToolsRealApiTest {
+
+ private MockRestServiceServer loyaltyServer;
+ private MockRestServiceServer rewardServer;
+ private LoyaltyAgentTools tools;
+
+ @BeforeEach
+ void setUp() {
+ RestClient.Builder loyaltyRestClientBuilder = RestClient.builder().baseUrl("http://core.test");
+ loyaltyServer = MockRestServiceServer.bindTo(loyaltyRestClientBuilder).build();
+ RestClient loyaltyRestClient = loyaltyRestClientBuilder.build();
+
+ RestClient.Builder rewardRestClientBuilder = RestClient.builder().baseUrl("http://reward.test");
+ rewardServer = MockRestServiceServer.bindTo(rewardRestClientBuilder).build();
+ RestClient rewardRestClient = rewardRestClientBuilder.build();
+
+ MemberTierApiClient memberTierApiClient = createClient(loyaltyRestClient, MemberTierApiClient.class);
+ PointPoolApiClient pointPoolApiClient = createClient(rewardRestClient, PointPoolApiClient.class);
+ CampaignApiClient campaignApiClient = createClient(rewardRestClient, CampaignApiClient.class);
+ tools = new LoyaltyAgentTools(memberTierApiClient, pointPoolApiClient, campaignApiClient, loyaltyRestClient);
+ }
+
+ @Test
+ void listMemberTiersCallsRealEndpoint() {
+ loyaltyServer.expect(requestTo("http://core.test/svc/reward/api/decide-tier-view/list?page=0&size=100&status.in=A"))
+ .andExpect(method(GET))
+ .andRespond(withSuccess("[{\"key\":\"TIER_GOLD\",\"code\":\"GOLD\",\"name\":\"Gold\"}]", MediaType.APPLICATION_JSON));
+
+ MemberTier tier = tools.listMemberTiers().getFirst();
+
+ assertEquals("TIER_GOLD", tier.tierId());
+ assertEquals("Gold", tier.tierName());
+ loyaltyServer.verify();
+ }
+
+ @Test
+ void listPointPoolsCallsRealEndpoint() {
+ rewardServer.expect(requestTo("http://reward.test/api/pool-definition/all"))
+ .andExpect(method(GET))
+ .andRespond(withSuccess("[{\"poolId\":\"POOL_1\",\"poolName\":\"Main Pool\"}]", MediaType.APPLICATION_JSON));
+
+ assertEquals("POOL_1", tools.listPointPools().getFirst().poolId());
+ rewardServer.verify();
+ }
+
+ @Test
+ void createCampaignGeneratesIdAndUnwrapsOperationResultEntity() {
+ rewardServer.expect(requestTo("http://reward.test/api/campaign/csr/id/generate"))
+ .andExpect(method(GET))
+ .andRespond(withSuccess("CAMP_1", MediaType.TEXT_PLAIN));
+ rewardServer.expect(requestTo("http://reward.test/api/campaign/csr/create-draft"))
+ .andExpect(method(POST))
+ .andRespond(withSuccess(
+ "{\"message\":\"OK\",\"entity\":{\"campaignId\":\"CAMP_1\",\"name\":\"Summer\",\"status\":\"DRAFT\"}}",
+ MediaType.APPLICATION_JSON));
+
+ Campaign campaign = tools.createCampaign(new CreateCampaignRequest("Summer", "TIER_GOLD", "POOL_1"));
+
+ assertEquals("CAMP_1", campaign.campaignId());
+ assertEquals("DRAFT", campaign.status());
+ rewardServer.verify();
+ }
+
+ private static T createClient(RestClient restClient, Class clientType) {
+ HttpServiceProxyFactory factory = HttpServiceProxyFactory
+ .builderFor(RestClientAdapter.create(restClient))
+ .build();
+ return factory.createClient(clientType);
+ }
+}
diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java
new file mode 100644
index 0000000..60b0ed7
--- /dev/null
+++ b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java
@@ -0,0 +1,112 @@
+package dev.sonpx.loyalty.mcp.tool;
+
+import dev.sonpx.loyalty.mcp.draft.DependencyValidator;
+import dev.sonpx.loyalty.mcp.draft.DraftSessionManager;
+import dev.sonpx.loyalty.mcp.draft.model.DraftSession;
+import dev.sonpx.loyalty.mcp.tool.model.pool.CreatePoolDefinitionRequest;
+import dev.sonpx.loyalty.mcp.tool.model.pool.CreateSopRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.AddRuleToCampaignRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.SubmitCampaignDraftRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignDraftRequest;
+import dev.sonpx.loyalty.mcp.tool.pool.PointPoolTools;
+import dev.sonpx.loyalty.mcp.tool.reward.CampaignRuleMapper;
+import dev.sonpx.loyalty.mcp.tool.reward.CampaignRuleTools;
+import dev.sonpx.loyalty.mcp.tool.reward.CampaignTools;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestClient;
+
+import java.time.OffsetDateTime;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import static org.springframework.http.HttpMethod.POST;
+
+class McpOrchestratorTest {
+
+ private DraftSessionManager sessionManager;
+ private DependencyValidator dependencyValidator;
+ private MockRestServiceServer mockServer;
+
+ private PointPoolTools pointPoolTools;
+ private CampaignTools campaignTools;
+ private CampaignRuleTools campaignRuleTools;
+
+ @BeforeEach
+ void setUp() {
+ sessionManager = new DraftSessionManager();
+ dependencyValidator = new DependencyValidator();
+ RestClient.Builder restClientBuilder = RestClient.builder().baseUrl("http://reward.test");
+ mockServer = MockRestServiceServer.bindTo(restClientBuilder).build();
+ RestClient restClient = restClientBuilder.build();
+
+ pointPoolTools = new PointPoolTools(sessionManager, dependencyValidator, restClient);
+ campaignTools = new CampaignTools(restClient, sessionManager, dependencyValidator);
+ campaignRuleTools = new CampaignRuleTools(new CampaignRuleMapper(), restClient, sessionManager, dependencyValidator);
+ }
+
+ @Test
+ void testFullDependencyGraphAndErrorPropagation() {
+ String convId = "test-conv-123";
+
+ mockServer.expect(requestTo("http://reward.test/api/pool-definition/csr/create-draft"))
+ .andExpect(method(POST))
+ .andRespond(withSuccess("{\"poolId\":\"POOL-1\"}", MediaType.APPLICATION_JSON));
+ mockServer.expect(requestTo("http://reward.test/api/sop/csr/create-draft"))
+ .andExpect(method(POST))
+ .andRespond(withSuccess("{\"sopId\":\"SOP-1\"}", MediaType.APPLICATION_JSON));
+ mockServer.expect(requestTo("http://reward.test/api/campaign/csr/create-draft"))
+ .andExpect(method(POST))
+ .andRespond(withSuccess("{\"campaignId\":\"CAMP-1\",\"status\":\"DRAFT_CREATED\"}", MediaType.APPLICATION_JSON));
+ mockServer.expect(requestTo("http://reward.test/api/campaign/csr/create-draft"))
+ .andExpect(method(POST))
+ .andRespond(withStatus(HttpStatus.BAD_REQUEST).body("400 Bad Request"));
+
+ // 1. Create Pool Definition
+ CreatePoolDefinitionRequest poolReq = new CreatePoolDefinitionRequest(convId, "POOL-1", "Test Pool", "STANDARD", "USD");
+ Map poolRes = pointPoolTools.createPoolDefinition(poolReq);
+ assertEquals("POOL-1", poolRes.get("poolId"));
+
+ // 2. Create SOP
+ CreateSopRequest sopReq = new CreateSopRequest(convId, "SOP-1", "POOL-1", "Test SOP", "ACCRUAL");
+ Map sopRes = pointPoolTools.createSop(sopReq);
+ assertEquals("SOP-1", sopRes.get("sopId"));
+
+ // 3. Create Campaign Draft
+ UpdateCampaignDraftRequest updateCampReq = new UpdateCampaignDraftRequest(convId, "CAMP-1", "My Campaign", "TIER-1");
+ campaignTools.updateCampaignDraft(updateCampReq);
+
+ // 4. Add Rule to Campaign (Valid)
+ CreateCampaignRuleToolRequest ruleDetails = new CreateCampaignRuleToolRequest("CAMP-1", "RULE-1", "My Rule", "AWD", "Desc", OffsetDateTime.now(), OffsetDateTime.now(), "POOL-1", null);
+ AddRuleToCampaignRequest addRuleReq = new AddRuleToCampaignRequest(convId, "CAMP-1", "TXN-CODE-1", true, "POOL-1", ruleDetails);
+ Map ruleRes = campaignRuleTools.addRuleToCampaign(addRuleReq);
+ assertEquals("RULE-1", ruleRes.get("ruleId"));
+
+ // 5. Submit Campaign Draft
+ SubmitCampaignDraftRequest submitCampReq = new SubmitCampaignDraftRequest(convId);
+ Map submitCampRes = campaignTools.submitCampaignDraft(submitCampReq);
+ assertEquals("CAMP-1", submitCampRes.get("campaignId"));
+
+ // Test Error Propagation: Missing Pool ID for SOP
+ CreateSopRequest badSopReq = new CreateSopRequest(convId, "SOP-2", "", "Bad SOP", "ACCRUAL");
+ assertThrows(IllegalArgumentException.class, () -> pointPoolTools.createSop(badSopReq));
+
+ // Test Error Propagation: Add Rule missing required Pool ID
+ AddRuleToCampaignRequest badRuleReq = new AddRuleToCampaignRequest(convId, "CAMP-1", "TXN-CODE-2", true, "", ruleDetails);
+ assertThrows(IllegalArgumentException.class, () -> campaignRuleTools.addRuleToCampaign(badRuleReq));
+
+ // Test Error propagation from Core API
+ Map errorRes = campaignTools.submitCampaignDraft(submitCampReq);
+ assertTrue(((String)errorRes.get("error")).contains("400 Bad Request"));
+
+ mockServer.verify();
+ }
+}
diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java
new file mode 100644
index 0000000..d5eb29b
--- /dev/null
+++ b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java
@@ -0,0 +1,83 @@
+package dev.sonpx.loyalty.mcp.tool.reward;
+
+import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto;
+import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest;
+import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignRuleToolRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.OffsetDateTime;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CampaignRuleMapperTest {
+
+ private CampaignRuleMapper mapper;
+
+ @BeforeEach
+ void setUp() {
+ mapper = new CampaignRuleMapper();
+ }
+
+ @Test
+ void testToDto_CreateRequest_WithFormulaOne() {
+ CreateCampaignRuleToolRequest request = new CreateCampaignRuleToolRequest(
+ "CAMPAIGN-123",
+ "RULE-1",
+ "My Rule",
+ "AWD",
+ "Description",
+ OffsetDateTime.now(),
+ OffsetDateTime.now().plusDays(10),
+ "POOL-ABC",
+ new CreateCampaignRuleToolRequest.FormulaOneConfig(10.0, 100.0)
+ );
+
+ CampaignRuleDto dto = mapper.toDto(request);
+
+ assertNotNull(dto);
+ assertNotNull(dto.getCampaignId());
+ assertEquals("CAMPAIGN-123", dto.getCampaignId().getCode());
+ assertEquals("RULE-1", dto.getRuleId());
+ assertEquals("My Rule", dto.getRuleName());
+ assertEquals(CampaignRuleDto.RuleTypeEnum.AWD, dto.getRuleType());
+ assertNotNull(dto.getPoolId());
+ assertEquals("POOL-ABC", dto.getPoolId().getCode());
+ assertNotNull(dto.getCampaignFormulaOne());
+ assertEquals(10.0, dto.getCampaignFormulaOne().getAwardPerTxnAmt());
+ assertEquals(100.0, dto.getCampaignFormulaOne().getTxnAmt());
+ assertEquals(1, dto.getFormulaSequence().size());
+ assertEquals("1", dto.getFormulaSequence().get(0));
+ }
+
+ @Test
+ void testToDto_UpdateRequest_WithoutFormulaOne() {
+ UpdateCampaignRuleToolRequest request = new UpdateCampaignRuleToolRequest(
+ "CAMPAIGN-456",
+ "RULE-2",
+ "My Updated Rule",
+ "RED",
+ "Updated Description",
+ null,
+ null,
+ "POOL-XYZ",
+ null
+ );
+
+ CampaignRuleDto dto = mapper.toDto(request);
+
+ assertNotNull(dto);
+ assertNotNull(dto.getCampaignId());
+ assertEquals("CAMPAIGN-456", dto.getCampaignId().getCode());
+ assertEquals("RULE-2", dto.getRuleId());
+ assertEquals("My Updated Rule", dto.getRuleName());
+ assertEquals(CampaignRuleDto.RuleTypeEnum.RED, dto.getRuleType());
+ assertNotNull(dto.getPoolId());
+ assertEquals("POOL-XYZ", dto.getPoolId().getCode());
+ assertNull(dto.getCampaignFormulaOne());
+ assertTrue(dto.getFormulaSequence() == null || dto.getFormulaSequence().isEmpty());
+ }
+}
diff --git a/mvnw b/mvnw
old mode 100644
new mode 100755
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..59863bb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "agent-ui-temp",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "oxlint",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@radix-ui/react-avatar": "^1.2.2",
+ "@radix-ui/react-scroll-area": "^1.2.14",
+ "@radix-ui/react-slot": "^1.3.0",
+ "@stomp/stompjs": "^7.3.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^1.24.0",
+ "react": "^19.2.7",
+ "react-dom": "^19.2.7",
+ "react-markdown": "^10.1.0",
+ "remark-gfm": "^4.0.1",
+ "tailwind-merge": "^3.6.0",
+ "zustand": "^5.0.14"
+ },
+ "devDependencies": {
+ "@tailwindcss/typography": "^0.5.20",
+ "@types/node": "^24.13.2",
+ "@types/react": "^19.2.17",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.3",
+ "autoprefixer": "^10.5.4",
+ "oxlint": "^1.71.0",
+ "postcss": "^8.5.19",
+ "tailwindcss": "^3.4.19",
+ "tailwindcss-animate": "^1.0.7",
+ "typescript": "~6.0.2",
+ "vite": "^8.1.1"
+ }
+}
diff --git a/plan/master_plan.md b/plan/master_plan.md
new file mode 100644
index 0000000..03faf21
--- /dev/null
+++ b/plan/master_plan.md
@@ -0,0 +1,229 @@
+# Master Plan: Loyalty Agent Service
+
+_Focus hiện tại: MCP Orchestrator cho Reward Core API, dựa trên `loyalty-mcp-server/src/main/resources/specs/reward.json`._
+
+Core Reward API đang expose **11 CSR resources chính** theo cùng một pattern Maker-Checker:
+
+1. `Transaction Category`
+2. `Transaction Code`
+3. `Statement Output Pool (SOP)`
+4. `Pool Definition`
+5. `Pool Conversion Rate`
+6. `Currency Rate`
+7. `Event Maintenance`
+8. `Deduction Sequences`
+9. `Counter Definition`
+10. `Campaign`
+11. `Campaign Rule`
+
+Ngoài nhóm CSR trên, spec còn có các endpoint hỗ trợ rất quan trọng cho Agent như `id/generate`, `id/check`, `list`, `count`, `history`, `list-by-ids`, `list-by-codes`, `list-by-pool`, `precision-by-pools`, `find-default`, `exists-default`, `verifyCriteria`, `amount-to-use-view`, `decide-tier-view`.
+
+Vì Core API không sửa được, Loyalty Agent Service/MCP Server sẽ đóng vai trò **orchestrator + DTO builder + validator**: hướng dẫn user điền thông tin, tra cứu dữ liệu phụ thuộc, build DTO hợp lệ, validate trước, rồi mới gọi endpoint CSR tương ứng.
+
+---
+
+## Phase 1: Reward MCP Orchestrator
+
+_Mục tiêu: biến Reward OpenAPI thành bộ MCP tools dùng được trong hội thoại, ưu tiên luồng tạo Campaign/Rule/Pool end-to-end._
+
+### 1.1. Foundation Tools dùng chung
+
+Các resource CSR có pattern giống nhau, nên cần build một lớp client/tool generator hoặc wrapper dùng chung thay vì viết thủ công từng API.
+
+- `generate_id(resource)`: gọi `/api/{resource}/csr/id/generate`.
+- `check_id(resource, id)`: gọi `/api/{resource}/csr/id/check/{id}`.
+- `get_by_id(resource, id)` hoặc `get_by_record_no(resource, recordNo)`.
+- `list(resource, criteria, page, size, sort)`.
+- `count(resource, criteria)`.
+- `create_draft(resource, dto)`.
+- `edit_draft(resource, dto)`.
+- `approve/reject/bulk-approve/bulk-reject` chỉ expose sau, vì đây là hành động nhạy cảm.
+- `history(resource, criteria)` và `rejection_reason(recordNo)` dùng để giải thích trạng thái Maker-Checker cho user.
+
+Nguyên tắc: trong giai đoạn đầu, Agent ưu tiên tạo draft, không tự approve.
+
+### 1.2. DTO Builder & Validation Layer
+
+MCP Server cần giữ local draft theo conversation/session để user có thể build DTO từng bước.
+
+- Mỗi draft có `draftId`, `resource`, `dto`, `missingRequiredFields`, `warnings`, `lastCoreValidation`.
+- Không đưa các field `readOnly` vào request: `status`, `lastUpdateBy`, `lastUpdateDate`, `lastApproveBy`, `lastApproveDate`, `lastUpdateByName`, `lastApproveByName`.
+- Validate required fields theo schema trước khi gọi Core API.
+- Validate enum, maxLength/minLength, date/date-time format.
+- Với field `$ref: ReferenceData`, Agent phải tra cứu hoặc xác nhận object reference, không chỉ nhét string trần nếu Core client yêu cầu object.
+- Với nested DTO trong `CampaignRuleDto`, build theo template theo `ruleType` thay vì hỏi tất cả field.
+
+Required fields quan trọng theo spec:
+
+- `CampaignDto`: `campaignId`, `name`.
+- `CampaignRuleDto`: `campaignId`, `ruleId`, `ruleName`.
+- `PoolDefinitionDto`: `poolId`, `poolName`.
+- `TransactionCodeDto`: `code`, `description`.
+- `TransactionCategoryDto`: `code`, `name`.
+- `PoolConversionRateDto`: `code`, `description`.
+- `CurrencyRateDto`: `pcrCode`, `effectiveFrom`, `effectiveTo`, `buyRate`, `sellRate`.
+- `EventMaintenanceDto`: `eventId`, `description`, `occursNext`.
+- `DeductionSequencesDto`: `sequenceId`, `sequenceName`, `effectiveFrom`, `effectiveTo`, `isDefault`.
+- `CounterDefinitionDto`: `counterId`, `counterName`, `effectiveFrom`, `effectiveTo`, `entity`, `bucketPeriodUnit`, `whatToCount`, `resetType`, `resetValue`, `updateStateWhen`, `lateValuePosting`.
+
+### 1.3. Dependency Graph thực tế
+
+Không hard-code rằng Campaign Rule nối trực tiếp với Event. Trong schema hiện tại, `CampaignRuleDto` không có `eventId`; nó nối chủ yếu qua:
+
+- `campaignId`
+- `poolId` / `subPoolId`
+- `campaignTcLinkages`
+- `campaignCriteria`
+- `campaignFormula*`
+- `campaignRuleSchedule`
+- `marketingRewardRequest`
+- reward content theo channel `SMS`, `EMAIL`, `NOTIFY`
+
+Dependency graph đề xuất:
+
+1. **Reference/Lookup layer**
+ - `amount-to-use-view/list`
+ - `decide-tier-view/list`
+ - transaction code lookup: `/api/transaction-code/list-by-codes`
+ - pool lookup: `/api/pool-definition/list-by-ids`, `/api/pool-definition/precision-by-pools`
+ - deduction sequence default: `/api/deduction-seq/find-default`
+
+2. **Parameter layer**
+ - `Transaction Category`
+ - `Transaction Code`
+ - `Pool Conversion Rate`
+ - `Currency Rate`
+
+3. **Pool layer**
+ - `Statement Output Pool`
+ - `Pool Definition`
+ - optional `Counter Definition` nếu rule dùng cap/limit/counter formula
+
+4. **Campaign layer**
+ - `Campaign`
+
+5. **Rule layer**
+ - `Campaign Rule`
+ - validate criteria bằng `/api/campaign-rule/campaign-criteria/verify`
+ - kiểm tra liên kết bằng `/api/campaign-rule/list-by-pool`, `/api/campaign-rule/rule-linked-by-transaction-code`, `/api/campaign-rule/find-matching-rules`
+
+6. **Standalone/scheduled support**
+ - `Event Maintenance`
+ - `Deduction Sequences`
+
+### 1.4. Scope triển khai Phase 1
+
+Không làm cả 11 resource ngang nhau ngay. Ưu tiên theo giá trị end-to-end:
+
+**Milestone 1: Read/Lookup Tools**
+
+- Implement tool list/search cho `Campaign`, `Campaign Rule`, `Pool Definition`, `Transaction Code`.
+- Implement `generate_id` và `check_id` cho các resource chính.
+- Implement helper lookup: `list-by-codes`, `list-by-ids`, `precision-by-pools`, `amount-to-use-view/list`, `decide-tier-view/list`.
+- Output tool phải trả dữ liệu ngắn gọn để Agent dùng tiếp, không dump DTO lớn.
+
+**Milestone 2: Pool Setup**
+
+- Tool tạo draft `PoolConversionRateDto`.
+- Tool tạo draft `PoolDefinitionDto`.
+- Tool tạo draft `StatementOutputPoolDto` nếu workflow statement cần SOP.
+- Validate expiry policy: `FD`, `NE`, `ARD`, `MF`, `QF`, `YF`, `SP`, `AOM`.
+- Validate pool type: `BPT`, `CR`, `GFT`, `MI`, `LDR`.
+
+**Milestone 3: Campaign Setup**
+
+- Tool tạo draft `CampaignDto`.
+- Agent tự generate/check `campaignId` nếu user chưa có mã.
+- Giữ template tối thiểu: `campaignId`, `name`, `ownerName`, `description`, `campaignType`, `effectiveFrom`, `effectiveTo`.
+- Không yêu cầu user nhập `numOfRule`; field này nên derive/hiển thị từ Core nếu có.
+
+**Milestone 4: Campaign Rule Setup**
+
+- Tool tạo draft `CampaignRuleDto`.
+- Template theo `ruleType`: `AWD`, `RED`, `IRED`, `ADJ`, `CEP`, `REP`, `TEP`, `MAWD`.
+- Với earning/award flow, ưu tiên support:
+ - `campaignId`
+ - `ruleId`, `ruleName`
+ - `poolId`
+ - `campaignTcLinkages`
+ - `campaignCriteria`
+ - `campaignFormulaSetting`
+ - một trong các `campaignFormulaOne/Two/Four/Five/Six/Eight/Ten/Eleven`
+ - `campaignAwardLimits` nếu có cap/counter
+- Gọi `verifyCriteria` trước khi submit draft nếu user có nhập criteria expression.
+
+**Milestone 5: Approval Visibility**
+
+- Tool xem trạng thái draft, history, rejection reason.
+- Tool giải thích record đang ở trạng thái nào và cần maker/checker làm gì tiếp theo.
+- Chưa expose approve/reject mặc định; chỉ bật khi có phân quyền và xác nhận rõ.
+
+---
+
+## Phase 2: Memory & Conversation State
+
+_Mục tiêu: Agent nhớ chính xác DTO đang build, các reference đã chọn, và trạng thái Maker-Checker._
+
+- Lưu conversation memory và local draft state trong DB.
+- Context locking theo draft: khi user đang build `CampaignRuleDto`, Agent không tự chuyển sang draft khác trừ khi user xác nhận.
+- Cho phép nested flow có kiểm soát: nếu đang build rule nhưng thiếu pool, Agent có thể tạo sub-draft `PoolDefinitionDto`, hoàn tất xong quay lại rule.
+- Lưu các reference đã tạo/chọn gần nhất: `campaignId`, `ruleId`, `poolId`, `transactionCode`, `counterId`, `pcrCode`.
+- Khi user nói "dùng pool vừa tạo", Agent phải resolve từ memory thành reference rõ ràng và hỏi lại nếu có nhiều pool gần đây.
+- Mỗi draft cần có audit trail: user intent, fields collected, validation result, endpoint đã gọi, response recordNo/status.
+
+---
+
+## Phase 3: UI/Dashboard hỗ trợ Agent
+
+_Mục tiêu: giảm hội thoại dài bằng UI chuyên biệt cho DTO lớn._
+
+- **Draft Inspector:** hiển thị DTO hiện tại, missing fields, warnings, Core response.
+- **Dependency Viewer:** hiển thị Campaign -> Campaign Rule -> Pool -> Transaction Code/Counter/Formula.
+- **Interactive Forms:** form động sinh từ schema cho các DTO lớn như `CampaignRuleDto`.
+- **Criteria Builder:** UI hỗ trợ build và verify `campaignCriteria.criteria`.
+- **Formula Builder:** UI chọn formula type và chỉ hiện field liên quan.
+- **Maker-Checker Timeline:** show draft/history/rejection reason để user biết đang chờ bước nào.
+- Dùng WebSocket hiện tại để sync draft state real-time giữa chat và UI.
+
+---
+
+## Phase 4: Auto-Pilot & Simulation
+
+_Mục tiêu: từ intent tự nhiên tạo được campaign/rule/pool hoàn chỉnh nhưng vẫn kiểm soát được rủi ro._
+
+- Intent template: "Tạo campaign nhân đôi điểm sinh nhật", "Tạo campaign hoàn tiền 5%", "Tạo rule tặng điểm theo tier".
+- Agent tự lập execution plan trước khi gọi API: cần tạo/check resource nào, endpoint nào, payload summary nào.
+- Auto-create draft theo thứ tự phụ thuộc, dừng lại khi thiếu field bắt buộc hoặc Core reject.
+- Simulation trước approve:
+ - sample transaction
+ - matched transaction code
+ - pool impacted
+ - formula result
+ - cap/limit/counter impact
+- Human confirmation bắt buộc trước các hành động tạo nhiều draft hoặc gửi approve/reject.
+
+---
+
+## Nguyên tắc triển khai
+
+- OpenAPI spec là source of truth; không đoán DTO field ngoài schema.
+- Tool output phải ngắn, có cấu trúc, dễ đưa lại vào Agent context.
+- Mutating tools phải idempotent ở tầng Agent/MCP nếu có thể: có `draftId`, correlation id, request summary.
+- Mọi create/edit draft đều chạy local validation trước, rồi mới gọi Core.
+- Với action nhạy cảm (`approve`, `reject`, `bulk-*`, `delete`), bắt buộc xác nhận rõ từ user.
+- Không để Agent tự tạo/chỉnh quá nhiều resource ngầm nếu chưa trình bày execution plan.
+
+---
+
+## Backlog kỹ thuật gần nhất
+
+- [ ] Parse `reward.json` thành metadata nội bộ: resources, schemas, required fields, enums, readOnly fields, endpoints.
+- [ ] Implement generic Reward API client cho CSR pattern.
+- [ ] Implement MCP tools read-only cho Campaign/Rule/Pool/Transaction Code.
+- [ ] Implement local draft store cho `CampaignDto`, `PoolDefinitionDto`, `CampaignRuleDto`.
+- [ ] Implement validator dùng OpenAPI schema.
+- [ ] Implement create-draft flow cho Pool Definition.
+- [ ] Implement create-draft flow cho Campaign.
+- [ ] Implement create-draft flow cho Campaign Rule với `verifyCriteria`.
+- [ ] Add integration tests dùng mock Reward Core API.
+- [ ] Add UI Draft Inspector sau khi backend flow ổn định.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..cfbe62f
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,2649 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@radix-ui/react-avatar':
+ specifier: ^1.2.2
+ version: 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-scroll-area':
+ specifier: ^1.2.14
+ version: 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot':
+ specifier: ^1.3.0
+ version: 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@stomp/stompjs':
+ specifier: ^7.3.0
+ version: 7.3.0
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ lucide-react:
+ specifier: ^1.24.0
+ version: 1.24.0(react@19.2.7)
+ react:
+ specifier: ^19.2.7
+ version: 19.2.7
+ react-dom:
+ specifier: ^19.2.7
+ version: 19.2.7(react@19.2.7)
+ react-markdown:
+ specifier: ^10.1.0
+ version: 10.1.0(@types/react@19.2.17)(react@19.2.7)
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
+ tailwind-merge:
+ specifier: ^3.6.0
+ version: 3.6.0
+ zustand:
+ specifier: ^5.0.14
+ version: 5.0.14(@types/react@19.2.17)(react@19.2.7)
+ devDependencies:
+ '@tailwindcss/typography':
+ specifier: ^0.5.20
+ version: 0.5.20(tailwindcss@3.4.19)
+ '@types/node':
+ specifier: ^24.13.2
+ version: 24.13.3
+ '@types/react':
+ specifier: ^19.2.17
+ version: 19.2.17
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.17)
+ '@vitejs/plugin-react':
+ specifier: ^6.0.3
+ version: 6.0.3(vite@8.1.5(@types/node@24.13.3)(jiti@1.21.7))
+ autoprefixer:
+ specifier: ^10.5.4
+ version: 10.5.4(postcss@8.5.19)
+ oxlint:
+ specifier: ^1.71.0
+ version: 1.74.0
+ postcss:
+ specifier: ^8.5.19
+ version: 8.5.19
+ tailwindcss:
+ specifier: ^3.4.19
+ version: 3.4.19
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.19)
+ typescript:
+ specifier: ~6.0.2
+ version: 6.0.3
+ vite:
+ specifier: ^8.1.1
+ version: 8.1.5(@types/node@24.13.3)(jiti@1.21.7)
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@emnapi/core@1.11.1':
+ resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
+
+ '@emnapi/runtime@1.11.1':
+ resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
+
+ '@emnapi/wasi-threads@1.2.2':
+ resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@1.1.6':
+ resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1
+ '@emnapi/runtime': ^1.7.1
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@oxc-project/types@0.139.0':
+ resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==}
+
+ '@oxlint/binding-android-arm-eabi@1.74.0':
+ resolution: {integrity: sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxlint/binding-android-arm64@1.74.0':
+ resolution: {integrity: sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxlint/binding-darwin-arm64@1.74.0':
+ resolution: {integrity: sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint/binding-darwin-x64@1.74.0':
+ resolution: {integrity: sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint/binding-freebsd-x64@1.74.0':
+ resolution: {integrity: sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.74.0':
+ resolution: {integrity: sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm-musleabihf@1.74.0':
+ resolution: {integrity: sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm64-gnu@1.74.0':
+ resolution: {integrity: sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-arm64-musl@1.74.0':
+ resolution: {integrity: sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-ppc64-gnu@1.74.0':
+ resolution: {integrity: sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-gnu@1.74.0':
+ resolution: {integrity: sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-musl@1.74.0':
+ resolution: {integrity: sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-s390x-gnu@1.74.0':
+ resolution: {integrity: sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-gnu@1.74.0':
+ resolution: {integrity: sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-musl@1.74.0':
+ resolution: {integrity: sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-openharmony-arm64@1.74.0':
+ resolution: {integrity: sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxlint/binding-win32-arm64-msvc@1.74.0':
+ resolution: {integrity: sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint/binding-win32-ia32-msvc@1.74.0':
+ resolution: {integrity: sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxlint/binding-win32-x64-msvc@1.74.0':
+ resolution: {integrity: sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@radix-ui/number@1.1.2':
+ resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==}
+
+ '@radix-ui/primitive@1.1.5':
+ resolution: {integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==}
+
+ '@radix-ui/react-avatar@1.2.2':
+ resolution: {integrity: sha512-sST0qh8GzOB7besQ3tMLWLyngnRuSk0gc/Hm+667KYKQFCt6Y6ZXv25WlqM7dIDK54ULCh5+CHmk4LIolzfz+A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.3':
+ resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-context@1.2.0':
+ resolution: {integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-direction@1.1.2':
+ resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-presence@1.1.7':
+ resolution: {integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.7':
+ resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-scroll-area@1.2.14':
+ resolution: {integrity: sha512-bBODCWZK7JTbQLHs0uIP4f73wIWatakK4OS33UzkR1x897wu0PuO658a3f+6P2GEGyDzGYMuHRatMVoAk9WZTw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.3.0':
+ resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.2':
+ resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-is-hydrated@0.1.1':
+ resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.2':
+ resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@rolldown/binding-android-arm64@1.1.5':
+ resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.1.5':
+ resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.1.5':
+ resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.1.5':
+ resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
+ resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.1.5':
+ resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-arm64-musl@1.1.5':
+ resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-ppc64-gnu@1.1.5':
+ resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-s390x-gnu@1.1.5':
+ resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-gnu@1.1.5':
+ resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-musl@1.1.5':
+ resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-openharmony-arm64@1.1.5':
+ resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-wasm32-wasi@1.1.5':
+ resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [wasm32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.1.5':
+ resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.1.5':
+ resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/pluginutils@1.0.1':
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
+
+ '@stomp/stompjs@7.3.0':
+ resolution: {integrity: sha512-nKMLoFfJhrQAqkvvKd1vLq/cVBGCMwPRCD0LqW7UT1fecRx9C3GoKEIR2CYwVuErGeZu8w0kFkl2rlhPlqHVgQ==}
+
+ '@tailwindcss/typography@0.5.20':
+ resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || >=4.0.0 || insiders'
+
+ '@tybys/wasm-util@0.10.3':
+ resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
+
+ '@types/debug@4.1.13':
+ resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+
+ '@types/estree-jsx@1.0.5':
+ resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/hast@3.0.5':
+ resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/node@24.13.3':
+ resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.17':
+ resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
+
+ '@types/unist@2.0.11':
+ resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@ungap/structured-clone@1.3.3':
+ resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==}
+
+ '@vitejs/plugin-react@6.0.3':
+ resolution: {integrity: sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
+ babel-plugin-react-compiler: ^1.0.0
+ vite: ^8.0.0
+ peerDependenciesMeta:
+ '@rolldown/plugin-babel':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ autoprefixer@10.5.4:
+ resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+
+ bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
+ baseline-browser-mapping@2.10.43:
+ resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.28.6:
+ resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ camelcase-css@2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
+ caniuse-lite@1.0.30001806:
+ resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+ character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+ character-reference-invalid@2.0.1:
+ resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ class-variance-authority@0.7.1:
+ resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-named-character-reference@1.3.0:
+ resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ didyoumean@1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+ dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ electron-to-chromium@1.5.392:
+ resolution: {integrity: sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
+ estree-util-is-identifier-name@3.0.0:
+ resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
+
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fastq@1.20.1:
+ resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ fraction.js@5.3.4:
+ resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ hasown@2.0.4:
+ resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
+ engines: {node: '>= 0.4'}
+
+ hast-util-to-jsx-runtime@2.3.6:
+ resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ html-url-attributes@3.0.1:
+ resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
+
+ inline-style-parser@0.2.7:
+ resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
+
+ is-alphabetical@2.0.1:
+ resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
+
+ is-alphanumerical@2.0.1:
+ resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-core-module@2.16.2:
+ resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==}
+ engines: {node: '>= 0.4'}
+
+ is-decimal@2.0.1:
+ resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-hexadecimal@2.0.1:
+ resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ jiti@1.21.7:
+ resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
+ hasBin: true
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lilconfig@3.1.3:
+ resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ lucide-react@1.24.0:
+ resolution: {integrity: sha512-YT6mBD8lGKkg4nM39enlm94/sfJIiW0YKUT60fBy4YK8tai31ylg1VhGNWxkpSKHo9UagfnZqwIff3HTDQwXeA==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+ mdast-util-from-markdown@2.0.3:
+ resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+ mdast-util-mdx-expression@2.0.1:
+ resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
+
+ mdast-util-mdx-jsx@3.2.0:
+ resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==}
+
+ mdast-util-mdxjs-esm@2.0.1:
+ resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-hast@13.2.1:
+ resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
+
+ mdast-util-to-markdown@2.1.2:
+ resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromark-core-commonmark@2.0.3:
+ resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.1:
+ resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-space@2.0.1:
+ resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-decode-string@2.0.1:
+ resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ micromark@4.0.2:
+ resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.16:
+ resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ node-releases@2.0.51:
+ resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==}
+ engines: {node: '>=18'}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ oxlint@1.74.0:
+ resolution: {integrity: sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ oxlint-tsgolint: '>=0.24.0'
+ vite-plus: '*'
+ peerDependenciesMeta:
+ oxlint-tsgolint:
+ optional: true
+ vite-plus:
+ optional: true
+
+ parse-entities@4.0.2:
+ resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.2:
+ resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.5:
+ resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
+ engines: {node: '>=12'}
+
+ pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ postcss-import@15.1.0:
+ resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-js@4.1.0:
+ resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+
+ postcss-load-config@6.0.1:
+ resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ jiti: '>=1.21.0'
+ postcss: '>=8.0.9'
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+ postcss:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ postcss-nested@6.2.0:
+ resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+
+ postcss-selector-parser@6.0.10:
+ resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+ engines: {node: '>=4'}
+
+ postcss-selector-parser@6.1.4:
+ resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.5.19:
+ resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ property-information@7.2.0:
+ resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-dom@19.2.7:
+ resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
+ peerDependencies:
+ react: ^19.2.7
+
+ react-markdown@10.1.0:
+ resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
+ peerDependencies:
+ '@types/react': '>=18'
+ react: '>=18'
+
+ react@19.2.7:
+ resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
+ engines: {node: '>=0.10.0'}
+
+ read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ remark-gfm@4.0.1:
+ resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
+
+ remark-parse@11.0.0:
+ resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+
+ remark-rehype@11.1.2:
+ resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
+
+ remark-stringify@11.0.0:
+ resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
+ resolve@1.22.12:
+ resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rolldown@1.1.5:
+ resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ stringify-entities@4.0.4:
+ resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
+ style-to-js@1.1.21:
+ resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
+
+ style-to-object@1.0.14:
+ resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
+
+ sucrase@3.35.1:
+ resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tailwind-merge@3.6.0:
+ resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==}
+
+ tailwindcss-animate@1.0.7:
+ resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+
+ tailwindcss@3.4.19:
+ resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ trough@2.2.0:
+ resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ typescript@6.0.3:
+ resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@7.18.2:
+ resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
+
+ unified@11.0.5:
+ resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.2:
+ resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+
+ unist-util-visit@5.1.0:
+ resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite@8.1.5:
+ resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ '@vitejs/devtools': ^0.3.0
+ esbuild: ^0.27.0 || ^0.28.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ '@vitejs/devtools':
+ optional: true
+ esbuild:
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ zustand@5.0.14:
+ resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@emnapi/core@1.11.1':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.2
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.11.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.2.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ dependencies:
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@tybys/wasm-util': 0.10.3
+ optional: true
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.20.1
+
+ '@oxc-project/types@0.139.0': {}
+
+ '@oxlint/binding-android-arm-eabi@1.74.0':
+ optional: true
+
+ '@oxlint/binding-android-arm64@1.74.0':
+ optional: true
+
+ '@oxlint/binding-darwin-arm64@1.74.0':
+ optional: true
+
+ '@oxlint/binding-darwin-x64@1.74.0':
+ optional: true
+
+ '@oxlint/binding-freebsd-x64@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-musleabihf@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-gnu@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-musl@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-ppc64-gnu@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-gnu@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-musl@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-s390x-gnu@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-gnu@1.74.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-musl@1.74.0':
+ optional: true
+
+ '@oxlint/binding-openharmony-arm64@1.74.0':
+ optional: true
+
+ '@oxlint/binding-win32-arm64-msvc@1.74.0':
+ optional: true
+
+ '@oxlint/binding-win32-ia32-msvc@1.74.0':
+ optional: true
+
+ '@oxlint/binding-win32-x64-msvc@1.74.0':
+ optional: true
+
+ '@radix-ui/number@1.1.2': {}
+
+ '@radix-ui/primitive@1.1.5': {}
+
+ '@radix-ui/react-avatar@1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-presence@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-scroll-area@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.5
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@rolldown/binding-android-arm64@1.1.5':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.1.5':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.1.5':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-ppc64-gnu@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-s390x-gnu@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.1.5':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.1.5':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.1.5':
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.1.5':
+ dependencies:
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.1.5':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.1.5':
+ optional: true
+
+ '@rolldown/pluginutils@1.0.1': {}
+
+ '@stomp/stompjs@7.3.0': {}
+
+ '@tailwindcss/typography@0.5.20(tailwindcss@3.4.19)':
+ dependencies:
+ postcss-selector-parser: 6.0.10
+ tailwindcss: 3.4.19
+
+ '@tybys/wasm-util@0.10.3':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/debug@4.1.13':
+ dependencies:
+ '@types/ms': 2.1.0
+
+ '@types/estree-jsx@1.0.5':
+ dependencies:
+ '@types/estree': 1.0.9
+
+ '@types/estree@1.0.9': {}
+
+ '@types/hast@3.0.5':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@2.1.0': {}
+
+ '@types/node@24.13.3':
+ dependencies:
+ undici-types: 7.18.2
+
+ '@types/react-dom@19.2.3(@types/react@19.2.17)':
+ dependencies:
+ '@types/react': 19.2.17
+
+ '@types/react@19.2.17':
+ dependencies:
+ csstype: 3.2.3
+
+ '@types/unist@2.0.11': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@ungap/structured-clone@1.3.3': {}
+
+ '@vitejs/plugin-react@6.0.3(vite@8.1.5(@types/node@24.13.3)(jiti@1.21.7))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.1
+ vite: 8.1.5(@types/node@24.13.3)(jiti@1.21.7)
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.2
+
+ arg@5.0.2: {}
+
+ autoprefixer@10.5.4(postcss@8.5.19):
+ dependencies:
+ browserslist: 4.28.6
+ caniuse-lite: 1.0.30001806
+ fraction.js: 5.3.4
+ picocolors: 1.1.1
+ postcss: 8.5.19
+ postcss-value-parser: 4.2.0
+
+ bail@2.0.2: {}
+
+ baseline-browser-mapping@2.10.43: {}
+
+ binary-extensions@2.3.0: {}
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.28.6:
+ dependencies:
+ baseline-browser-mapping: 2.10.43
+ caniuse-lite: 1.0.30001806
+ electron-to-chromium: 1.5.392
+ node-releases: 2.0.51
+ update-browserslist-db: 1.2.3(browserslist@4.28.6)
+
+ camelcase-css@2.0.1: {}
+
+ caniuse-lite@1.0.30001806: {}
+
+ ccount@2.0.1: {}
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ character-entities@2.0.2: {}
+
+ character-reference-invalid@2.0.1: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ class-variance-authority@0.7.1:
+ dependencies:
+ clsx: 2.1.1
+
+ clsx@2.1.1: {}
+
+ comma-separated-tokens@2.0.3: {}
+
+ commander@4.1.1: {}
+
+ cssesc@3.0.0: {}
+
+ csstype@3.2.3: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.3.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ dequal@2.0.3: {}
+
+ detect-libc@2.1.2: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ didyoumean@1.2.2: {}
+
+ dlv@1.1.3: {}
+
+ electron-to-chromium@1.5.392: {}
+
+ es-errors@1.3.0: {}
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ estree-util-is-identifier-name@3.0.0: {}
+
+ extend@3.0.2: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fastq@1.20.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fdir@6.5.0(picomatch@4.0.5):
+ optionalDependencies:
+ picomatch: 4.0.5
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ fraction.js@5.3.4: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ hasown@2.0.4:
+ dependencies:
+ function-bind: 1.1.2
+
+ hast-util-to-jsx-runtime@2.3.6:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/hast': 3.0.5
+ '@types/unist': 3.0.3
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ hast-util-whitespace: 3.0.0
+ mdast-util-mdx-expression: 2.0.1
+ mdast-util-mdx-jsx: 3.2.0
+ mdast-util-mdxjs-esm: 2.0.1
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ style-to-js: 1.1.21
+ unist-util-position: 5.0.0
+ vfile-message: 4.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+
+ html-url-attributes@3.0.1: {}
+
+ inline-style-parser@0.2.7: {}
+
+ is-alphabetical@2.0.1: {}
+
+ is-alphanumerical@2.0.1:
+ dependencies:
+ is-alphabetical: 2.0.1
+ is-decimal: 2.0.1
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-core-module@2.16.2:
+ dependencies:
+ hasown: 2.0.4
+
+ is-decimal@2.0.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-hexadecimal@2.0.1: {}
+
+ is-number@7.0.0: {}
+
+ is-plain-obj@4.1.0: {}
+
+ jiti@1.21.7: {}
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ lilconfig@3.1.3: {}
+
+ lines-and-columns@1.2.4: {}
+
+ longest-streak@3.1.0: {}
+
+ lucide-react@1.24.0(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+
+ markdown-table@3.0.4: {}
+
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ mdast-util-from-markdown@2.0.3:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.2
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-decode-string: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdx-expression@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdx-jsx@3.2.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ parse-entities: 4.0.2
+ stringify-entities: 4.0.4
+ unist-util-stringify-position: 4.0.0
+ vfile-message: 4.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdxjs-esm@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.1
+
+ mdast-util-to-hast@13.2.1:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ '@ungap/structured-clone': 1.3.3
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.1
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+
+ mdast-util-to-markdown@2.1.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-classify-character: 2.0.1
+ micromark-util-decode-string: 2.0.1
+ unist-util-visit: 5.1.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
+ merge2@1.4.1: {}
+
+ micromark-core-commonmark@2.0.3:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.0.1
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.1
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-space@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-decode-string@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ micromark-util-character: 2.1.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ micromark@4.0.2:
+ dependencies:
+ '@types/debug': 4.1.13
+ debug: 4.4.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.2
+
+ ms@2.1.3: {}
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.16: {}
+
+ node-releases@2.0.51: {}
+
+ normalize-path@3.0.0: {}
+
+ object-assign@4.1.1: {}
+
+ object-hash@3.0.0: {}
+
+ oxlint@1.74.0:
+ optionalDependencies:
+ '@oxlint/binding-android-arm-eabi': 1.74.0
+ '@oxlint/binding-android-arm64': 1.74.0
+ '@oxlint/binding-darwin-arm64': 1.74.0
+ '@oxlint/binding-darwin-x64': 1.74.0
+ '@oxlint/binding-freebsd-x64': 1.74.0
+ '@oxlint/binding-linux-arm-gnueabihf': 1.74.0
+ '@oxlint/binding-linux-arm-musleabihf': 1.74.0
+ '@oxlint/binding-linux-arm64-gnu': 1.74.0
+ '@oxlint/binding-linux-arm64-musl': 1.74.0
+ '@oxlint/binding-linux-ppc64-gnu': 1.74.0
+ '@oxlint/binding-linux-riscv64-gnu': 1.74.0
+ '@oxlint/binding-linux-riscv64-musl': 1.74.0
+ '@oxlint/binding-linux-s390x-gnu': 1.74.0
+ '@oxlint/binding-linux-x64-gnu': 1.74.0
+ '@oxlint/binding-linux-x64-musl': 1.74.0
+ '@oxlint/binding-openharmony-arm64': 1.74.0
+ '@oxlint/binding-win32-arm64-msvc': 1.74.0
+ '@oxlint/binding-win32-ia32-msvc': 1.74.0
+ '@oxlint/binding-win32-x64-msvc': 1.74.0
+
+ parse-entities@4.0.2:
+ dependencies:
+ '@types/unist': 2.0.11
+ character-entities-legacy: 3.0.0
+ character-reference-invalid: 2.0.1
+ decode-named-character-reference: 1.3.0
+ is-alphanumerical: 2.0.1
+ is-decimal: 2.0.1
+ is-hexadecimal: 2.0.1
+
+ path-parse@1.0.7: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.2: {}
+
+ picomatch@4.0.5: {}
+
+ pify@2.3.0: {}
+
+ pirates@4.0.7: {}
+
+ postcss-import@15.1.0(postcss@8.5.19):
+ dependencies:
+ postcss: 8.5.19
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.12
+
+ postcss-js@4.1.0(postcss@8.5.19):
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.5.19
+
+ postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.19):
+ dependencies:
+ lilconfig: 3.1.3
+ optionalDependencies:
+ jiti: 1.21.7
+ postcss: 8.5.19
+
+ postcss-nested@6.2.0(postcss@8.5.19):
+ dependencies:
+ postcss: 8.5.19
+ postcss-selector-parser: 6.1.4
+
+ postcss-selector-parser@6.0.10:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-selector-parser@6.1.4:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.5.19:
+ dependencies:
+ nanoid: 3.3.16
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ property-information@7.2.0: {}
+
+ queue-microtask@1.2.3: {}
+
+ react-dom@19.2.7(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ scheduler: 0.27.0
+
+ react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ '@types/react': 19.2.17
+ devlop: 1.1.0
+ hast-util-to-jsx-runtime: 2.3.6
+ html-url-attributes: 3.0.1
+ mdast-util-to-hast: 13.2.1
+ react: 19.2.7
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ unified: 11.0.5
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ react@19.2.7: {}
+
+ read-cache@1.0.0:
+ dependencies:
+ pify: 2.3.0
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.2
+
+ remark-gfm@4.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-gfm: 3.1.0
+ micromark-extension-gfm: 3.0.0
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-parse@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ micromark-util-types: 2.0.2
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-rehype@11.1.2:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ mdast-util-to-hast: 13.2.1
+ unified: 11.0.5
+ vfile: 6.0.3
+
+ remark-stringify@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-to-markdown: 2.1.2
+ unified: 11.0.5
+
+ resolve@1.22.12:
+ dependencies:
+ es-errors: 1.3.0
+ is-core-module: 2.16.2
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.1.0: {}
+
+ rolldown@1.1.5:
+ dependencies:
+ '@oxc-project/types': 0.139.0
+ '@rolldown/pluginutils': 1.0.1
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.1.5
+ '@rolldown/binding-darwin-arm64': 1.1.5
+ '@rolldown/binding-darwin-x64': 1.1.5
+ '@rolldown/binding-freebsd-x64': 1.1.5
+ '@rolldown/binding-linux-arm-gnueabihf': 1.1.5
+ '@rolldown/binding-linux-arm64-gnu': 1.1.5
+ '@rolldown/binding-linux-arm64-musl': 1.1.5
+ '@rolldown/binding-linux-ppc64-gnu': 1.1.5
+ '@rolldown/binding-linux-s390x-gnu': 1.1.5
+ '@rolldown/binding-linux-x64-gnu': 1.1.5
+ '@rolldown/binding-linux-x64-musl': 1.1.5
+ '@rolldown/binding-openharmony-arm64': 1.1.5
+ '@rolldown/binding-wasm32-wasi': 1.1.5
+ '@rolldown/binding-win32-arm64-msvc': 1.1.5
+ '@rolldown/binding-win32-x64-msvc': 1.1.5
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ scheduler@0.27.0: {}
+
+ source-map-js@1.2.1: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ stringify-entities@4.0.4:
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+
+ style-to-js@1.1.21:
+ dependencies:
+ style-to-object: 1.0.14
+
+ style-to-object@1.0.14:
+ dependencies:
+ inline-style-parser: 0.2.7
+
+ sucrase@3.35.1:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ commander: 4.1.1
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.7
+ tinyglobby: 0.2.17
+ ts-interface-checker: 0.1.13
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tailwind-merge@3.6.0: {}
+
+ tailwindcss-animate@1.0.7(tailwindcss@3.4.19):
+ dependencies:
+ tailwindcss: 3.4.19
+
+ tailwindcss@3.4.19:
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.6.0
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.3
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.7
+ lilconfig: 3.1.3
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.19
+ postcss-import: 15.1.0(postcss@8.5.19)
+ postcss-js: 4.1.0(postcss@8.5.19)
+ postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.19)
+ postcss-nested: 6.2.0(postcss@8.5.19)
+ postcss-selector-parser: 6.1.4
+ resolve: 1.22.12
+ sucrase: 3.35.1
+ transitivePeerDependencies:
+ - tsx
+ - yaml
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.5)
+ picomatch: 4.0.5
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ trim-lines@3.0.1: {}
+
+ trough@2.2.0: {}
+
+ ts-interface-checker@0.1.13: {}
+
+ tslib@2.8.1:
+ optional: true
+
+ typescript@6.0.3: {}
+
+ undici-types@7.18.2: {}
+
+ unified@11.0.5:
+ dependencies:
+ '@types/unist': 3.0.3
+ bail: 2.0.2
+ devlop: 1.1.0
+ extend: 3.0.2
+ is-plain-obj: 4.1.0
+ trough: 2.2.0
+ vfile: 6.0.3
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.1.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ update-browserslist-db@1.2.3(browserslist@4.28.6):
+ dependencies:
+ browserslist: 4.28.6
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ util-deprecate@1.0.2: {}
+
+ vfile-message@4.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.3
+
+ vite@8.1.5(@types/node@24.13.3)(jiti@1.21.7):
+ dependencies:
+ lightningcss: 1.32.0
+ picomatch: 4.0.5
+ postcss: 8.5.19
+ rolldown: 1.1.5
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 24.13.3
+ fsevents: 2.3.3
+ jiti: 1.21.7
+
+ zustand@5.0.14(@types/react@19.2.17)(react@19.2.7):
+ optionalDependencies:
+ '@types/react': 19.2.17
+ react: 19.2.7
+
+ zwitch@2.0.4: {}
diff --git a/pom.xml b/pom.xml
index 94accbd..701b483 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,93 +7,28 @@
org.springframework.boot
spring-boot-starter-parent
- 4.1.0
+ 4.0.0
dev.sonpx.loyalty
- loyalty-agent-service
+ loyalty-agent-service-parent
0.0.1-SNAPSHOT
- loyalty-agent-service
- AI Agent Service to orchestrate Loyalty core system
+ pom
+ loyalty-agent-service-parent
+ Parent POM for Loyalty Agent Service
+
+
+ loyalty-agent
+ loyalty-mcp-server
+
25
2.0.0
+ 1.18.38
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
-
- org.springframework.ai
- spring-ai-starter-model-ollama
-
-
-
-
- org.springframework.boot
- spring-boot-starter-webflux
-
-
-
-
- org.springframework.boot
- spring-boot-starter-oauth2-client
-
-
-
-
- org.projectlombok
- lombok
- true
-
-
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- 2.7.0
-
-
-
-
- org.openapitools
- jackson-databind-nullable
- 0.2.6
-
-
- jakarta.annotation
- jakarta.annotation-api
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
- org.apache.httpcomponents
- httpclient
- 4.5.14
-
-
- org.apache.httpcomponents
- httpmime
- 4.5.14
-
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
@@ -106,236 +41,6 @@
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 3.5.0
-
-
- add-source
- generate-sources
-
- add-source
-
-
-
- ${project.build.directory}/generated-sources/openapi/src/main/java
-
-
-
-
-
-
-
-
-
-
- gen
-
-
-
-
-
- org.openapitools
- openapi-generator-maven-plugin
- 7.23.0
-
- true
-
- org.springframework.util.MultiValueMap
-
-
- org.springframework.util.MultiValueMap
-
- user_name=user_name_raw,setAttributeId=set_attribute_id_val
-
-
-
- generate-attribute
- generate
-
- ${project.basedir}/src/main/resources/specs/attribute.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.attribute
- dev.sonpx.loyalty.agent.client.model.attribute
- false
- false
-
- true
- true
-
-
-
-
-
- generate-catalogue
- generate
-
- ${project.basedir}/src/main/resources/specs/catalogue.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.catalogue
- dev.sonpx.loyalty.agent.client.model.catalogue
- false
- false
-
- true
- true
-
-
-
-
- generate-customer
- generate
-
- ${project.basedir}/src/main/resources/specs/customer.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.customer
- dev.sonpx.loyalty.agent.client.model.customer
- false
- false
-
- true
- true
-
-
-
-
- generate-identity
- generate
-
- ${project.basedir}/src/main/resources/specs/identity.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.identity
- dev.sonpx.loyalty.agent.client.model.identity
- false
- false
-
- true
- true
-
-
-
-
- generate-marketing
- generate
-
- ${project.basedir}/src/main/resources/specs/marketing.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.marketing
- dev.sonpx.loyalty.agent.client.model.marketing
- false
- false
-
- true
- true
-
-
-
-
- generate-master
- generate
-
- ${project.basedir}/src/main/resources/specs/master.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.master
- dev.sonpx.loyalty.agent.client.model.master
- false
- false
-
- true
- true
-
-
-
-
-
- generate-reward
- generate
-
- ${project.basedir}/src/main/resources/specs/reward.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.reward
- dev.sonpx.loyalty.agent.client.model.reward
- false
- false
-
- true
- true
-
-
-
-
- generate-transaction
- generate
-
- ${project.basedir}/src/main/resources/specs/transaction.json
- java
- native
- dev.sonpx.loyalty.agent.client.api.transaction
- dev.sonpx.loyalty.agent.client.model.transaction
- false
- false
-
- true
- true
-
-
-
-
-
-
-
-
- com.google.code.maven-replacer-plugin
- replacer
- 1.5.3
-
-
- process-sources
-
- replace
-
-
-
-
-
- ${project.basedir}/target/generated-sources/openapi/src/main/java/**/*Api.java
-
-
-
- (?<!\.)MultiValueMapStringString
- dev.sonpx.loyalty.agent.client.model.transaction.MultiValueMapStringString
-
-
-
-
-
-
-
-
-
spring-milestones
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..2e7af2b
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..6893eb1
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/icons.svg b/public/icons.svg
new file mode 100644
index 0000000..e952219
--- /dev/null
+++ b/public/icons.svg
@@ -0,0 +1,24 @@
+
diff --git a/src/App.css b/src/App.css
new file mode 100644
index 0000000..f90339d
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,184 @@
+.counter {
+ font-size: 16px;
+ padding: 5px 10px;
+ border-radius: 5px;
+ color: var(--accent);
+ background: var(--accent-bg);
+ border: 2px solid transparent;
+ transition: border-color 0.3s;
+ margin-bottom: 24px;
+
+ &:hover {
+ border-color: var(--accent-border);
+ }
+ &:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+ }
+}
+
+.hero {
+ position: relative;
+
+ .base,
+ .framework,
+ .vite {
+ inset-inline: 0;
+ margin: 0 auto;
+ }
+
+ .base {
+ width: 170px;
+ position: relative;
+ z-index: 0;
+ }
+
+ .framework,
+ .vite {
+ position: absolute;
+ }
+
+ .framework {
+ z-index: 1;
+ top: 34px;
+ height: 28px;
+ transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
+ scale(1.4);
+ }
+
+ .vite {
+ z-index: 0;
+ top: 107px;
+ height: 26px;
+ width: auto;
+ transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
+ scale(0.8);
+ }
+}
+
+#center {
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ place-content: center;
+ place-items: center;
+ flex-grow: 1;
+
+ @media (max-width: 1024px) {
+ padding: 32px 20px 24px;
+ gap: 18px;
+ }
+}
+
+#next-steps {
+ display: flex;
+ border-top: 1px solid var(--border);
+ text-align: left;
+
+ & > div {
+ flex: 1 1 0;
+ padding: 32px;
+ @media (max-width: 1024px) {
+ padding: 24px 20px;
+ }
+ }
+
+ .icon {
+ margin-bottom: 16px;
+ width: 22px;
+ height: 22px;
+ }
+
+ @media (max-width: 1024px) {
+ flex-direction: column;
+ text-align: center;
+ }
+}
+
+#docs {
+ border-right: 1px solid var(--border);
+
+ @media (max-width: 1024px) {
+ border-right: none;
+ border-bottom: 1px solid var(--border);
+ }
+}
+
+#next-steps ul {
+ list-style: none;
+ padding: 0;
+ display: flex;
+ gap: 8px;
+ margin: 32px 0 0;
+
+ .logo {
+ height: 18px;
+ }
+
+ a {
+ color: var(--text-h);
+ font-size: 16px;
+ border-radius: 6px;
+ background: var(--social-bg);
+ display: flex;
+ padding: 6px 12px;
+ align-items: center;
+ gap: 8px;
+ text-decoration: none;
+ transition: box-shadow 0.3s;
+
+ &:hover {
+ box-shadow: var(--shadow);
+ }
+ .button-icon {
+ height: 18px;
+ width: 18px;
+ }
+ }
+
+ @media (max-width: 1024px) {
+ margin-top: 20px;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ li {
+ flex: 1 1 calc(50% - 8px);
+ }
+
+ a {
+ width: 100%;
+ justify-content: center;
+ box-sizing: border-box;
+ }
+ }
+}
+
+#spacer {
+ height: 88px;
+ border-top: 1px solid var(--border);
+ @media (max-width: 1024px) {
+ height: 48px;
+ }
+}
+
+.ticks {
+ position: relative;
+ width: 100%;
+
+ &::before,
+ &::after {
+ content: '';
+ position: absolute;
+ top: -4.5px;
+ border: 5px solid transparent;
+ }
+
+ &::before {
+ left: 0;
+ border-left-color: var(--border);
+ }
+ &::after {
+ right: 0;
+ border-right-color: var(--border);
+ }
+}
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 0000000..3257eb5
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,9 @@
+import { ChatLayout } from '@/components/chat/ChatLayout'
+
+function App() {
+ return (
+
+ )
+}
+
+export default App
diff --git a/src/assets/hero.png b/src/assets/hero.png
new file mode 100644
index 0000000..02251f4
Binary files /dev/null and b/src/assets/hero.png differ
diff --git a/src/assets/react.svg b/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/vite.svg b/src/assets/vite.svg
new file mode 100644
index 0000000..5101b67
--- /dev/null
+++ b/src/assets/vite.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/campaign/CampaignList.tsx b/src/components/campaign/CampaignList.tsx
new file mode 100644
index 0000000..95c7877
--- /dev/null
+++ b/src/components/campaign/CampaignList.tsx
@@ -0,0 +1,109 @@
+import { useEffect, useState } from 'react';
+
+interface Campaign {
+ id: string;
+ name: string;
+ status: string;
+ startDate?: string;
+ endDate?: string;
+}
+
+export function CampaignList() {
+ const [campaigns, setCampaigns] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [selectedCampaignId, setSelectedCampaignId] = useState(null);
+ const [rules, setRules] = useState([]);
+ const [loadingRules, setLoadingRules] = useState(false);
+
+ useEffect(() => {
+ fetch('http://localhost:9332/api/v1/agent/campaigns')
+ .then((res) => res.json())
+ .then((data) => {
+ if (Array.isArray(data)) setCampaigns(data);
+ else if (data && Array.isArray(data.content)) setCampaigns(data.content);
+ else if (data && Array.isArray(data.data)) setCampaigns(data.data);
+ else setCampaigns([]);
+ })
+ .catch((err) => {
+ console.error('Failed to fetch campaigns', err);
+ setCampaigns([]);
+ })
+ .finally(() => setLoading(false));
+ }, []);
+
+ const handleSelectCampaign = (id: string) => {
+ if (selectedCampaignId === id) {
+ setSelectedCampaignId(null);
+ setRules([]);
+ return;
+ }
+
+ setSelectedCampaignId(id);
+ setLoadingRules(true);
+ fetch(`http://localhost:9332/api/v1/agent/campaigns/${id}/rules`)
+ .then((res) => res.json())
+ .then((data) => {
+ if (Array.isArray(data)) setRules(data);
+ else if (data && Array.isArray(data.content)) setRules(data.content);
+ else if (data && Array.isArray(data.data)) setRules(data.data);
+ else setRules([]);
+ })
+ .catch((err) => {
+ console.error('Failed to fetch rules', err);
+ setRules([]);
+ })
+ .finally(() => setLoadingRules(false));
+ };
+
+ if (loading) {
+ return Loading campaigns...
;
+ }
+
+ if (campaigns.length === 0) {
+ return No campaigns found.
;
+ }
+
+ return (
+
+ {campaigns.map((camp) => (
+
+
handleSelectCampaign(camp.id)}
+ className={`glass-card rounded-xl p-4 text-sm group cursor-pointer relative overflow-hidden transition-all duration-200 ${selectedCampaignId === camp.id ? 'ring-2 ring-primary/50' : ''}`}>
+
+
+
{camp.name || 'Unnamed Campaign'}
+
+
+
+ {camp.status === 'A' ? 'Active' : camp.status}
+
+ {camp.id}
+
+
+
+
+ {selectedCampaignId === camp.id && (
+
+ {loadingRules ? (
+
Loading rules...
+ ) : rules.length === 0 ? (
+
No rules found.
+ ) : (
+ rules.map((rule, idx) => (
+
+
{rule.name || 'Unnamed Rule'}
+
{rule.description || 'No description'}
+ {rule.status && (
+
Status: {rule.status}
+ )}
+
+ ))
+ )}
+
+ )}
+
+ ))}
+
+ );
+}
diff --git a/src/components/chat/ChatBottombar.tsx b/src/components/chat/ChatBottombar.tsx
new file mode 100644
index 0000000..98dec69
--- /dev/null
+++ b/src/components/chat/ChatBottombar.tsx
@@ -0,0 +1,60 @@
+import { useState } from 'react'
+import type { KeyboardEvent } from 'react'
+import { Textarea } from '@/components/ui/textarea'
+import { Button } from '@/components/ui/button'
+import { useChatStore } from '@/store/useChatStore'
+import { SendHorizontal } from 'lucide-react'
+
+export function ChatBottombar() {
+ const [input, setInput] = useState('')
+ const sendMessage = useChatStore((state) => state.sendMessage)
+ const isConnected = useChatStore((state) => state.isConnected)
+ const isConnecting = useChatStore((state) => state.isConnecting)
+
+ const handleSend = () => {
+ if (input.trim() && isConnected) {
+ sendMessage(input.trim())
+ setInput('')
+ }
+ }
+
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ handleSend()
+ }
+ }
+
+ return (
+
+
+
+ {!isConnected && !isConnecting && (
+
+ Disconnected from agent server. Retrying...
+
+ )}
+ {isConnecting && (
+
+ Connecting to agent server...
+
+ )}
+
+ )
+}
diff --git a/src/components/chat/ChatBubble.tsx b/src/components/chat/ChatBubble.tsx
new file mode 100644
index 0000000..b26365f
--- /dev/null
+++ b/src/components/chat/ChatBubble.tsx
@@ -0,0 +1,48 @@
+import { Avatar, AvatarFallback } from '@/components/ui/avatar'
+import { cn } from '@/lib/utils'
+import ReactMarkdown from 'react-markdown'
+import remarkGfm from 'remark-gfm'
+import type { Message } from '@/store/useChatStore'
+
+export function ChatBubble({ message }: { message: Message }) {
+ const isAgent = message.role === 'agent'
+
+ return (
+
+ {isAgent && (
+
+ AG
+
+ )}
+
+ {message.toolStatus && (
+
+ {message.toolStatus}
+
+ )}
+
+ {isAgent ? (
+
+
+ {message.content}
+
+ {message.isStreaming && }
+
+ ) : (
+
{message.content}
+ )}
+
+
+ {!isAgent && (
+
+ U
+
+ )}
+
+ )
+}
diff --git a/src/components/chat/ChatHistoryList.tsx b/src/components/chat/ChatHistoryList.tsx
new file mode 100644
index 0000000..d3f864e
--- /dev/null
+++ b/src/components/chat/ChatHistoryList.tsx
@@ -0,0 +1,40 @@
+import { MessageSquare } from 'lucide-react';
+
+interface ChatSession {
+ id: string;
+ title: string;
+ date: string;
+}
+
+const MOCK_SESSIONS: ChatSession[] = [
+ { id: '1', title: 'Phân tích Campaign Noel 2026', date: 'Vừa xong' },
+ { id: '2', title: 'Tại sao Rule giảm giá không hoạt động?', date: '2 giờ trước' },
+ { id: '3', title: 'Tạo Campaign sinh nhật khách hàng', date: 'Hôm qua' },
+ { id: '4', title: 'Kiểm tra điểm thưởng KH VIP', date: 'Hôm qua' },
+ { id: '5', title: 'Lỗi đồng bộ dữ liệu CRM', date: '3 ngày trước' },
+];
+
+export function ChatHistoryList() {
+ return (
+
+ {MOCK_SESSIONS.map((session) => (
+
+
+
+
+
+ {session.title}
+
+
+ {session.date}
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/src/components/chat/ChatLayout.tsx b/src/components/chat/ChatLayout.tsx
new file mode 100644
index 0000000..7cb9895
--- /dev/null
+++ b/src/components/chat/ChatLayout.tsx
@@ -0,0 +1,61 @@
+import { useEffect } from 'react'
+import { ChatList } from './ChatList'
+import { ChatBottombar } from './ChatBottombar'
+import { useChatStore } from '@/store/useChatStore'
+import { MessageSquarePlus, PanelRightClose, PanelRight } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+
+import { CampaignList } from '@/components/campaign/CampaignList'
+import { ChatHistoryList } from './ChatHistoryList'
+
+export function ChatLayout() {
+ const messages = useChatStore((state) => state.messages)
+ const isRightPanelOpen = useChatStore((state) => state.isRightPanelOpen)
+ const toggleRightPanel = useChatStore((state) => state.toggleRightPanel)
+ const initStomp = useChatStore((state) => state.initStomp)
+
+ useEffect(() => {
+ initStomp()
+ }, [initStomp])
+
+ return (
+
+ {/* Left Panel: Chat History */}
+
+
+ {/* Main Chat Area */}
+
+
+ Loyalty Agent
+
+
+
+
+
+
+
+
+ {/* Right Panel: Context / Campaigns */}
+ {isRightPanelOpen && (
+
+ )}
+
+ )
+}
diff --git a/src/components/chat/ChatList.tsx b/src/components/chat/ChatList.tsx
new file mode 100644
index 0000000..7a8d8ef
--- /dev/null
+++ b/src/components/chat/ChatList.tsx
@@ -0,0 +1,28 @@
+import { useRef, useEffect } from 'react'
+import { ChatBubble } from './ChatBubble'
+import type { Message } from '@/store/useChatStore'
+import { ScrollArea } from '@/components/ui/scroll-area'
+
+export function ChatList({ messages }: { messages: Message[] }) {
+ const bottomRef = useRef(null)
+
+ useEffect(() => {
+ bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
+ }, [messages])
+
+ return (
+
+
+ {messages.map((msg) => (
+
+ ))}
+ {messages.length === 0 && (
+
+ Send a message to start chatting with the Loyalty Agent.
+
+ )}
+
+
+
+ )
+}
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
new file mode 100644
index 0000000..51e507b
--- /dev/null
+++ b/src/components/ui/avatar.tsx
@@ -0,0 +1,50 @@
+"use client"
+
+import * as React from "react"
+import * as AvatarPrimitive from "@radix-ui/react-avatar"
+
+import { cn } from "@/lib/utils"
+
+const Avatar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+Avatar.displayName = AvatarPrimitive.Root.displayName
+
+const AvatarImage = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AvatarImage.displayName = AvatarPrimitive.Image.displayName
+
+const AvatarFallback = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
new file mode 100644
index 0000000..65d4fcd
--- /dev/null
+++ b/src/components/ui/button.tsx
@@ -0,0 +1,57 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..cf253cf
--- /dev/null
+++ b/src/components/ui/scroll-area.tsx
@@ -0,0 +1,46 @@
+import * as React from "react"
+import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
+
+import { cn } from "@/lib/utils"
+
+const ScrollArea = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+ {children}
+
+
+
+
+))
+ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
+
+const ScrollBar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, orientation = "vertical", ...props }, ref) => (
+
+
+
+))
+ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
+
+export { ScrollArea, ScrollBar }
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..e56b0af
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,22 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Textarea = React.forwardRef<
+ HTMLTextAreaElement,
+ React.ComponentProps<"textarea">
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 0000000..60b8851
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,96 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 180 30% 98%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 171 77% 35%; /* Teal/Emerald */
+ --primary-foreground: 210 40% 98%;
+ --secondary: 180 15% 90%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 180 15% 90%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 171 77% 35%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 180 15% 85%;
+ --input: 180 15% 85%;
+ --ring: 171 77% 35%;
+ --radius: 1rem; /* 16px border radius for glassmorphism */
+ }
+
+ .dark {
+ --background: 222 47% 7%;
+ --foreground: 210 40% 98%;
+ --card: 222 47% 9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222 47% 9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 171 77% 45%; /* Vibrant Teal */
+ --primary-foreground: 210 40% 98%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 171 77% 45%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground font-sans;
+ /* Soft mesh gradient background */
+ background-image:
+ radial-gradient(at 0% 0%, hsla(171, 77%, 35%, 0.15) 0px, transparent 50%),
+ radial-gradient(at 100% 0%, hsla(222, 47%, 50%, 0.15) 0px, transparent 50%),
+ radial-gradient(at 100% 100%, hsla(280, 50%, 50%, 0.15) 0px, transparent 50%);
+ background-attachment: fixed;
+ }
+ .dark body {
+ background-image:
+ radial-gradient(at 0% 0%, hsla(171, 77%, 45%, 0.15) 0px, transparent 50%),
+ radial-gradient(at 100% 0%, hsla(222, 47%, 50%, 0.15) 0px, transparent 50%),
+ radial-gradient(at 100% 100%, hsla(280, 50%, 50%, 0.15) 0px, transparent 50%);
+ }
+}
+
+/* Glassmorphism utility classes */
+@layer utilities {
+ .glass-panel {
+ @apply bg-background/60 backdrop-blur-xl border border-white/20 dark:border-white/10 shadow-lg;
+ }
+ .glass-card {
+ @apply bg-card/60 backdrop-blur-md border border-white/20 dark:border-white/10 shadow-sm hover:shadow-md transition-shadow;
+ }
+
+ /* Custom thin scrollbar */
+ .custom-scrollbar::-webkit-scrollbar {
+ width: 4px;
+ height: 4px;
+ }
+ .custom-scrollbar::-webkit-scrollbar-track {
+ background: transparent;
+ }
+ .custom-scrollbar::-webkit-scrollbar-thumb {
+ @apply bg-border rounded-full;
+ }
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ @apply bg-primary/50;
+ }
+}
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 0000000..bef5202
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.tsx'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/api/CampaignApi.java b/src/main/java/dev/sonpx/loyalty/agent/client/api/CampaignApi.java
deleted file mode 100644
index 233791f..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/api/CampaignApi.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package dev.sonpx.loyalty.agent.client.api;
-
-import com.example.loyalty.agent.client.model.Campaign;
-import com.example.loyalty.agent.client.model.CreateCampaignRequest;
-
-import java.util.UUID;
-
-public class CampaignApi {
- public Campaign createCampaign(CreateCampaignRequest request) {
- // Mock behavior
- return new Campaign(
- UUID.randomUUID().toString(),
- request.name(),
- request.targetTierId(),
- request.rewardPoolId(),
- "ACTIVE"
- );
- }
-}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/api/MemberTierApi.java b/src/main/java/dev/sonpx/loyalty/agent/client/api/MemberTierApi.java
deleted file mode 100644
index 3a2f74d..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/api/MemberTierApi.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.sonpx.loyalty.agent.client.api;
-
-import com.example.loyalty.agent.client.model.MemberTier;
-
-import java.util.List;
-
-public class MemberTierApi {
- public List listMemberTiers() {
- // Mock data to ensure it compiles and works before OpenAPI generation
- return List.of(new MemberTier("TIER_GOLD", "Gold", 1000));
- }
-}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/api/PointPoolApi.java b/src/main/java/dev/sonpx/loyalty/agent/client/api/PointPoolApi.java
deleted file mode 100644
index 2d96eee..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/api/PointPoolApi.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package dev.sonpx.loyalty.agent.client.api;
-
-import com.example.loyalty.agent.client.model.PointPool;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-public class PointPoolApi {
- public List listPointPools() {
- // Mock data
- return List.of(new PointPool("POOL_1", "Main Pool", new BigDecimal("1000000")));
- }
-}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/model/Campaign.java b/src/main/java/dev/sonpx/loyalty/agent/client/model/Campaign.java
deleted file mode 100644
index 0b991aa..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/model/Campaign.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.example.loyalty.agent.client.model;
-
-public record Campaign(String campaignId, String name, String targetTierId, String rewardPoolId, String status) {}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/model/CreateCampaignRequest.java b/src/main/java/dev/sonpx/loyalty/agent/client/model/CreateCampaignRequest.java
deleted file mode 100644
index 5fc3b04..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/model/CreateCampaignRequest.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.example.loyalty.agent.client.model;
-
-public record CreateCampaignRequest(String name, String targetTierId, String rewardPoolId) {}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/model/MemberTier.java b/src/main/java/dev/sonpx/loyalty/agent/client/model/MemberTier.java
deleted file mode 100644
index 58640ef..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/model/MemberTier.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.example.loyalty.agent.client.model;
-
-public record MemberTier(String tierId, String tierName, Integer requiredPoints) {}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/client/model/PointPool.java b/src/main/java/dev/sonpx/loyalty/agent/client/model/PointPool.java
deleted file mode 100644
index f351f7a..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/client/model/PointPool.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.example.loyalty.agent.client.model;
-
-import java.math.BigDecimal;
-
-public record PointPool(String poolId, String poolName, BigDecimal balance) {}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/config/ApiClientConfig.java b/src/main/java/dev/sonpx/loyalty/agent/config/ApiClientConfig.java
deleted file mode 100644
index 5851d16..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/config/ApiClientConfig.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package dev.sonpx.loyalty.agent.config;
-
-import dev.sonpx.loyalty.agent.client.api.CampaignApi;
-import dev.sonpx.loyalty.agent.client.api.MemberTierApi;
-import dev.sonpx.loyalty.agent.client.api.PointPoolApi;
-import dev.sonpx.loyalty.agent.client.api.ApiClient;
-import dev.sonpx.loyalty.agent.client.api.attribute.StaticAttributeResourceApi;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
-import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
-import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
-
-/**
- * Configures the generated API clients.
- * This class mocks the configuration that will wrap the actual classes
- * generated by openapi-generator-maven-plugin.
- */
-@Configuration
-public class ApiClientConfig {
-
- @Value("${loyalty.core.base-url}")
- private String coreBaseUrl;
-
- private ApiClient createApiClient(OAuth2AuthorizedClientManager authorizedClientManager, String servicePrefix) {
- ApiClient apiClient = new ApiClient();
- apiClient.updateBaseUri(coreBaseUrl + servicePrefix);
-
- apiClient.setRequestInterceptor(builder -> {
- System.out.println("Executing API Client Interceptor for keycloak...");
- org.springframework.security.core.Authentication principal =
- new org.springframework.security.authentication.AnonymousAuthenticationToken(
- "key", "loyalty-agent-service",
- org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
- OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
- .withClientRegistrationId("keycloak")
- .principal(principal)
- .build();
- try {
- OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
- if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
- System.out.println("Successfully fetched OAuth2 Token! Token length: " + authorizedClient.getAccessToken().getTokenValue().length());
- builder.header("Authorization", "Bearer " + authorizedClient.getAccessToken().getTokenValue());
- } else {
- System.out.println("WARNING: authorizedClient is null or accessToken is null!");
- }
- } catch (Exception e) {
- System.out.println("ERROR during OAuth2 authorize: " + e.getMessage());
- e.printStackTrace();
- }
- });
-
- return apiClient;
- }
-
- @Bean
- public StaticAttributeResourceApi staticAttributeResourceApi(OAuth2AuthorizedClientManager authorizedClientManager) {
- return new StaticAttributeResourceApi(createApiClient(authorizedClientManager, "/svc/attribute"));
- }
-
- @Bean
- public org.springframework.web.client.RestClient loyaltyRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
- return org.springframework.web.client.RestClient.builder()
- .baseUrl(coreBaseUrl + "/svc/attribute")
- .requestInterceptor((request, body, execution) -> {
- org.springframework.security.core.Authentication principal =
- new org.springframework.security.authentication.AnonymousAuthenticationToken(
- "key", "loyalty-agent-service",
- org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
- OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
- .withClientRegistrationId("keycloak")
- .principal(principal)
- .build();
- try {
- OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
- if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
- request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return execution.execute(request, body);
- })
- .build();
- }
-
- @Bean
- public MemberTierApi memberTierApi() {
- // In reality: return new MemberTierApi(apiClient());
- return new MemberTierApi();
- }
-
- @Bean
- public PointPoolApi pointPoolApi() {
- // In reality: return new PointPoolApi(apiClient());
- return new PointPoolApi();
- }
-
- @Bean
- public CampaignApi campaignApi() {
- // In reality: return new CampaignApi(apiClient());
- return new CampaignApi();
- }
-}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/config/LoyaltyAgentTools.java b/src/main/java/dev/sonpx/loyalty/agent/config/LoyaltyAgentTools.java
deleted file mode 100644
index a86a837..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/config/LoyaltyAgentTools.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package dev.sonpx.loyalty.agent.config;
-
-import dev.sonpx.loyalty.agent.client.api.CampaignApi;
-import dev.sonpx.loyalty.agent.client.api.MemberTierApi;
-import dev.sonpx.loyalty.agent.client.api.PointPoolApi;
-import dev.sonpx.loyalty.agent.client.api.attribute.StaticAttributeResourceApi;
-import dev.sonpx.loyalty.agent.client.model.attribute.StaticAttributeViewCriteria;
-import dev.sonpx.loyalty.agent.client.model.attribute.StaticAttributeDto;
-import dev.sonpx.loyalty.agent.client.model.attribute.StringFilter;
-import dev.sonpx.loyalty.agent.client.model.attribute.Pageable;
-import com.example.loyalty.agent.client.model.Campaign;
-import com.example.loyalty.agent.client.model.CreateCampaignRequest;
-import com.example.loyalty.agent.client.model.MemberTier;
-import com.example.loyalty.agent.client.model.PointPool;
-import org.springframework.ai.tool.annotation.Tool;
-
-import java.util.Arrays;
-import java.util.List;
-
-public class LoyaltyAgentTools {
-
- private final MemberTierApi memberTierApi;
- private final PointPoolApi pointPoolApi;
- private final CampaignApi campaignApi;
- private final StaticAttributeResourceApi staticAttributeApi;
- private final org.springframework.web.client.RestClient restClient;
-
- public LoyaltyAgentTools(MemberTierApi memberTierApi, PointPoolApi pointPoolApi, CampaignApi campaignApi, StaticAttributeResourceApi staticAttributeApi, org.springframework.web.client.RestClient restClient) {
- this.memberTierApi = memberTierApi;
- this.pointPoolApi = pointPoolApi;
- this.campaignApi = campaignApi;
- this.staticAttributeApi = staticAttributeApi;
- this.restClient = restClient;
- }
-
- @Tool(description = "List all available Member Tiers. Always use this to verify a Tier exists before creating a Campaign.")
- public List listMemberTiers() {
- return memberTierApi.listMemberTiers();
- }
-
- @Tool(description = "List all available Point Pools. Always use this to verify a Pool exists and has sufficient balance before creating a Campaign.")
- public List listPointPools() {
- return pointPoolApi.listPointPools();
- }
-
- @Tool(description = "Create a new Campaign. This requires a valid targetTierId and rewardPoolId, which MUST be verified beforehand using listMemberTiers and listPointPools.")
- public Campaign createCampaign(CreateCampaignRequest request) {
- return campaignApi.createCampaign(request);
- }
-
- @Tool(description = "List all available Static Attributes. Use this tool when you need to find configurations, properties, or static values like tier types or currencies.")
- public List listStaticAttributes() {
- try {
- String url = "/api/static-attribute/csr/view/list?sort=lastApproveDate,desc&sort=lastUpdateDate,desc&page=0&size=10&status.in=A";
- return restClient.get()
- .uri(url)
- .retrieve()
- .body(new org.springframework.core.ParameterizedTypeReference>() {});
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Failed to fetch static attributes", e);
- }
- }
-}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java b/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java
deleted file mode 100644
index 71b85c8..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package dev.sonpx.loyalty.agent.controller;
-
-import dev.sonpx.loyalty.agent.service.LoyaltyAgentService;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("/api/v1/agent")
-public class AgentController {
-
- private final LoyaltyAgentService agentService;
-
- public AgentController(LoyaltyAgentService agentService) {
- this.agentService = agentService;
- }
-
- public record ChatRequest(String prompt) {}
- public record ChatResponse(String answer) {}
-
- @PostMapping("/chat")
- public ChatResponse chat(@RequestBody ChatRequest request) {
- String answer = agentService.chat(request.prompt());
- return new ChatResponse(answer);
- }
-}
diff --git a/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java b/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java
deleted file mode 100644
index 3539b12..0000000
--- a/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package dev.sonpx.loyalty.agent.service;
-
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.stereotype.Service;
-
-import dev.sonpx.loyalty.agent.client.api.CampaignApi;
-import dev.sonpx.loyalty.agent.client.api.MemberTierApi;
-import dev.sonpx.loyalty.agent.client.api.PointPoolApi;
-import dev.sonpx.loyalty.agent.client.api.attribute.StaticAttributeResourceApi;
-import dev.sonpx.loyalty.agent.config.LoyaltyAgentTools;
-
-@Service
-public class LoyaltyAgentService {
-
- private final ChatClient chatClient;
-
- public LoyaltyAgentService(ChatClient.Builder chatClientBuilder,
- MemberTierApi memberTierApi, PointPoolApi pointPoolApi,
- CampaignApi campaignApi, StaticAttributeResourceApi staticAttributeApi,
- org.springframework.web.client.RestClient loyaltyRestClient) {
-
- LoyaltyAgentTools tools = new LoyaltyAgentTools(memberTierApi, pointPoolApi, campaignApi, staticAttributeApi, loyaltyRestClient);
-
- this.chatClient = chatClientBuilder
- .defaultSystem("""
- You are an expert Loyalty System AI Assistant.
- Your primary job is to orchestrate operations for our Loyalty core system.
-
- CRITICAL RULES:
- 1. DO NOT create a model.client.dev.sonpx.loyalty.agent.Campaign unless you verify that Member Tiers and Point Pools exist first.
- 2. Use the `listMemberTiers` tool to check for valid tier IDs.
- 3. Use the `listPointPools` tool to check for valid pool IDs and ensure they have a sufficient balance.
- 4. Use the `listStaticAttributes` tool when asked about configurations, properties, or static values.
- 5. Only use the `createCampaign` tool once verification is complete.
- 6. IMPORTANT: You must use the native tool calling capability to execute tools. NEVER output raw JSON tool calls as your text response. After receiving the tool's result, provide a natural language summary to the user.
- 7. Always answer the user in the language they used (e.g., Vietnamese).
- """)
- .defaultTools(tools)
- .build();
- }
-
- public String chat(String userMessage) {
- return this.chatClient.prompt()
- .user(userMessage)
- .call()
- .content();
- }
-}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
deleted file mode 100644
index d3a7030..0000000
--- a/src/main/resources/application.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-server:
- port: 8085
-
-spring:
- application:
- name: loyalty-agent-service
- ai:
- ollama:
- base-url: http://192.168.10.112:11434
- chat:
- options:
- model: qwen3.5:9b
- temperature: 0.3
- security:
- oauth2:
- client:
- registration:
- keycloak:
- client-id: ols-cli
- client-secret: 1jV8NSAeybIqmUK0AWhI8hgdo1q5itj7
- authorization-grant-type: client_credentials
- provider:
- keycloak:
- token-uri: http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token
-
-loyalty:
- core:
- base-url: http://192.168.99.242:8081
-
-logging:
- level:
- org.springframework.ai: DEBUG
- org.springframework.web: DEBUG
- dev.sonpx.loyalty: DEBUG
diff --git a/src/store/useChatStore.ts b/src/store/useChatStore.ts
new file mode 100644
index 0000000..90d7ee8
--- /dev/null
+++ b/src/store/useChatStore.ts
@@ -0,0 +1,139 @@
+import { create } from 'zustand'
+import { Client } from '@stomp/stompjs'
+
+export type Role = 'user' | 'agent'
+
+export interface Message {
+ id: string
+ role: Role
+ content: string
+ isStreaming?: boolean
+ toolStatus?: string
+}
+
+interface ChatState {
+ messages: Message[]
+ isRightPanelOpen: boolean
+ isConnected: boolean
+ isConnecting: boolean
+ stompClient: Client | null
+ activeAgentMessageId: string | null
+ initStomp: () => void
+ sendMessage: (content: string, activeCampaignId?: string) => void
+ addMessage: (message: Message) => void
+ updateMessage: (id: string, updater: (msg: Message) => Message) => void
+ toggleRightPanel: () => void
+ conversationId: string
+}
+
+export const useChatStore = create((set, get) => ({
+ messages: [],
+ isRightPanelOpen: false,
+ isConnected: false,
+ isConnecting: false,
+ stompClient: null,
+ activeAgentMessageId: null,
+ conversationId: crypto.randomUUID(),
+ toggleRightPanel: () => set((state) => ({ isRightPanelOpen: !state.isRightPanelOpen })),
+
+ initStomp: () => {
+ if (get().stompClient) return;
+
+ set({ isConnecting: true })
+
+ const client = new Client({
+ brokerURL: 'ws://localhost:9332/ws',
+ reconnectDelay: 5000,
+ heartbeatIncoming: 10000,
+ heartbeatOutgoing: 10000,
+ onConnect: () => {
+ set({ isConnected: true, isConnecting: false })
+
+ client.subscribe('/user/queue/chat-events', (message) => {
+ const event = JSON.parse(message.body);
+ const msgId = event.messageId;
+
+ if (!msgId) return;
+
+ if (event.type === 'TOKEN') {
+ get().updateMessage(msgId, (msg) => ({
+ ...msg,
+ content: msg.content + event.content
+ }));
+ } else if (event.type === 'TOOL_STATUS') {
+ get().updateMessage(msgId, (msg) => ({
+ ...msg,
+ toolStatus: event.status === 'running' ? `Executing ${event.tool}...` : ''
+ }));
+ } else if (event.type === 'DONE') {
+ get().updateMessage(msgId, (msg) => ({
+ ...msg,
+ isStreaming: false
+ }));
+ } else if (event.type === 'ERROR') {
+ get().updateMessage(msgId, (msg) => ({
+ ...msg,
+ content: msg.content + '\n[ERROR] ' + event.content,
+ isStreaming: false
+ }));
+ }
+ });
+ },
+ onWebSocketClose: () => {
+ set({ isConnected: false, isConnecting: false })
+ },
+ onStompError: (frame) => {
+ console.error('Broker error', frame.headers['message'])
+ set({ isConnected: false, isConnecting: false })
+ }
+ })
+
+ client.activate()
+ set({ stompClient: client })
+ },
+
+ sendMessage: (content: string, activeCampaignId?: string) => {
+ const client = get().stompClient
+ if (!client || !client.connected) {
+ console.error('STOMP client is not connected')
+ return
+ }
+
+ const userMessage: Message = {
+ id: crypto.randomUUID(),
+ role: 'user',
+ content,
+ }
+
+ get().addMessage(userMessage)
+
+ const agentMessageId = crypto.randomUUID()
+ set({ activeAgentMessageId: agentMessageId })
+ get().addMessage({
+ id: agentMessageId,
+ role: 'agent',
+ content: '',
+ isStreaming: true
+ })
+
+ client.publish({
+ destination: '/app/chat',
+ body: JSON.stringify({
+ conversationId: get().conversationId,
+ messageId: agentMessageId,
+ prompt: content,
+ activeCampaignId
+ })
+ })
+ },
+
+ addMessage: (message: Message) => {
+ set((state) => ({ messages: [...state.messages, message] }))
+ },
+
+ updateMessage: (id: string, updater: (msg: Message) => Message) => {
+ set((state) => ({
+ messages: state.messages.map((m) => (m.id === id ? updater(m) : m)),
+ }))
+ },
+}))
diff --git a/start-all.sh b/start-all.sh
new file mode 100755
index 0000000..6f93e49
--- /dev/null
+++ b/start-all.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+# Define variables
+PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+LOG_DIR="$PROJECT_ROOT/log"
+
+# Create log directory if it doesn't exist
+mkdir -p "$LOG_DIR"
+
+if [ -f "$PROJECT_ROOT/.env" ]; then
+ echo "Loading environment variables from .env"
+ set -a
+ source "$PROJECT_ROOT/.env"
+ set +a
+fi
+
+echo "=========================================="
+echo "Cleaning up existing processes..."
+echo "=========================================="
+for PORT in 9331 9332 9333; do
+ PID=$(lsof -t -i:$PORT)
+ if [ -n "$PID" ]; then
+ echo "Killing process on port $PORT (PID: $PID)..."
+ kill -9 $PID
+ fi
+done
+
+echo "=========================================="
+echo "Starting Loyalty Agent Services"
+echo "=========================================="
+
+# 1. Start MCP Server
+echo "Starting loyalty-mcp-server..."
+cd "$PROJECT_ROOT/loyalty-mcp-server"
+../mvnw spring-boot:run -Pgen -Dmaven.test.skip=true > "$LOG_DIR/loyalty-mcp-server.log" 2>&1 &
+MCP_PID=$!
+echo "loyalty-mcp-server started with PID: $MCP_PID"
+
+# Wait for MCP Server to start listening on port 9331
+echo "Waiting for loyalty-mcp-server to initialize..."
+while ! nc -z localhost 9331; do
+ sleep 2
+done
+echo "loyalty-mcp-server is up!"
+
+# Start nodemon watcher for MCP Server
+echo "Starting nodemon watcher for loyalty-mcp-server..."
+npx -y nodemon --watch src -e java,xml,yml,yaml,properties --exec "../mvnw compile -Pgen" > "$LOG_DIR/loyalty-mcp-server-nodemon.log" 2>&1 &
+MCP_NODEMON_PID=$!
+
+
+# 2. Start Agent Service
+echo "Starting loyalty-agent..."
+cd "$PROJECT_ROOT/loyalty-agent"
+../mvnw spring-boot:run -Pgen -Dmaven.test.skip=true > "$LOG_DIR/loyalty-agent.log" 2>&1 &
+AGENT_PID=$!
+echo "loyalty-agent started with PID: $AGENT_PID"
+
+# Wait for Agent to start listening on port 9332
+echo "Waiting for loyalty-agent to initialize..."
+while ! nc -z localhost 9332; do
+ sleep 2
+done
+echo "loyalty-agent is up!"
+
+# Start nodemon watcher for Agent Service
+echo "Starting nodemon watcher for loyalty-agent..."
+npx -y nodemon --watch src -e java,xml,yml,yaml,properties --exec "../mvnw compile -Pgen" > "$LOG_DIR/loyalty-agent-nodemon.log" 2>&1 &
+AGENT_NODEMON_PID=$!
+
+
+# 3. Start Frontend UI
+echo "Starting Frontend UI..."
+cd "$PROJECT_ROOT"
+pnpm dev > "$LOG_DIR/frontend.log" 2>&1 &
+UI_PID=$!
+echo "Frontend UI started with PID: $UI_PID"
+
+echo "=========================================="
+echo "All services started successfully!"
+echo "Log files are stored in: $LOG_DIR"
+echo "- $LOG_DIR/loyalty-mcp-server.log"
+echo "- $LOG_DIR/loyalty-mcp-server-nodemon.log"
+echo "- $LOG_DIR/loyalty-agent.log"
+echo "- $LOG_DIR/loyalty-agent-nodemon.log"
+echo "- $LOG_DIR/frontend.log"
+echo "=========================================="
+echo "To stop the services, you can run:"
+echo "kill $MCP_PID $MCP_NODEMON_PID $AGENT_PID $AGENT_NODEMON_PID $UI_PID"
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..bdd9543
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,74 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ darkMode: ["class"],
+ content: [
+ "./index.html",
+ "./src/**/*.{ts,tsx,js,jsx}"
+ ],
+ theme: {
+ container: {
+ center: true,
+ padding: "2rem",
+ screens: {
+ "2xl": "1400px",
+ },
+ },
+ extend: {
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: 0 },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: 0 },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ },
+ },
+ },
+ plugins: [require("@tailwindcss/typography"), require("tailwindcss-animate")],
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 0000000..551f5a9
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,31 @@
+{
+ "compilerOptions": {
+ "ignoreDeprecations": "6.0",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023", "DOM"],
+ "module": "esnext",
+ "types": ["vite/client", "node"],
+ "allowArbitraryExtensions": true,
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..51ae545
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "files": [],
+ "compilerOptions": {
+ "ignoreDeprecations": "6.0",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..8455dcb
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023"],
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "module": "nodenext",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..7889ae5
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,14 @@
+import path from "path"
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ server: { port: 9333 },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
+ },
+})