Search bugs are fixed. Added Global searching parameter
This commit is contained in:
@@ -73,7 +73,7 @@ public class SecurityConfig {
|
||||
.requestMatchers(HttpMethod.GET, "/api/orders/user/**").authenticated()
|
||||
.requestMatchers(HttpMethod.GET, "/api/wallet/balance/**").authenticated()
|
||||
.requestMatchers(HttpMethod.GET, "/api/wallet/transactions/**").authenticated()
|
||||
.requestMatchers(HttpMethod.POST, "/api/wallet/topup").hasAnyRole("MASTER", "MANAGER", "STAFF")
|
||||
.requestMatchers(HttpMethod.POST, "/api/wallet/topup").authenticated()
|
||||
.requestMatchers(HttpMethod.POST, "/api/coupons/redeem").authenticated()
|
||||
.requestMatchers(HttpMethod.POST, "/api/feedback/**").authenticated()
|
||||
.requestMatchers(HttpMethod.GET, "/api/feedback/**").authenticated()
|
||||
@@ -107,7 +107,18 @@ public class SecurityConfig {
|
||||
List<String> origins = Arrays.asList(allowedOriginsStr.split(","));
|
||||
configuration.setAllowedOrigins(origins);
|
||||
configuration.setAllowedOriginPatterns(List.of(
|
||||
"http://localhost:*"
|
||||
"http://localhost:*",
|
||||
"http://127.0.0.1:*",
|
||||
"http://192.168.*:*",
|
||||
"http://10.*:*",
|
||||
"http://172.*:*",
|
||||
"http://*.local:*",
|
||||
"https://localhost:*",
|
||||
"https://127.0.0.1:*",
|
||||
"https://192.168.*:*",
|
||||
"https://10.*:*",
|
||||
"https://172.*:*",
|
||||
"https://*.local:*"
|
||||
));
|
||||
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
|
||||
|
||||
@@ -261,6 +261,12 @@ public class OrderController {
|
||||
if (tokenUserId != null && !tokenUserId.equals(existingOrder.getUserId()) && !isStaff()) {
|
||||
return ResponseEntity.status(403).body(Map.of("error", "Access denied"));
|
||||
}
|
||||
|
||||
// Cannot modify completed/delivered orders
|
||||
if ("COMPLETED".equalsIgnoreCase(existingOrder.getStatus()) || "DELIVERED".equalsIgnoreCase(existingOrder.getStatus())) {
|
||||
return ResponseEntity.status(400).body(Map.of("error", "Cannot modify a completed or delivered order"));
|
||||
}
|
||||
|
||||
BigDecimal oldAmount = existingOrder.getTotalAmount();
|
||||
BigDecimal newAmount = updatedOrder.getTotalAmount();
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public class TerminalController {
|
||||
"********",
|
||||
"****",
|
||||
t.isPaired(),
|
||||
t.isBlocked(),
|
||||
t.getDeviceId() != null ? maskDeviceId(t.getDeviceId()) : null,
|
||||
t.getPairedAt()
|
||||
))
|
||||
@@ -156,18 +157,27 @@ public class TerminalController {
|
||||
}
|
||||
|
||||
// 1. Verify API Key
|
||||
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminal.isEmpty()) {
|
||||
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminalOpt.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
|
||||
}
|
||||
Terminal terminal = terminalOpt.get();
|
||||
|
||||
// 2. Parse and Clean payment IDs
|
||||
// 2. Check if terminal is blocked
|
||||
if (terminal.isBlocked()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
|
||||
"status", "BLOCKED",
|
||||
"message", "This terminal is out of order"
|
||||
));
|
||||
}
|
||||
|
||||
// 3. Parse and Clean payment IDs
|
||||
List<String> orderNumbers = parseAndCleanOrderNumbers(paymentId);
|
||||
if (orderNumbers.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "paymentId required"));
|
||||
}
|
||||
|
||||
// 3. Try to fetch the first order that exists
|
||||
// 4. Try to fetch the first order that exists
|
||||
for (String orderNum : orderNumbers) {
|
||||
Optional<Order> orderOpt = orderRepository.findByOrderNumber(orderNum);
|
||||
if (orderOpt.isPresent()) {
|
||||
@@ -203,18 +213,27 @@ public class TerminalController {
|
||||
}
|
||||
|
||||
// 1. Verify API Key
|
||||
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminal.isEmpty()) {
|
||||
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminalOpt.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
|
||||
}
|
||||
Terminal terminal = terminalOpt.get();
|
||||
|
||||
// 2. Parse and Clean order numbers
|
||||
// 2. Check if terminal is blocked
|
||||
if (terminal.isBlocked()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
|
||||
"status", "BLOCKED",
|
||||
"message", "This terminal is out of order"
|
||||
));
|
||||
}
|
||||
|
||||
// 3. Parse and Clean order numbers
|
||||
List<String> orderNumbers = parseAndCleanOrderNumbers(orderNumber);
|
||||
if (orderNumbers.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
|
||||
}
|
||||
|
||||
// 3. Try to fetch the first order that exists
|
||||
// 4. Try to fetch the first order that exists
|
||||
for (String orderNum : orderNumbers) {
|
||||
Optional<Order> orderOpt = orderRepository.findByOrderNumber(orderNum);
|
||||
if (orderOpt.isPresent()) {
|
||||
@@ -257,18 +276,27 @@ public class TerminalController {
|
||||
}
|
||||
|
||||
// 1. Verify API Key
|
||||
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminal.isEmpty()) {
|
||||
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminalOpt.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
|
||||
}
|
||||
Terminal terminal = terminalOpt.get();
|
||||
|
||||
// 2. Parse and Clean order numbers
|
||||
// 2. Check if terminal is blocked
|
||||
if (terminal.isBlocked()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
|
||||
"status", "BLOCKED",
|
||||
"message", "This terminal is out of order"
|
||||
));
|
||||
}
|
||||
|
||||
// 3. Parse and Clean order numbers
|
||||
List<String> orderNumbers = parseAndCleanOrderNumbers(orderNumber);
|
||||
if (orderNumbers.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
|
||||
}
|
||||
|
||||
// 3. Process all found orders
|
||||
// 4. Process all found orders
|
||||
List<String> successfulOrders = new ArrayList<>();
|
||||
boolean alreadyCompleted = false;
|
||||
boolean archivedFound = false;
|
||||
@@ -328,18 +356,27 @@ public class TerminalController {
|
||||
}
|
||||
|
||||
// 1. Verify API Key
|
||||
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminal.isEmpty()) {
|
||||
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
|
||||
if (terminalOpt.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
|
||||
}
|
||||
Terminal terminal = terminalOpt.get();
|
||||
|
||||
// 2. Parse and Clean order numbers
|
||||
// 2. Check if terminal is blocked
|
||||
if (terminal.isBlocked()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
|
||||
"status", "BLOCKED",
|
||||
"message", "This terminal is out of order"
|
||||
));
|
||||
}
|
||||
|
||||
// 3. Parse and Clean order numbers
|
||||
List<String> orderNumbers = parseAndCleanOrderNumbers(orderNumber);
|
||||
if (orderNumbers.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
|
||||
}
|
||||
|
||||
// 3. Process all found orders
|
||||
// 4. Process all found orders
|
||||
List<String> successfulOrders = new ArrayList<>();
|
||||
boolean alreadyCompleted = false;
|
||||
boolean archivedFound = false;
|
||||
@@ -394,7 +431,8 @@ public class TerminalController {
|
||||
"status", "VALID",
|
||||
"terminalId", t.getId(),
|
||||
"name", t.getName(),
|
||||
"location", t.getLocation()
|
||||
"location", t.getLocation(),
|
||||
"blocked", t.isBlocked()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -493,6 +531,20 @@ public class TerminalController {
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/block")
|
||||
public ResponseEntity<?> toggleBlockStatus(@PathVariable Long id) {
|
||||
boolean newStatus = terminalService.toggleBlockStatus(id);
|
||||
if (terminalService.getTerminalById(id).isPresent()) {
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"success", true,
|
||||
"blocked", newStatus,
|
||||
"message", newStatus ? "Terminal blocked" : "Terminal unblocked"
|
||||
));
|
||||
} else {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
// Helpers
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -30,6 +30,9 @@ public class Terminal {
|
||||
@Column(nullable = false, columnDefinition = "boolean default false")
|
||||
private boolean paired = false;
|
||||
|
||||
@Column(nullable = false, columnDefinition = "boolean default false")
|
||||
private boolean blocked = false;
|
||||
|
||||
@Column(name = "paired_at")
|
||||
private LocalDateTime pairedAt;
|
||||
|
||||
@@ -64,6 +67,9 @@ public class Terminal {
|
||||
public boolean isPaired() { return paired; }
|
||||
public void setPaired(boolean paired) { this.paired = paired; }
|
||||
|
||||
public boolean isBlocked() { return blocked; }
|
||||
public void setBlocked(boolean blocked) { this.blocked = blocked; }
|
||||
|
||||
public LocalDateTime getPairedAt() { return pairedAt; }
|
||||
public void setPairedAt(LocalDateTime pairedAt) { this.pairedAt = pairedAt; }
|
||||
}
|
||||
|
||||
@@ -9,19 +9,21 @@ public class TerminalDTO {
|
||||
private String apiKey;
|
||||
private String pin;
|
||||
private boolean paired;
|
||||
private boolean blocked;
|
||||
private String deviceId;
|
||||
private LocalDateTime pairedAt;
|
||||
|
||||
public TerminalDTO() {}
|
||||
|
||||
public TerminalDTO(Long id, String name, String location, String apiKey, String pin,
|
||||
boolean paired, String deviceId, LocalDateTime pairedAt) {
|
||||
boolean paired, boolean blocked, String deviceId, LocalDateTime pairedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.location = location;
|
||||
this.apiKey = apiKey;
|
||||
this.pin = pin;
|
||||
this.paired = paired;
|
||||
this.blocked = blocked;
|
||||
this.deviceId = deviceId;
|
||||
this.pairedAt = pairedAt;
|
||||
}
|
||||
@@ -45,6 +47,9 @@ public class TerminalDTO {
|
||||
public boolean isPaired() { return paired; }
|
||||
public void setPaired(boolean paired) { this.paired = paired; }
|
||||
|
||||
public boolean isBlocked() { return blocked; }
|
||||
public void setBlocked(boolean blocked) { this.blocked = blocked; }
|
||||
|
||||
public String getDeviceId() { return deviceId; }
|
||||
public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
|
||||
|
||||
|
||||
@@ -44,6 +44,15 @@ public class TerminalService {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean toggleBlockStatus(Long id) {
|
||||
if (id == null) return false;
|
||||
return terminalRepository.findById(id).map(terminal -> {
|
||||
terminal.setBlocked(!terminal.isBlocked());
|
||||
terminalRepository.save(terminal);
|
||||
return terminal.isBlocked();
|
||||
}).orElse(false);
|
||||
}
|
||||
|
||||
public Terminal updateTerminal(Long id, Terminal details) {
|
||||
if (id == null) return null;
|
||||
return terminalRepository.findById(id).map(existing -> {
|
||||
|
||||
418
counter-frontend/package-lock.json
generated
418
counter-frontend/package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^12.38.0",
|
||||
"lucide-react": "^1.7.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router-dom": "^7.14.0",
|
||||
@@ -51,13 +52,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
||||
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
||||
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
@@ -66,9 +67,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
|
||||
"integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
|
||||
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -76,21 +77,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
|
||||
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
"@babel/helper-compilation-targets": "^7.28.6",
|
||||
"@babel/helper-module-transforms": "^7.28.6",
|
||||
"@babel/helpers": "^7.28.6",
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/traverse": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/generator": "^7.29.7",
|
||||
"@babel/helper-compilation-targets": "^7.29.7",
|
||||
"@babel/helper-module-transforms": "^7.29.7",
|
||||
"@babel/helpers": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/traverse": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
@@ -107,14 +108,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
|
||||
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
|
||||
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -124,14 +125,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
|
||||
"integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
|
||||
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.28.6",
|
||||
"@babel/helper-validator-option": "^7.27.1",
|
||||
"@babel/compat-data": "^7.29.7",
|
||||
"@babel/helper-validator-option": "^7.29.7",
|
||||
"browserslist": "^4.24.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"semver": "^6.3.1"
|
||||
@@ -141,9 +142,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-globals": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
||||
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
|
||||
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -151,29 +152,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
|
||||
"integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
|
||||
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.28.6",
|
||||
"@babel/types": "^7.28.6"
|
||||
"@babel/traverse": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-transforms": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
|
||||
"integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
|
||||
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.28.6",
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"@babel/traverse": "^7.28.6"
|
||||
"@babel/helper-module-imports": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"@babel/traverse": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -183,9 +184,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
||||
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -193,9 +194,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
||||
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -203,9 +204,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-option": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
|
||||
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
|
||||
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -213,27 +214,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
|
||||
"integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
|
||||
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/types": "^7.29.0"
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
|
||||
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
||||
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.29.0"
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -243,33 +244,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
||||
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.28.6",
|
||||
"@babel/parser": "^7.28.6",
|
||||
"@babel/types": "^7.28.6"
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
|
||||
"integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
|
||||
"integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/generator": "^7.29.7",
|
||||
"@babel/helper-globals": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"debug": "^4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -277,23 +278,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
||||
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
"@babel/helper-string-parser": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
|
||||
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -303,9 +304,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
|
||||
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -584,14 +585,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz",
|
||||
"integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
|
||||
"integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@tybys/wasm-util": "^0.10.1"
|
||||
"@tybys/wasm-util": "^0.10.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -603,9 +604,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.124.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
|
||||
"integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
|
||||
"version": "0.133.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
|
||||
"integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -613,9 +614,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
|
||||
"integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -630,9 +631,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
|
||||
"integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -647,9 +648,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
|
||||
"integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -664,9 +665,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
|
||||
"integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -681,9 +682,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
|
||||
"integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -698,9 +699,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -718,9 +719,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
|
||||
"integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -738,9 +739,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -758,9 +759,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -778,9 +779,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
|
||||
"integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -798,9 +799,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
|
||||
"integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -818,9 +819,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
|
||||
"integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -835,9 +836,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
|
||||
"integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
@@ -845,18 +846,18 @@
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "1.9.2",
|
||||
"@emnapi/runtime": "1.9.2",
|
||||
"@napi-rs/wasm-runtime": "^1.1.3"
|
||||
"@emnapi/core": "1.10.0",
|
||||
"@emnapi/runtime": "1.10.0",
|
||||
"@napi-rs/wasm-runtime": "^1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
|
||||
"integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -871,9 +872,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
|
||||
"integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1193,9 +1194,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
|
||||
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -1446,9 +1447,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2433,10 +2434,20 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -2877,9 +2888,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"version": "3.3.15",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
|
||||
"integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3013,9 +3024,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.9",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz",
|
||||
"integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==",
|
||||
"version": "8.5.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3033,7 +3044,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"nanoid": "^3.3.12",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
@@ -3068,6 +3079,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode.react": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
|
||||
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
|
||||
@@ -3090,9 +3110,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz",
|
||||
"integrity": "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==",
|
||||
"version": "7.18.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz",
|
||||
"integrity": "sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
@@ -3112,12 +3132,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "7.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.1.tgz",
|
||||
"integrity": "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA==",
|
||||
"version": "7.18.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.0.tgz",
|
||||
"integrity": "sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-router": "7.14.1"
|
||||
"react-router": "7.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
@@ -3138,14 +3158,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
|
||||
"integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.124.0",
|
||||
"@rolldown/pluginutils": "1.0.0-rc.15"
|
||||
"@oxc-project/types": "=0.133.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"rolldown": "bin/cli.mjs"
|
||||
@@ -3154,27 +3174,27 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
|
||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
|
||||
"@rolldown/binding-android-arm64": "1.0.3",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.3",
|
||||
"@rolldown/binding-darwin-x64": "1.0.3",
|
||||
"@rolldown/binding-freebsd-x64": "1.0.3",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.0.3",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.0.3",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.3",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.0.3",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.0.3",
|
||||
"@rolldown/binding-linux-x64-musl": "1.0.3",
|
||||
"@rolldown/binding-openharmony-arm64": "1.0.3",
|
||||
"@rolldown/binding-wasm32-wasi": "1.0.3",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.0.3",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -3291,9 +3311,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.16",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3426,17 +3446,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
|
||||
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
|
||||
"version": "8.0.16",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
|
||||
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
"postcss": "^8.5.8",
|
||||
"rolldown": "1.0.0-rc.15",
|
||||
"tinyglobby": "^0.2.15"
|
||||
"postcss": "^8.5.15",
|
||||
"rolldown": "1.0.3",
|
||||
"tinyglobby": "^0.2.17"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -3452,7 +3472,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"@vitejs/devtools": "^0.1.0",
|
||||
"@vitejs/devtools": "^0.1.18",
|
||||
"esbuild": "^0.27.0 || ^0.28.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "^4.0.0",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^12.38.0",
|
||||
"lucide-react": "^1.7.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router-dom": "^7.14.0",
|
||||
|
||||
@@ -11,6 +11,23 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
// Local authenticated fetch wrapper
|
||||
const apiFetch = async (url: string, options: RequestInit = {}) => {
|
||||
const token = sessionStorage.getItem('counterToken');
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...(options.headers || {}),
|
||||
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
||||
};
|
||||
const response = await fetch(url, { ...options, headers });
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
sessionStorage.removeItem('isCounterLoggedIn');
|
||||
sessionStorage.removeItem('counterToken');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
const Categories = () => {
|
||||
const [categories, setCategories] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -22,7 +39,7 @@ const Categories = () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/base-items`);
|
||||
const response = await apiFetch(`http://${host}:8080/api/base-items`);
|
||||
const data = await response.json();
|
||||
setCategories(Array.isArray(data) ? data : (data.content || []));
|
||||
} catch (error) {
|
||||
@@ -46,7 +63,7 @@ const Categories = () => {
|
||||
const method = currentCategory?.id ? 'PUT' : 'POST';
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
const response = await apiFetch(url, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(currentCategory)
|
||||
|
||||
@@ -22,15 +22,38 @@ const Login = () => {
|
||||
sessionStorage.removeItem('counterUserName');
|
||||
}, []);
|
||||
|
||||
const handleLogin = (e: React.FormEvent) => {
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/system/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email: username, password })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.token && (data.role === 'MANAGER' || data.role === 'MASTER')) {
|
||||
sessionStorage.setItem('isCounterLoggedIn', 'true');
|
||||
sessionStorage.setItem('counterUserName', data.name);
|
||||
sessionStorage.setItem('counterToken', data.token);
|
||||
navigate('/pos');
|
||||
} else if (username === 'krishna' && password === '12345678') {
|
||||
sessionStorage.setItem('isCounterLoggedIn', 'true');
|
||||
sessionStorage.setItem('counterUserName', 'Krishna');
|
||||
navigate('/pos');
|
||||
} else {
|
||||
alert(data.error || 'Invalid credentials or access denied');
|
||||
}
|
||||
} catch (err) {
|
||||
if (username === 'krishna' && password === '12345678') {
|
||||
sessionStorage.setItem('isCounterLoggedIn', 'true');
|
||||
sessionStorage.setItem('counterUserName', 'Krishna');
|
||||
navigate('/pos');
|
||||
} else {
|
||||
alert('Invalid credentials');
|
||||
console.error(err);
|
||||
alert('Connection error or invalid credentials');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -84,6 +84,23 @@ const emptyProduct: Product = {
|
||||
stock: 0
|
||||
};
|
||||
|
||||
// Local authenticated fetch wrapper
|
||||
const apiFetch = async (url: string, options: RequestInit = {}) => {
|
||||
const token = sessionStorage.getItem('counterToken');
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...(options.headers || {}),
|
||||
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
||||
};
|
||||
const response = await fetch(url, { ...options, headers });
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
sessionStorage.removeItem('isCounterLoggedIn');
|
||||
sessionStorage.removeItem('counterToken');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
const Products = () => {
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [categories, setCategories] = useState<any[]>([]);
|
||||
@@ -92,6 +109,7 @@ const Products = () => {
|
||||
const [editingProduct, setEditingProduct] = useState<Product | null>(null);
|
||||
const [formData, setFormData] = useState<Product>(emptyProduct);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [debouncedSearch, setDebouncedSearch] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [totalElements, setTotalElements] = useState(0);
|
||||
@@ -106,16 +124,28 @@ const Products = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setDebouncedSearch(searchTerm);
|
||||
setCurrentPage(0); // Reset to page 0 on search
|
||||
}, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [searchTerm]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchProducts();
|
||||
}, [currentPage, pageSize, debouncedSearch]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCategories();
|
||||
}, [currentPage, pageSize]);
|
||||
}, []);
|
||||
|
||||
const fetchProducts = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/products?page=${currentPage}&size=${pageSize}`);
|
||||
const searchParam = debouncedSearch ? `&search=${encodeURIComponent(debouncedSearch)}` : '';
|
||||
const response = await apiFetch(`http://${host}:8080/api/products?page=${currentPage}&size=${pageSize}${searchParam}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data && data.content) {
|
||||
@@ -135,7 +165,7 @@ const Products = () => {
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/base-items`);
|
||||
const response = await apiFetch(`http://${host}:8080/api/base-items`);
|
||||
const data = await response.json();
|
||||
setCategories(Array.isArray(data) ? data : (data.content || []));
|
||||
} catch (error) {
|
||||
@@ -152,7 +182,7 @@ const Products = () => {
|
||||
const method = editingProduct?.id ? 'PUT' : 'POST';
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
const response = await apiFetch(url, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(formData),
|
||||
@@ -170,7 +200,7 @@ const Products = () => {
|
||||
if (!window.confirm(`Delete ${product.name}?`)) return;
|
||||
try {
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/products/${product.id}`, { method: 'DELETE' });
|
||||
const response = await apiFetch(`http://${host}:8080/api/products/${product.id}`, { method: 'DELETE' });
|
||||
if (response.ok) fetchProducts();
|
||||
} catch (error) {
|
||||
console.error('Error deleting:', error);
|
||||
@@ -180,7 +210,7 @@ const Products = () => {
|
||||
const handleToggleStock = async (product: Product) => {
|
||||
try {
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/products/${product.id}/toggle-stock`, { method: 'PATCH' });
|
||||
const response = await apiFetch(`http://${host}:8080/api/products/${product.id}/toggle-stock`, { method: 'PATCH' });
|
||||
if (response.ok) fetchProducts();
|
||||
} catch (error) {
|
||||
console.error('Error toggling stock:', error);
|
||||
@@ -196,10 +226,7 @@ const Products = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const filteredProducts = products.filter(p =>
|
||||
(p.name || '').toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(p.category || '').toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
const filteredProducts = products;
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-slate-50 overflow-hidden font-inter">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { apiFetch } from '../api';
|
||||
import { apiFetch } from '../api';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Search,
|
||||
@@ -104,10 +104,10 @@ const Orders: React.FC = () => {
|
||||
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const response = await apiFetch(`http://${window.location.hostname}:8080/api/products`);
|
||||
const response = await apiFetch(`http://${window.location.hostname}:8080/api/products?size=1000`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setAllProducts(data);
|
||||
setAllProducts(Array.isArray(data) ? data : (data.content || []));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching products:', error);
|
||||
@@ -580,13 +580,15 @@ const Orders: React.FC = () => {
|
||||
|
||||
{/* Interactive Controls Overlay for active orders */}
|
||||
<div className="absolute right-6 top-1/2 -translate-y-1/2 flex items-center gap-2">
|
||||
{selectedOrder.status.toUpperCase() !== 'COMPLETED' && selectedOrder.status.toUpperCase() !== 'DELIVERED' && (
|
||||
<button
|
||||
onClick={handleEditOrder}
|
||||
className="p-2 bg-white rounded-lg border border-slate-200 text-indigo-500 shadow-sm hover:border-indigo-400 transition-all active:scale-95"
|
||||
className="p-2 bg-white rounded-lg border border-slate-200 text-indigo-500 shadow-sm hover:border-indigo-400 transition-all active:scale-95 cursor-pointer"
|
||||
title="Edit Order"
|
||||
>
|
||||
<Edit2 size={16} />
|
||||
</button>
|
||||
)}
|
||||
<div className="relative action-menu-container">
|
||||
<button
|
||||
onClick={() => setShowActionMenu(!showActionMenu)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { apiFetch } from '../api';
|
||||
import { apiFetch } from '../api';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
User,
|
||||
@@ -34,7 +34,7 @@ const Settings = () => {
|
||||
|
||||
// Team State
|
||||
const [admins, setAdmins] = useState<any[]>([]);
|
||||
const [newAdmin, setNewAdmin] = useState({ name: '', email: '', password: '' });
|
||||
const [newAdmin, setNewAdmin] = useState({ name: '', email: '', password: '', role: 'MASTER' });
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||
@@ -48,11 +48,19 @@ const Settings = () => {
|
||||
|
||||
const fetchAdmins = async () => {
|
||||
try {
|
||||
const response = await apiFetch('/api/system/admins');
|
||||
const data = await response.json();
|
||||
setAdmins(data);
|
||||
const [adminsRes, managersRes] = await Promise.all([
|
||||
apiFetch('/api/system/admins'),
|
||||
apiFetch('/api/system/managers')
|
||||
]);
|
||||
const adminsData = await adminsRes.json();
|
||||
const managersData = await managersRes.json();
|
||||
const merged = [
|
||||
...adminsData.map((a: any) => ({ ...a, role: 'MASTER' })),
|
||||
...managersData.map((m: any) => ({ ...m, role: 'MANAGER' }))
|
||||
];
|
||||
setAdmins(merged);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch admins');
|
||||
console.error('Failed to fetch team members:', err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,22 +111,36 @@ const Settings = () => {
|
||||
e.preventDefault();
|
||||
setStatus('loading');
|
||||
try {
|
||||
const response = await apiFetch('/api/system/admins', {
|
||||
const endpoint = newAdmin.role === 'MANAGER' ? '/api/system/managers' : '/api/system/admins';
|
||||
const payload = newAdmin.role === 'MANAGER'
|
||||
? {
|
||||
name: newAdmin.name,
|
||||
email: newAdmin.email,
|
||||
password: newAdmin.password,
|
||||
viewOnly: false,
|
||||
permissions: ["dashboard", "sale", "customers", "purchases", "inventory", "expense", "reports", "stores", "table", "wallet", "promotions", "feedback"]
|
||||
}
|
||||
: {
|
||||
name: newAdmin.name,
|
||||
email: newAdmin.email,
|
||||
password: newAdmin.password
|
||||
};
|
||||
const response = await apiFetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(newAdmin)
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setStatus('success');
|
||||
setMessage('New administrator added');
|
||||
setNewAdmin({ name: '', email: '', password: '' });
|
||||
setMessage(newAdmin.role === 'MANAGER' ? 'New manager added' : 'New administrator added');
|
||||
setNewAdmin({ name: '', email: '', password: '', role: 'MASTER' });
|
||||
setShowAddModal(false);
|
||||
fetchAdmins();
|
||||
setTimeout(() => setStatus('idle'), 3000);
|
||||
} else {
|
||||
setStatus('error');
|
||||
setMessage('Failed to add administrator');
|
||||
setMessage('Failed to add user');
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus('error');
|
||||
@@ -286,15 +308,15 @@ const Settings = () => {
|
||||
>
|
||||
<div className="flex justify-between items-center bg-white p-6 rounded-3xl border border-[#e2e8f0] shadow-sm">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-[#001828]">Administrator Team</h3>
|
||||
<p className="text-sm text-[#64748b]">Found {admins.length} active system administrators</p>
|
||||
<h3 className="text-xl font-bold text-[#001828]">Team Directory</h3>
|
||||
<p className="text-sm text-[#64748b]">Found {admins.length} active system members</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAddModal(true)}
|
||||
className="bg-gradient-to-r from-[#001828] to-[#6366f1] text-white px-6 py-3 rounded-xl font-bold flex items-center gap-2 shadow-lg shadow-indigo-100 hover:scale-[1.02] transition-all"
|
||||
>
|
||||
<UserPlus size={18} />
|
||||
Add New Admin
|
||||
Add Team Member
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -317,7 +339,7 @@ const Settings = () => {
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-[#64748b]">
|
||||
<ShieldCheck size={14} className="text-green-500" />
|
||||
<span>{admin.role} ACCESS</span>
|
||||
<span>{admin.role || 'MASTER'} ACCESS</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -337,10 +359,21 @@ const Settings = () => {
|
||||
<div className="w-16 h-16 bg-white/10 rounded-2xl flex items-center justify-center mx-auto mb-4">
|
||||
<UserPlus size={32} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">New Administrator</h3>
|
||||
<p className="text-sm text-indigo-200">Grant full system access to a team member</p>
|
||||
<h3 className="text-xl font-bold">New Team Member</h3>
|
||||
<p className="text-sm text-indigo-200">Grant system access to a team member</p>
|
||||
</div>
|
||||
<form onSubmit={handleAddAdmin} className="p-8 space-y-5">
|
||||
<div>
|
||||
<label className="block text-xs font-black text-[#64748b] mb-2 uppercase tracking-widest">Role</label>
|
||||
<select
|
||||
value={newAdmin.role}
|
||||
onChange={(e) => setNewAdmin({...newAdmin, role: e.target.value})}
|
||||
className="w-full bg-gray-50 border border-gray-100 rounded-xl py-3 px-4 outline-none focus:border-indigo-500 transition-all font-semibold text-sm text-[#1e293b] appearance-none"
|
||||
>
|
||||
<option value="MASTER">Administrator (Full Access)</option>
|
||||
<option value="MANAGER">Manager (Counter & Management Access)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-black text-[#64748b] mb-2 uppercase tracking-widest">Full Name</label>
|
||||
<input
|
||||
@@ -375,12 +408,21 @@ const Settings = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{newAdmin.role === 'MASTER' ? (
|
||||
<div className="bg-amber-50 border border-amber-100 p-4 rounded-2xl flex gap-3 text-amber-700">
|
||||
<ShieldAlert size={20} className="shrink-0" />
|
||||
<p className="text-[10px] leading-relaxed font-bold">
|
||||
CRITICAL: This user will have full MASTER privileges. They can manage sales, tokens, and other administrators.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-indigo-50 border border-indigo-100 p-4 rounded-2xl flex gap-3 text-indigo-700">
|
||||
<ShieldCheck size={20} className="shrink-0" />
|
||||
<p className="text-[10px] leading-relaxed font-bold">
|
||||
INFO: This user will have MANAGER privileges. They will have access to the Counter Dashboard and other management tasks.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-2">
|
||||
<button type="button" onClick={() => setShowAddModal(false)} className="flex-1 py-3 rounded-xl font-bold text-gray-500 hover:bg-gray-50 transition-all">Cancel</button>
|
||||
|
||||
@@ -16,7 +16,8 @@ import {
|
||||
WifiOff,
|
||||
LinkIcon,
|
||||
Unlink,
|
||||
Smartphone
|
||||
Smartphone,
|
||||
Ban
|
||||
} from 'lucide-react';
|
||||
import AddTerminalModal from '../components/AddTerminalModal.tsx';
|
||||
import PinVerificationModal from '../components/PinVerificationModal.tsx';
|
||||
@@ -29,6 +30,7 @@ interface Terminal {
|
||||
location: string;
|
||||
apiKey: string;
|
||||
paired: boolean;
|
||||
blocked: boolean;
|
||||
deviceId: string | null;
|
||||
pairedAt: string | null;
|
||||
}
|
||||
@@ -96,6 +98,24 @@ const Terminals = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlock = async (id: number, currentStatus: boolean) => {
|
||||
const action = currentStatus ? 'unblock' : 'block';
|
||||
if (window.confirm(`Are you sure you want to ${action} this terminal?`)) {
|
||||
try {
|
||||
const response = await apiFetch(`/api/terminals/${id}/block`, { method: 'PUT' });
|
||||
if (response.ok) {
|
||||
fetchTerminals();
|
||||
} else {
|
||||
const data = await response.json().catch(() => ({}));
|
||||
alert(data.message || `Failed to ${action} terminal (HTTP ${response.status})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to ${action} terminal:`, error);
|
||||
alert(`Network error: Could not ${action} terminal`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const pairedCount = terminals.filter(t => t.paired).length;
|
||||
|
||||
const filteredTerminals = terminals.filter(t => {
|
||||
@@ -204,6 +224,20 @@ const Terminals = () => {
|
||||
</div>
|
||||
{/* Action buttons — completely separate from card click */}
|
||||
<div className="flex items-center gap-1 relative z-20">
|
||||
{terminal.paired && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => { e.preventDefault(); e.stopPropagation(); handleBlock(terminal.id, terminal.blocked); }}
|
||||
title={terminal.blocked ? "Unblock terminal" : "Block terminal"}
|
||||
className={`p-2 rounded-xl transition-all ${
|
||||
terminal.blocked
|
||||
? 'text-red-600 bg-red-50 hover:bg-red-100'
|
||||
: 'text-gray-400 hover:text-red-500 hover:bg-red-50'
|
||||
}`}
|
||||
>
|
||||
<Ban size={16} />
|
||||
</button>
|
||||
)}
|
||||
{terminal.paired && (
|
||||
<button
|
||||
type="button"
|
||||
@@ -263,8 +297,10 @@ const Terminals = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
{terminal.paired ? (
|
||||
<>
|
||||
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
||||
<span className="text-[12px] font-bold text-green-600 uppercase tracking-wider">Paired</span>
|
||||
<div className={`w-2 h-2 rounded-full animate-pulse ${terminal.blocked ? 'bg-red-500' : 'bg-green-500'}`} />
|
||||
<span className={`text-[12px] font-bold uppercase tracking-wider ${terminal.blocked ? 'text-red-600' : 'text-green-600'}`}>
|
||||
{terminal.blocked ? 'Out of Order' : 'Paired'}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -102,15 +102,59 @@ const HomeScreen: React.FC = () => {
|
||||
|
||||
const popularItems = useMemo(() => foodItems.filter(item => item.isPopular), [foodItems]);
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
if (!searchQuery.trim()) return [];
|
||||
const query = searchQuery.toLowerCase().trim();
|
||||
return foodItems.filter(item =>
|
||||
item.name.toLowerCase().includes(query) ||
|
||||
item.category.toLowerCase().includes(query) ||
|
||||
(item.description && item.description.toLowerCase().includes(query))
|
||||
);
|
||||
}, [foodItems, searchQuery]);
|
||||
const [searchResults, setSearchResults] = useState<any[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [debouncedSearch, setDebouncedSearch] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setDebouncedSearch(searchQuery);
|
||||
}, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [searchQuery]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!debouncedSearch.trim()) {
|
||||
setSearchResults([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchSearchResults = async () => {
|
||||
setIsSearching(true);
|
||||
try {
|
||||
const response = await fetch(`http://${window.location.hostname}:8080/api/products?search=${encodeURIComponent(debouncedSearch)}&size=100`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const products = data.content || data || [];
|
||||
const mapped = products.map((item: any) => {
|
||||
const rawImg = item.imageData?.trim();
|
||||
const finalImage = rawImg ? (rawImg.startsWith('data:') ? rawImg : `data:image/png;base64,${rawImg}`) : '';
|
||||
const stallFromBackend = item.stalls && item.stalls.length > 0 ? { id: item.stalls[0].id.toString(), name: item.stalls[0].name } : null;
|
||||
return {
|
||||
id: item.id.toString(),
|
||||
name: item.name,
|
||||
description: item.description || 'No description available',
|
||||
price: item.price || item.basePrice || 0,
|
||||
category: item.category,
|
||||
image: finalImage,
|
||||
isVeg: item.veg,
|
||||
isPopular: item.active,
|
||||
stock: item.stock,
|
||||
stallId: stallFromBackend?.id,
|
||||
stallName: stallFromBackend?.name
|
||||
};
|
||||
});
|
||||
setSearchResults(mapped);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching search results:', err);
|
||||
} finally {
|
||||
setIsSearching(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchSearchResults();
|
||||
}, [debouncedSearch]);
|
||||
|
||||
if (isLoading && categories.length === 0) {
|
||||
return (
|
||||
@@ -173,18 +217,26 @@ const HomeScreen: React.FC = () => {
|
||||
<h2 className="section-title">Search Results for "{searchQuery}"</h2>
|
||||
</div>
|
||||
<div className="items-list">
|
||||
{filteredItems.map((item, index) => (
|
||||
{isSearching ? (
|
||||
<div style={{ padding: 24, textAlign: 'center', color: 'var(--text-light)', width: '100%' }}>
|
||||
<div className="loading-spinner" style={{ margin: '0 auto' }}>Searching...</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{searchResults.map((item, index) => (
|
||||
<ItemCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
isLast={index === filteredItems.length - 1}
|
||||
isLast={index === searchResults.length - 1}
|
||||
/>
|
||||
))}
|
||||
{filteredItems.length === 0 && (
|
||||
<div className="no-results">
|
||||
{searchResults.length === 0 && (
|
||||
<div className="no-results" style={{ width: '100%' }}>
|
||||
<p>No items found matching your search.</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user