Add ability to view and manage DLC products with role-based access

Implement API endpoints for fetching DLC products, their statistics, and individual details, with filtering and permission checks based on user roles and group IDs. Update storage methods to support group-based filtering for customer orders, DLC products, and tasks.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 7c468936-2a88-4686-ac0a-84921a45222d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/7c468936-2a88-4686-ac0a-84921a45222d/KsafRhL
This commit is contained in:
michaelschal committed 2025-08-13 12:39:57 +00:00
1 parent 9fd0a9d26c
commit 5f5ea30c78
5 files changed
+524 -10

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

+1 -1
View File
@@ -442,7 +442,7 @@ export default function DlcPage() {
}
};
const filteredProducts = dlcProducts.filter(product => {
const filteredProducts = (dlcProducts || []).filter(product => {
if (searchTerm) {
return product.productName.toLowerCase().includes(searchTerm.toLowerCase()) ||
(product.supplier?.name && product.supplier.name.toLowerCase().includes(searchTerm.toLowerCase()));
+1 -1
View File
@@ -2,4 +2,4 @@
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_localhost FALSE / FALSE 1755174893 connect.sid s%3AnBdh-Dk3_lel7Iwxh8gfP8z9NpAO6ksl.nU8o8o7MiYpWRYfCNtxnZrgrQwhlUHQqUbUPQXD7uMI
#HttpOnly_localhost FALSE / FALSE 1755175168 connect.sid s%3AXOwkelwwA5ZeDs-gJuKcdEXK9b4Bk627.LH21I0KeUC6s6IPDiFTXL31UaqY8BVhwFQNagdkpoKg
+482
View File
@@ -958,6 +958,488 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// DLC Products routes
app.get('/api/dlc-products', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const { storeId, status, supplierId } = req.query;
let groupIds: number[] | undefined;
if (user.role === 'admin') {
groupIds = storeId ? [parseInt(storeId as string)] : undefined;
} else {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (storeId && userGroupIds.includes(parseInt(storeId as string))) {
groupIds = [parseInt(storeId as string)];
} else {
groupIds = userGroupIds;
}
}
const filters: any = {};
if (status && status !== 'all') filters.status = status as string;
if (supplierId && supplierId !== 'all') filters.supplierId = parseInt(supplierId as string);
console.log('DLC Products API called with:', { groupIds, filters, userRole: user.role });
const dlcProducts = await storage.getDlcProducts(groupIds, filters);
console.log('DLC Products returned:', dlcProducts.length, 'items');
res.json(dlcProducts);
} catch (error) {
console.error("Error fetching DLC products:", error);
res.status(500).json([]);
}
});
app.get('/api/dlc-products/stats', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const { storeId } = req.query;
let groupIds: number[] | undefined;
if (user.role === 'admin') {
groupIds = storeId ? [parseInt(storeId as string)] : undefined;
} else {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (storeId && userGroupIds.includes(parseInt(storeId as string))) {
groupIds = [parseInt(storeId as string)];
} else {
groupIds = userGroupIds;
}
}
const stats = await storage.getDlcStats(groupIds);
res.json(stats);
} catch (error) {
console.error("Error fetching DLC stats:", error);
res.status(500).json({ active: 0, expiringSoon: 0, expired: 0 });
}
});
app.get('/api/dlc-products/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const dlcProduct = await storage.getDlcProduct(id);
if (!dlcProduct) {
return res.status(404).json({ message: "DLC Product not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(dlcProduct.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
res.json(dlcProduct);
} catch (error) {
console.error("Error fetching DLC product:", error);
res.status(500).json({ message: "Failed to fetch DLC product" });
}
});
app.post('/api/dlc-products', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const data = {
...req.body,
createdBy: user.id,
};
// Check if user has access to the group
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(data.groupId)) {
return res.status(403).json({ message: "Access denied to this group" });
}
}
const dlcProduct = await storage.createDlcProduct(data);
res.json(dlcProduct);
} catch (error) {
console.error("Error creating DLC product:", error);
res.status(500).json({ message: "Failed to create DLC product" });
}
});
app.put('/api/dlc-products/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const dlcProduct = await storage.getDlcProduct(id);
if (!dlcProduct) {
return res.status(404).json({ message: "DLC Product not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(dlcProduct.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
const updatedProduct = await storage.updateDlcProduct(id, req.body);
res.json(updatedProduct);
} catch (error) {
console.error("Error updating DLC product:", error);
res.status(500).json({ message: "Failed to update DLC product" });
}
});
app.delete('/api/dlc-products/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const dlcProduct = await storage.getDlcProduct(id);
if (!dlcProduct) {
return res.status(404).json({ message: "DLC Product not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(dlcProduct.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
await storage.deleteDlcProduct(id);
res.json({ message: "DLC Product deleted successfully" });
} catch (error) {
console.error("Error deleting DLC product:", error);
res.status(500).json({ message: "Failed to delete DLC product" });
}
});
app.post('/api/dlc-products/:id/validate', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const dlcProduct = await storage.getDlcProduct(id);
if (!dlcProduct) {
return res.status(404).json({ message: "DLC Product not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(dlcProduct.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
const validatedProduct = await storage.validateDlcProduct(id, user.id);
res.json(validatedProduct);
} catch (error) {
console.error("Error validating DLC product:", error);
res.status(500).json({ message: "Failed to validate DLC product" });
}
});
// Tasks routes
app.get('/api/tasks', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const { storeId } = req.query;
let groupIds: number[] | undefined;
if (user.role === 'admin') {
groupIds = storeId ? [parseInt(storeId as string)] : undefined;
} else {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (storeId && userGroupIds.includes(parseInt(storeId as string))) {
groupIds = [parseInt(storeId as string)];
} else {
groupIds = userGroupIds;
}
}
console.log('Tasks API called with:', { groupIds, userRole: user.role });
const tasks = await storage.getTasks(groupIds);
console.log('Tasks returned:', tasks.length, 'items');
res.json(tasks);
} catch (error) {
console.error("Error fetching tasks:", error);
res.status(500).json([]);
}
});
app.post('/api/tasks', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const data = {
...req.body,
createdBy: user.id,
};
// Check if user has access to the group
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(data.groupId)) {
return res.status(403).json({ message: "Access denied to this group" });
}
}
const task = await storage.createTask(data);
res.json(task);
} catch (error) {
console.error("Error creating task:", error);
res.status(500).json({ message: "Failed to create task" });
}
});
app.put('/api/tasks/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const task = await storage.getTask(id);
if (!task) {
return res.status(404).json({ message: "Task not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(task.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
const updatedTask = await storage.updateTask(id, req.body);
res.json(updatedTask);
} catch (error) {
console.error("Error updating task:", error);
res.status(500).json({ message: "Failed to update task" });
}
});
app.delete('/api/tasks/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const task = await storage.getTask(id);
if (!task) {
return res.status(404).json({ message: "Task not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(task.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
await storage.deleteTask(id);
res.json({ message: "Task deleted successfully" });
} catch (error) {
console.error("Error deleting task:", error);
res.status(500).json({ message: "Failed to delete task" });
}
});
app.post('/api/tasks/:id/complete', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const task = await storage.getTask(id);
if (!task) {
return res.status(404).json({ message: "Task not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(task.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
await storage.completeTask(id, user.id);
res.json({ message: "Task completed successfully" });
} catch (error) {
console.error("Error completing task:", error);
res.status(500).json({ message: "Failed to complete task" });
}
});
// Customer Orders routes
app.get('/api/customer-orders', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const { storeId } = req.query;
let groupIds: number[] | undefined;
if (user.role === 'admin') {
groupIds = storeId ? [parseInt(storeId as string)] : undefined;
} else {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (storeId && userGroupIds.includes(parseInt(storeId as string))) {
groupIds = [parseInt(storeId as string)];
} else {
groupIds = userGroupIds;
}
}
console.log('Customer Orders API called with:', { groupIds, userRole: user.role });
const customerOrders = await storage.getCustomerOrders(groupIds);
console.log('Customer Orders returned:', customerOrders.length, 'items');
res.json(customerOrders);
} catch (error) {
console.error("Error fetching customer orders:", error);
res.status(500).json([]);
}
});
app.post('/api/customer-orders', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const data = {
...req.body,
createdBy: user.id,
};
// Check if user has access to the group
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(data.groupId)) {
return res.status(403).json({ message: "Access denied to this group" });
}
}
const customerOrder = await storage.createCustomerOrder(data);
res.json(customerOrder);
} catch (error) {
console.error("Error creating customer order:", error);
res.status(500).json({ message: "Failed to create customer order" });
}
});
app.put('/api/customer-orders/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const customerOrder = await storage.getCustomerOrder(id);
if (!customerOrder) {
return res.status(404).json({ message: "Customer order not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(customerOrder.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
const updatedOrder = await storage.updateCustomerOrder(id, req.body);
res.json(updatedOrder);
} catch (error) {
console.error("Error updating customer order:", error);
res.status(500).json({ message: "Failed to update customer order" });
}
});
app.delete('/api/customer-orders/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const id = parseInt(req.params.id);
const customerOrder = await storage.getCustomerOrder(id);
if (!customerOrder) {
return res.status(404).json({ message: "Customer order not found" });
}
// Check permissions
if (user.role !== 'admin') {
const userGroupIds = user.userGroups.map(ug => ug.groupId);
if (!userGroupIds.includes(customerOrder.groupId)) {
return res.status(403).json({ message: "Access denied" });
}
}
await storage.deleteCustomerOrder(id);
res.json({ message: "Customer order deleted successfully" });
} catch (error) {
console.error("Error deleting customer order:", error);
res.status(500).json({ message: "Failed to delete customer order" });
}
});
// Statistics routes
app.get('/api/stats/monthly', isAuthenticated, async (req: any, res) => {
try {
+40 -8
View File
@@ -1464,8 +1464,14 @@ export class MemStorage implements IStorage {
async getPublicityParticipations(): Promise<PublicityParticipation[]> { return []; }
async setPublicityParticipations(): Promise<void> {}
async getCustomerOrders(): Promise<CustomerOrderWithRelations[]> {
return Array.from(this.customerOrders.values()).map(order => ({
async getCustomerOrders(groupIds?: number[]): Promise<CustomerOrderWithRelations[]> {
let orders = Array.from(this.customerOrders.values());
if (groupIds && groupIds.length > 0) {
orders = orders.filter(order => groupIds.includes(order.groupId));
}
return orders.map(order => ({
...order,
supplier: this.suppliers.get(order.supplierId)!,
group: this.groups.get(order.groupId)!,
@@ -1515,8 +1521,23 @@ export class MemStorage implements IStorage {
this.customerOrders.delete(id);
}
async getDlcProducts(): Promise<DlcProductWithRelations[]> {
return Array.from(this.dlcProducts.values()).map(product => ({
async getDlcProducts(groupIds?: number[], filters?: { status?: string; supplierId?: number; }): Promise<DlcProductWithRelations[]> {
let products = Array.from(this.dlcProducts.values());
if (groupIds && groupIds.length > 0) {
products = products.filter(product => groupIds.includes(product.groupId));
}
if (filters) {
if (filters.status) {
products = products.filter(product => product.status === filters.status);
}
if (filters.supplierId) {
products = products.filter(product => product.supplierId === filters.supplierId);
}
}
return products.map(product => ({
...product,
supplier: this.suppliers.get(product.supplierId)!,
group: this.groups.get(product.groupId)!,
@@ -1580,12 +1601,17 @@ export class MemStorage implements IStorage {
return { ...updatedProduct, dlcDate: new Date(updatedProduct.expiryDate) } as DlcProductFrontend;
}
async getDlcStats(): Promise<{ active: number; expiringSoon: number; expired: number; }> {
async getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }> {
const today = new Date();
const alertDate = new Date();
alertDate.setDate(today.getDate() + 15);
const products = Array.from(this.dlcProducts.values());
let products = Array.from(this.dlcProducts.values());
if (groupIds && groupIds.length > 0) {
products = products.filter(product => groupIds.includes(product.groupId));
}
return {
active: products.filter(p => new Date(p.expiryDate) > today).length,
expiringSoon: products.filter(p => {
@@ -1596,8 +1622,14 @@ export class MemStorage implements IStorage {
};
}
async getTasks(): Promise<TaskWithRelations[]> {
return Array.from(this.tasks.values()).map(task => ({
async getTasks(groupIds?: number[]): Promise<TaskWithRelations[]> {
let tasks = Array.from(this.tasks.values());
if (groupIds && groupIds.length > 0) {
tasks = tasks.filter(task => groupIds.includes(task.groupId));
}
return tasks.map(task => ({
...task,
group: this.groups.get(task.groupId)!,
creator: this.users.get(task.createdBy)!