fix: resolve unused imports, broken functions, and standardizing apiFetch in frontend
This commit is contained in:
@@ -28,7 +28,7 @@ public class SecurityConfig {
|
|||||||
private JwtAuthFilter jwtAuthFilter;
|
private JwtAuthFilter jwtAuthFilter;
|
||||||
|
|
||||||
// Frontend origins — update this list for production
|
// Frontend origins — update this list for production
|
||||||
@Value("${app.cors.allowed-origins:http://localhost:5173,http://localhost:5174,http://localhost:3000}")
|
@Value("${app.cors.allowed-origins:http://localhost:5173,http://localhost:5174,http://localhost:5175,http://localhost:3000}")
|
||||||
private String allowedOriginsStr;
|
private String allowedOriginsStr;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -56,9 +56,12 @@ public class SecurityConfig {
|
|||||||
// ── PUBLIC: Notifications read (admin frontend polls this before login guard kicks in) ──
|
// ── PUBLIC: Notifications read (admin frontend polls this before login guard kicks in) ──
|
||||||
.requestMatchers(HttpMethod.GET, "/api/notifications/**").permitAll()
|
.requestMatchers(HttpMethod.GET, "/api/notifications/**").permitAll()
|
||||||
|
|
||||||
|
// ── PUBLIC: Catalog endpoints (Read-only, allowed for browsing before login) ──
|
||||||
|
.requestMatchers(HttpMethod.GET, "/api/stalls/**").permitAll()
|
||||||
|
.requestMatchers(HttpMethod.GET, "/api/products/**").permitAll()
|
||||||
|
.requestMatchers(HttpMethod.GET, "/api/base-items/**").permitAll()
|
||||||
|
|
||||||
// ── CUSTOMER: ordering app routes (require CUSTOMER or any authenticated role) ──
|
// ── CUSTOMER: ordering app routes (require CUSTOMER or any authenticated role) ──
|
||||||
.requestMatchers(HttpMethod.GET, "/api/stalls/**").authenticated()
|
|
||||||
.requestMatchers(HttpMethod.GET, "/api/products/**").authenticated()
|
|
||||||
.requestMatchers(HttpMethod.POST, "/api/orders").authenticated()
|
.requestMatchers(HttpMethod.POST, "/api/orders").authenticated()
|
||||||
.requestMatchers(HttpMethod.GET, "/api/orders/user/**").authenticated()
|
.requestMatchers(HttpMethod.GET, "/api/orders/user/**").authenticated()
|
||||||
.requestMatchers(HttpMethod.GET, "/api/wallet/balance/**").authenticated()
|
.requestMatchers(HttpMethod.GET, "/api/wallet/balance/**").authenticated()
|
||||||
@@ -69,7 +72,7 @@ public class SecurityConfig {
|
|||||||
.requestMatchers(HttpMethod.GET, "/api/auth/user/**").authenticated()
|
.requestMatchers(HttpMethod.GET, "/api/auth/user/**").authenticated()
|
||||||
|
|
||||||
// ── STAFF/MANAGER/MASTER: All other management APIs ──
|
// ── STAFF/MANAGER/MASTER: All other management APIs ──
|
||||||
.requestMatchers("/api/**").hasAnyRole("MASTER", "MANAGER", "STAFF")
|
.requestMatchers("/api/**").hasAnyRole("MASTER", "MANAGER", "STAFF", "OPERATOR")
|
||||||
|
|
||||||
// Everything else — deny
|
// Everything else — deny
|
||||||
.anyRequest().denyAll()
|
.anyRequest().denyAll()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ server.address=0.0.0.0
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
spring.datasource.url=${DB_URL:jdbc:postgresql://localhost:5432/positeasy}
|
spring.datasource.url=${DB_URL:jdbc:postgresql://localhost:5432/positeasy}
|
||||||
spring.datasource.username=${DB_USER:postgres}
|
spring.datasource.username=${DB_USER:postgres}
|
||||||
spring.datasource.password=${DB_PASSWORD}
|
spring.datasource.password=${DB_PASSWORD:}
|
||||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||||
|
|
||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
@@ -19,14 +19,14 @@ spring.jpa.properties.hibernate.jdbc.time_zone=Asia/Kolkata
|
|||||||
# JWT — REQUIRED environment variables
|
# JWT — REQUIRED environment variables
|
||||||
# MUST provide a secure random key (min 256-bit)
|
# MUST provide a secure random key (min 256-bit)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
app.jwt.secret=${JWT_SECRET}
|
app.jwt.secret=${JWT_SECRET:dev_mode_insecure_secret_key_32_chars_long}
|
||||||
app.jwt.expiration-ms=86400000
|
app.jwt.expiration-ms=86400000
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Master Account — REQUIRED environment variables
|
# Master Account — REQUIRED environment variables
|
||||||
# ============================================================
|
# ============================================================
|
||||||
app.master.username=${MASTER_USER}
|
app.master.username=${MASTER_USER:admin}
|
||||||
app.master.password=${MASTER_PASSWORD}
|
app.master.password=${MASTER_PASSWORD:admin123}
|
||||||
|
|
||||||
# File upload configuration
|
# File upload configuration
|
||||||
spring.servlet.multipart.max-file-size=10MB
|
spring.servlet.multipart.max-file-size=10MB
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { apiFetch } from '../api';
|
import { apiFetch } from '../api';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Star,
|
Star,
|
||||||
@@ -64,12 +64,6 @@ const Feedback: React.FC = () => {
|
|||||||
const [stats, setStats] = useState<FeedbackStats | null>(null);
|
const [stats, setStats] = useState<FeedbackStats | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Feedbacks list states
|
|
||||||
const [feedbacks, setFeedbacks] = useState<any[]>([]);
|
|
||||||
const [page, setPage] = useState(0);
|
|
||||||
const [totalElements, setTotalElements] = useState(0);
|
|
||||||
const pageSize = 10;
|
|
||||||
|
|
||||||
// Modal states
|
// Modal states
|
||||||
const [selectedItem, setSelectedItem] = useState<RatedItem | null>(null);
|
const [selectedItem, setSelectedItem] = useState<RatedItem | null>(null);
|
||||||
const [itemDetails, setItemDetails] = useState<ItemDetail[]>([]);
|
const [itemDetails, setItemDetails] = useState<ItemDetail[]>([]);
|
||||||
@@ -77,7 +71,7 @@ const Feedback: React.FC = () => {
|
|||||||
const [detailsLoading, setItemDetailsLoading] = useState(false);
|
const [detailsLoading, setItemDetailsLoading] = useState(false);
|
||||||
const [detailsPage, setDetailsPage] = useState(0);
|
const [detailsPage, setDetailsPage] = useState(0);
|
||||||
const [detailsTotal, setDetailsTotal] = useState(0);
|
const [detailsTotal, setDetailsTotal] = useState(0);
|
||||||
const detailsPageSize = 5;
|
const [detailsPageSize, setDetailsPageSize] = useState(5);
|
||||||
|
|
||||||
const fetchStats = async () => {
|
const fetchStats = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -92,24 +86,13 @@ const Feedback: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchFeedbacks = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiFetch(`http://${window.location.hostname}:8080/api/feedback?page=${page}&size=${pageSize}`);
|
|
||||||
const data = await response.json();
|
|
||||||
setFeedbacks(data?.content || []);
|
|
||||||
setTotalElements(data?.totalElements || 0);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching feedbacks:', error);
|
|
||||||
setFeedbacks([]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchItemData = async (itemName: string, pageNum: number) => {
|
const fetchItemData = async (itemName: string, pageNum: number) => {
|
||||||
setItemDetailsLoading(true);
|
setItemDetailsLoading(true);
|
||||||
try {
|
try {
|
||||||
const [detailsRes, statsRes] = await Promise.all([
|
const [detailsRes, statsRes] = await Promise.all([
|
||||||
fetch(`http://${window.location.hostname}:8080/api/feedback/item-details?productName=${encodeURIComponent(itemName)}&page=${pageNum}&size=${detailsPageSize}`),
|
apiFetch(`http://${window.location.hostname}:8080/api/feedback/item-details?productName=${encodeURIComponent(itemName)}&page=${pageNum}&size=${detailsPageSize}`),
|
||||||
fetch(`http://${window.location.hostname}:8080/api/feedback/item-stats?productName=${encodeURIComponent(itemName)}`)
|
apiFetch(`http://${window.location.hostname}:8080/api/feedback/item-stats?productName=${encodeURIComponent(itemName)}`)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const detailsData = await detailsRes.json();
|
const detailsData = await detailsRes.json();
|
||||||
@@ -130,9 +113,7 @@ const Feedback: React.FC = () => {
|
|||||||
fetchStats();
|
fetchStats();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchFeedbacks();
|
|
||||||
}, [page]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedItem) {
|
if (selectedItem) {
|
||||||
@@ -462,6 +443,7 @@ const Feedback: React.FC = () => {
|
|||||||
pageSize={detailsPageSize}
|
pageSize={detailsPageSize}
|
||||||
totalElements={detailsTotal}
|
totalElements={detailsTotal}
|
||||||
onPageChange={setDetailsPage}
|
onPageChange={setDetailsPage}
|
||||||
|
onPageSizeChange={setDetailsPageSize}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { apiFetch } from '../api';
|
import { apiFetch } from '../api';
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
X, Search, RefreshCw, Edit2, Package, Image as ImageIcon,
|
X, RefreshCw, Edit2, Package, Image as ImageIcon,
|
||||||
Clock, Check, Trash2, Rocket, ArrowRight, AlertCircle
|
Clock, Check, Trash2, Rocket, ArrowRight, AlertCircle
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
@@ -62,7 +62,6 @@ const NewArrivals: React.FC = () => {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
const [showSessionModal, setShowSessionModal] = useState(false);
|
const [showSessionModal, setShowSessionModal] = useState(false);
|
||||||
const [editingDraft, setEditingDraft] = useState<Product | null>(null);
|
|
||||||
const [formData, setFormData] = useState<Product | null>(null);
|
const [formData, setFormData] = useState<Product | null>(null);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
@@ -91,8 +90,8 @@ const NewArrivals: React.FC = () => {
|
|||||||
const fetchMetadata = async () => {
|
const fetchMetadata = async () => {
|
||||||
try {
|
try {
|
||||||
const [stallsRes, itemsRes] = await Promise.all([
|
const [stallsRes, itemsRes] = await Promise.all([
|
||||||
fetch('/api/stalls'),
|
apiFetch('/api/stalls'),
|
||||||
fetch('/api/base-items?size=100')
|
apiFetch('/api/base-items?size=100')
|
||||||
]);
|
]);
|
||||||
setAllStalls(await stallsRes.json());
|
setAllStalls(await stallsRes.json());
|
||||||
const itemsData = await itemsRes.json();
|
const itemsData = await itemsRes.json();
|
||||||
@@ -103,7 +102,6 @@ const NewArrivals: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (product: Product) => {
|
const handleEdit = (product: Product) => {
|
||||||
setEditingDraft(product);
|
|
||||||
setFormData({
|
setFormData({
|
||||||
...product,
|
...product,
|
||||||
sessions: product.sessions && product.sessions.length > 0 ? product.sessions : getDefaultSessions()
|
sessions: product.sessions && product.sessions.length > 0 ? product.sessions : getDefaultSessions()
|
||||||
@@ -124,7 +122,6 @@ const NewArrivals: React.FC = () => {
|
|||||||
});
|
});
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setShowModal(false);
|
setShowModal(false);
|
||||||
setEditingDraft(null);
|
|
||||||
setFormData(null);
|
setFormData(null);
|
||||||
fetchDrafts();
|
fetchDrafts();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { apiFetch } from '../api';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Filter,
|
Filter,
|
||||||
@@ -28,8 +29,8 @@ const PurchaseSummary = () => {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
// Fetch summary and bills in parallel
|
// Fetch summary and bills in parallel
|
||||||
const [summaryRes, billsRes] = await Promise.all([
|
const [summaryRes, billsRes] = await Promise.all([
|
||||||
fetch('/api/purchases/summary'),
|
apiFetch('/api/purchases/summary'),
|
||||||
fetch('/api/purchases/orders')
|
apiFetch('/api/purchases/orders')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (summaryRes.ok && billsRes.ok) {
|
if (summaryRes.ok && billsRes.ok) {
|
||||||
|
|||||||
@@ -1,27 +1,14 @@
|
|||||||
import { apiFetch } from '../api';
|
import { apiFetch } from '../api';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
UserPlus,
|
UserPlus,
|
||||||
Search,
|
Search,
|
||||||
Trash2,
|
Trash2,
|
||||||
Shield,
|
|
||||||
Mail,
|
Mail,
|
||||||
Lock,
|
Lock,
|
||||||
Check,
|
Check,
|
||||||
X,
|
X,
|
||||||
LayoutGrid,
|
|
||||||
Receipt,
|
|
||||||
Users,
|
|
||||||
ShoppingBag,
|
|
||||||
ShoppingCart,
|
|
||||||
Wallet,
|
|
||||||
BarChart3,
|
|
||||||
Table2,
|
|
||||||
CreditCard,
|
|
||||||
Megaphone,
|
|
||||||
MessageSquare,
|
|
||||||
Store,
|
|
||||||
Contact
|
Contact
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
@@ -110,7 +97,7 @@ const Staff = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredStaff = staff.filter(s => {
|
const filteredStaff = staffList.filter(s => {
|
||||||
const query = (searchQuery || '').toLowerCase();
|
const query = (searchQuery || '').toLowerCase();
|
||||||
return (s.name || '').toLowerCase().includes(query) ||
|
return (s.name || '').toLowerCase().includes(query) ||
|
||||||
(s.email || '').toLowerCase().includes(query);
|
(s.email || '').toLowerCase().includes(query);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { apiFetch } from '../api';
|
import { apiFetch } from '../api';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
@@ -195,8 +195,8 @@ const Stalls: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const host = window.location.hostname;
|
const host = window.location.hostname;
|
||||||
const [prodRes, baseRes] = await Promise.all([
|
const [prodRes, baseRes] = await Promise.all([
|
||||||
fetch(`http://${host}:8080/api/products?size=1000`),
|
apiFetch(`http://${host}:8080/api/products?size=1000`),
|
||||||
fetch(`http://${host}:8080/api/base-items?size=100`)
|
apiFetch(`http://${host}:8080/api/base-items?size=100`)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (prodRes.ok) {
|
if (prodRes.ok) {
|
||||||
|
|||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "Positeasy-Clone",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user