First template code generated from replit.com

This commit is contained in:
2025-08-28 19:26:17 +05:30
commit ba255ebd90
104 changed files with 20669 additions and 0 deletions

71
server/index.ts Normal file
View File

@@ -0,0 +1,71 @@
import express, { type Request, Response, NextFunction } from "express";
import { registerRoutes } from "./routes";
import { setupVite, serveStatic, log } from "./vite";
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use((req, res, next) => {
const start = Date.now();
const path = req.path;
let capturedJsonResponse: Record<string, any> | undefined = undefined;
const originalResJson = res.json;
res.json = function (bodyJson, ...args) {
capturedJsonResponse = bodyJson;
return originalResJson.apply(res, [bodyJson, ...args]);
};
res.on("finish", () => {
const duration = Date.now() - start;
if (path.startsWith("/api")) {
let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
if (capturedJsonResponse) {
logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
}
if (logLine.length > 80) {
logLine = logLine.slice(0, 79) + "…";
}
log(logLine);
}
});
next();
});
(async () => {
const server = await registerRoutes(app);
app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
const status = err.status || err.statusCode || 500;
const message = err.message || "Internal Server Error";
res.status(status).json({ message });
throw err;
});
// importantly only setup vite in development and after
// setting up all the other routes so the catch-all route
// doesn't interfere with the other routes
if (app.get("env") === "development") {
await setupVite(app, server);
} else {
serveStatic(app);
}
// ALWAYS serve the app on the port specified in the environment variable PORT
// Other ports are firewalled. Default to 5000 if not specified.
// this serves both the API and the client.
// It is the only port that is not firewalled.
const port = parseInt(process.env.PORT || '5000', 10);
server.listen({
port,
host: "0.0.0.0",
reusePort: true,
}, () => {
log(`serving on port ${port}`);
});
})();

258
server/routes.ts Normal file
View File

@@ -0,0 +1,258 @@
import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { insertProductSchema, insertStockMovementSchema, insertDocumentSchema } from "@shared/schema";
import { z } from "zod";
export async function registerRoutes(app: Express): Promise<Server> {
// Products
app.get("/api/products", async (req, res) => {
try {
const products = await storage.getProducts();
res.json(products);
} catch (error) {
res.status(500).json({ message: "Failed to fetch products" });
}
});
app.get("/api/products/search", async (req, res) => {
try {
const query = req.query.q as string;
if (!query) {
return res.status(400).json({ message: "Search query is required" });
}
const products = await storage.searchProducts(query);
res.json(products);
} catch (error) {
res.status(500).json({ message: "Failed to search products" });
}
});
app.get("/api/products/low-stock", async (req, res) => {
try {
const products = await storage.getLowStockProducts();
res.json(products);
} catch (error) {
res.status(500).json({ message: "Failed to fetch low stock products" });
}
});
app.get("/api/products/out-of-stock", async (req, res) => {
try {
const products = await storage.getOutOfStockProducts();
res.json(products);
} catch (error) {
res.status(500).json({ message: "Failed to fetch out of stock products" });
}
});
app.get("/api/products/:id", async (req, res) => {
try {
const product = await storage.getProduct(req.params.id);
if (!product) {
return res.status(404).json({ message: "Product not found" });
}
res.json(product);
} catch (error) {
res.status(500).json({ message: "Failed to fetch product" });
}
});
app.post("/api/products", async (req, res) => {
try {
const productData = insertProductSchema.parse(req.body);
const product = await storage.createProduct(productData);
res.status(201).json(product);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: "Invalid product data", errors: error.errors });
}
res.status(500).json({ message: "Failed to create product" });
}
});
app.patch("/api/products/:id", async (req, res) => {
try {
const updates = insertProductSchema.partial().parse(req.body);
const product = await storage.updateProduct(req.params.id, updates);
if (!product) {
return res.status(404).json({ message: "Product not found" });
}
res.json(product);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: "Invalid product data", errors: error.errors });
}
res.status(500).json({ message: "Failed to update product" });
}
});
app.delete("/api/products/:id", async (req, res) => {
try {
const deleted = await storage.deleteProduct(req.params.id);
if (!deleted) {
return res.status(404).json({ message: "Product not found" });
}
res.status(204).send();
} catch (error) {
res.status(500).json({ message: "Failed to delete product" });
}
});
// Stock movements
app.get("/api/stock-movements", async (req, res) => {
try {
const productId = req.query.productId as string;
const movements = await storage.getStockMovements(productId);
res.json(movements);
} catch (error) {
res.status(500).json({ message: "Failed to fetch stock movements" });
}
});
app.post("/api/stock-movements", async (req, res) => {
try {
const movementData = insertStockMovementSchema.parse(req.body);
const movement = await storage.createStockMovement(movementData);
res.status(201).json(movement);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: "Invalid stock movement data", errors: error.errors });
}
res.status(500).json({ message: "Failed to create stock movement" });
}
});
// Documents
app.get("/api/documents", async (req, res) => {
try {
const documents = await storage.getDocuments();
res.json(documents);
} catch (error) {
res.status(500).json({ message: "Failed to fetch documents" });
}
});
app.get("/api/documents/:id", async (req, res) => {
try {
const document = await storage.getDocument(req.params.id);
if (!document) {
return res.status(404).json({ message: "Document not found" });
}
res.json(document);
} catch (error) {
res.status(500).json({ message: "Failed to fetch document" });
}
});
app.post("/api/documents", async (req, res) => {
try {
const documentData = insertDocumentSchema.parse(req.body);
const document = await storage.createDocument(documentData);
res.status(201).json(document);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: "Invalid document data", errors: error.errors });
}
res.status(500).json({ message: "Failed to create document" });
}
});
app.patch("/api/documents/:id", async (req, res) => {
try {
const updates = insertDocumentSchema.partial().parse(req.body);
const document = await storage.updateDocument(req.params.id, updates);
if (!document) {
return res.status(404).json({ message: "Document not found" });
}
res.json(document);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: "Invalid document data", errors: error.errors });
}
res.status(500).json({ message: "Failed to update document" });
}
});
// Generate PDF document
app.get("/api/documents/:id/pdf", async (req, res) => {
try {
const document = await storage.getDocument(req.params.id);
if (!document) {
return res.status(404).json({ message: "Document not found" });
}
// Simple PDF generation for now - in production would use proper PDF library
const pdfContent = `
Document: ${document.title}
Type: ${document.type}
Number: ${document.documentNumber}
Status: ${document.status}
Created: ${document.createdAt}
Content:
${JSON.stringify(document.content, null, 2)}
`;
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="${document.documentNumber}.pdf"`);
res.send(pdfContent);
} catch (error) {
res.status(500).json({ message: "Failed to generate PDF" });
}
});
// Notifications
app.get("/api/notifications", async (req, res) => {
try {
const notifications = await storage.getNotifications();
res.json(notifications);
} catch (error) {
res.status(500).json({ message: "Failed to fetch notifications" });
}
});
app.get("/api/notifications/unread-count", async (req, res) => {
try {
const count = await storage.getUnreadNotificationCount();
res.json({ count });
} catch (error) {
res.status(500).json({ message: "Failed to fetch notification count" });
}
});
app.patch("/api/notifications/:id/read", async (req, res) => {
try {
const success = await storage.markNotificationAsRead(req.params.id);
if (!success) {
return res.status(404).json({ message: "Notification not found" });
}
res.status(204).send();
} catch (error) {
res.status(500).json({ message: "Failed to mark notification as read" });
}
});
// Dashboard stats
app.get("/api/dashboard/stats", async (req, res) => {
try {
const products = await storage.getProducts();
const lowStockProducts = await storage.getLowStockProducts();
const outOfStockProducts = await storage.getOutOfStockProducts();
const stats = {
totalItems: products.length,
inStockItems: products.filter(p => p.currentStock > p.minThreshold).length,
lowStockItems: lowStockProducts.length,
outOfStockItems: outOfStockProducts.length,
};
res.json(stats);
} catch (error) {
res.status(500).json({ message: "Failed to fetch dashboard stats" });
}
});
const httpServer = createServer(app);
return httpServer;
}

349
server/storage.ts Normal file
View File

@@ -0,0 +1,349 @@
import {
type Product,
type InsertProduct,
type StockMovement,
type InsertStockMovement,
type Document,
type InsertDocument,
type Order,
type InsertOrder,
type Notification,
type InsertNotification
} from "@shared/schema";
import { randomUUID } from "crypto";
export interface IStorage {
// Products
getProducts(): Promise<Product[]>;
getProduct(id: string): Promise<Product | undefined>;
getProductBySku(sku: string): Promise<Product | undefined>;
createProduct(product: InsertProduct): Promise<Product>;
updateProduct(id: string, product: Partial<InsertProduct>): Promise<Product | undefined>;
deleteProduct(id: string): Promise<boolean>;
searchProducts(query: string): Promise<Product[]>;
getLowStockProducts(): Promise<Product[]>;
getOutOfStockProducts(): Promise<Product[]>;
// Stock movements
getStockMovements(productId?: string): Promise<StockMovement[]>;
createStockMovement(movement: InsertStockMovement): Promise<StockMovement>;
// Documents
getDocuments(): Promise<Document[]>;
getDocument(id: string): Promise<Document | undefined>;
createDocument(document: InsertDocument): Promise<Document>;
updateDocument(id: string, document: Partial<InsertDocument>): Promise<Document | undefined>;
deleteDocument(id: string): Promise<boolean>;
// Orders
getOrders(): Promise<Order[]>;
getOrder(id: string): Promise<Order | undefined>;
createOrder(order: InsertOrder): Promise<Order>;
updateOrder(id: string, order: Partial<InsertOrder>): Promise<Order | undefined>;
// Notifications
getNotifications(): Promise<Notification[]>;
createNotification(notification: InsertNotification): Promise<Notification>;
markNotificationAsRead(id: string): Promise<boolean>;
getUnreadNotificationCount(): Promise<number>;
}
export class MemStorage implements IStorage {
private products: Map<string, Product>;
private stockMovements: Map<string, StockMovement>;
private documents: Map<string, Document>;
private orders: Map<string, Order>;
private notifications: Map<string, Notification>;
constructor() {
this.products = new Map();
this.stockMovements = new Map();
this.documents = new Map();
this.orders = new Map();
this.notifications = new Map();
// Initialize with sample data
this.initializeSampleData();
}
private initializeSampleData() {
const sampleProducts: Product[] = [
{
id: "1",
sku: "SH-001",
name: "Safety Helmets - White",
description: "High-quality safety helmets for construction work",
currentStock: 125,
minThreshold: 20,
unit: "units",
price: "29.99",
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: "2",
sku: "WG-003",
name: "Work Gloves - Medium",
description: "Durable work gloves for general construction",
currentStock: 8,
minThreshold: 15,
unit: "pairs",
price: "12.50",
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: "3",
sku: "HV-005",
name: "Hi-Vis Vests - Large",
description: "High-visibility safety vests",
currentStock: 0,
minThreshold: 10,
unit: "units",
price: "15.75",
createdAt: new Date(),
updatedAt: new Date(),
},
];
sampleProducts.forEach(product => {
this.products.set(product.id, product);
});
// Create notifications for low/out of stock items
const lowStockNotification: Notification = {
id: "notif-1",
type: "low_stock",
title: "Low Stock Alert",
message: "Work Gloves - Medium is below minimum threshold",
isRead: false,
relatedId: "2",
createdAt: new Date(),
};
const outOfStockNotification: Notification = {
id: "notif-2",
type: "out_of_stock",
title: "Out of Stock Alert",
message: "Hi-Vis Vests - Large is out of stock",
isRead: false,
relatedId: "3",
createdAt: new Date(),
};
this.notifications.set("notif-1", lowStockNotification);
this.notifications.set("notif-2", outOfStockNotification);
}
// Products
async getProducts(): Promise<Product[]> {
return Array.from(this.products.values());
}
async getProduct(id: string): Promise<Product | undefined> {
return this.products.get(id);
}
async getProductBySku(sku: string): Promise<Product | undefined> {
return Array.from(this.products.values()).find(product => product.sku === sku);
}
async createProduct(insertProduct: InsertProduct): Promise<Product> {
const id = randomUUID();
const product: Product = {
...insertProduct,
id,
description: insertProduct.description ?? null,
createdAt: new Date(),
updatedAt: new Date(),
};
this.products.set(id, product);
return product;
}
async updateProduct(id: string, updates: Partial<InsertProduct>): Promise<Product | undefined> {
const product = this.products.get(id);
if (!product) return undefined;
const updatedProduct: Product = {
...product,
...updates,
updatedAt: new Date(),
};
this.products.set(id, updatedProduct);
return updatedProduct;
}
async deleteProduct(id: string): Promise<boolean> {
return this.products.delete(id);
}
async searchProducts(query: string): Promise<Product[]> {
const products = Array.from(this.products.values());
const lowercaseQuery = query.toLowerCase();
return products.filter(product =>
product.name.toLowerCase().includes(lowercaseQuery) ||
product.sku.toLowerCase().includes(lowercaseQuery) ||
product.description?.toLowerCase().includes(lowercaseQuery)
);
}
async getLowStockProducts(): Promise<Product[]> {
return Array.from(this.products.values()).filter(
product => product.currentStock > 0 && product.currentStock <= product.minThreshold
);
}
async getOutOfStockProducts(): Promise<Product[]> {
return Array.from(this.products.values()).filter(
product => product.currentStock === 0
);
}
// Stock movements
async getStockMovements(productId?: string): Promise<StockMovement[]> {
const movements = Array.from(this.stockMovements.values());
if (productId) {
return movements.filter(movement => movement.productId === productId);
}
return movements;
}
async createStockMovement(insertMovement: InsertStockMovement): Promise<StockMovement> {
const id = randomUUID();
const movement: StockMovement = {
...insertMovement,
id,
reason: insertMovement.reason ?? null,
createdAt: new Date(),
};
this.stockMovements.set(id, movement);
// Update product stock
const product = this.products.get(insertMovement.productId);
if (product) {
let newStock = product.currentStock;
if (insertMovement.type === 'in') {
newStock += insertMovement.quantity;
} else if (insertMovement.type === 'out') {
newStock -= insertMovement.quantity;
} else if (insertMovement.type === 'adjustment') {
newStock = insertMovement.quantity;
}
await this.updateProduct(insertMovement.productId, { currentStock: Math.max(0, newStock) });
}
return movement;
}
// Documents
async getDocuments(): Promise<Document[]> {
return Array.from(this.documents.values());
}
async getDocument(id: string): Promise<Document | undefined> {
return this.documents.get(id);
}
async createDocument(insertDocument: InsertDocument): Promise<Document> {
const id = randomUUID();
const document: Document = {
...insertDocument,
id,
content: insertDocument.content ?? {},
status: insertDocument.status ?? "draft",
createdAt: new Date(),
updatedAt: new Date(),
};
this.documents.set(id, document);
return document;
}
async updateDocument(id: string, updates: Partial<InsertDocument>): Promise<Document | undefined> {
const document = this.documents.get(id);
if (!document) return undefined;
const updatedDocument: Document = {
...document,
...updates,
updatedAt: new Date(),
};
this.documents.set(id, updatedDocument);
return updatedDocument;
}
async deleteDocument(id: string): Promise<boolean> {
return this.documents.delete(id);
}
// Orders
async getOrders(): Promise<Order[]> {
return Array.from(this.orders.values());
}
async getOrder(id: string): Promise<Order | undefined> {
return this.orders.get(id);
}
async createOrder(insertOrder: InsertOrder): Promise<Order> {
const id = randomUUID();
const order: Order = {
...insertOrder,
id,
status: insertOrder.status ?? "pending",
createdAt: new Date(),
updatedAt: new Date(),
};
this.orders.set(id, order);
return order;
}
async updateOrder(id: string, updates: Partial<InsertOrder>): Promise<Order | undefined> {
const order = this.orders.get(id);
if (!order) return undefined;
const updatedOrder: Order = {
...order,
...updates,
updatedAt: new Date(),
};
this.orders.set(id, updatedOrder);
return updatedOrder;
}
// Notifications
async getNotifications(): Promise<Notification[]> {
return Array.from(this.notifications.values()).sort(
(a, b) => (b.createdAt?.getTime() || 0) - (a.createdAt?.getTime() || 0)
);
}
async createNotification(insertNotification: InsertNotification): Promise<Notification> {
const id = randomUUID();
const notification: Notification = {
...insertNotification,
id,
isRead: insertNotification.isRead ?? false,
relatedId: insertNotification.relatedId ?? null,
createdAt: new Date(),
};
this.notifications.set(id, notification);
return notification;
}
async markNotificationAsRead(id: string): Promise<boolean> {
const notification = this.notifications.get(id);
if (!notification) return false;
notification.isRead = true;
this.notifications.set(id, notification);
return true;
}
async getUnreadNotificationCount(): Promise<number> {
return Array.from(this.notifications.values()).filter(n => !n.isRead).length;
}
}
export const storage = new MemStorage();

85
server/vite.ts Normal file
View File

@@ -0,0 +1,85 @@
import express, { type Express } from "express";
import fs from "fs";
import path from "path";
import { createServer as createViteServer, createLogger } from "vite";
import { type Server } from "http";
import viteConfig from "../vite.config";
import { nanoid } from "nanoid";
const viteLogger = createLogger();
export function log(message: string, source = "express") {
const formattedTime = new Date().toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: true,
});
console.log(`${formattedTime} [${source}] ${message}`);
}
export async function setupVite(app: Express, server: Server) {
const serverOptions = {
middlewareMode: true,
hmr: { server },
allowedHosts: true as const,
};
const vite = await createViteServer({
...viteConfig,
configFile: false,
customLogger: {
...viteLogger,
error: (msg, options) => {
viteLogger.error(msg, options);
process.exit(1);
},
},
server: serverOptions,
appType: "custom",
});
app.use(vite.middlewares);
app.use("*", async (req, res, next) => {
const url = req.originalUrl;
try {
const clientTemplate = path.resolve(
import.meta.dirname,
"..",
"client",
"index.html",
);
// always reload the index.html file from disk incase it changes
let template = await fs.promises.readFile(clientTemplate, "utf-8");
template = template.replace(
`src="/src/main.tsx"`,
`src="/src/main.tsx?v=${nanoid()}"`,
);
const page = await vite.transformIndexHtml(url, template);
res.status(200).set({ "Content-Type": "text/html" }).end(page);
} catch (e) {
vite.ssrFixStacktrace(e as Error);
next(e);
}
});
}
export function serveStatic(app: Express) {
const distPath = path.resolve(import.meta.dirname, "public");
if (!fs.existsSync(distPath)) {
throw new Error(
`Could not find the build directory: ${distPath}, make sure to build the client first`,
);
}
app.use(express.static(distPath));
// fall through to index.html if the file doesn't exist
app.use("*", (_req, res) => {
res.sendFile(path.resolve(distPath, "index.html"));
});
}