feat: complete BetterHuman HR portal with full-stack implementation

This commit is contained in:
BetterHuman
2026-05-05 23:02:42 +00:00
parent 2ad76ae14a
commit 2d47aa8da7
36436 changed files with 4350116 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
DATABASE_URL=postgresql://user:password@localhost:5432/betterhuman
JWT_SECRET=your-jwt-secret-change-in-production
JWT_REFRESH_SECRET=your-refresh-secret-change-in-production
SIGNOZ_OTEL_ENDPOINT=http://100.64.0.10:4318
PORT=5000
NODE_ENV=production
FRONTEND_URL=https://your-domain.com
+42
View File
@@ -0,0 +1,42 @@
# Stage 1: Build frontend
FROM node:20-alpine AS frontend-build
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm install --legacy-peer-deps
COPY frontend/ ./
RUN npm run build
# Stage 2: Build backend
FROM node:20-alpine AS backend-build
WORKDIR /app/backend
COPY backend/package*.json ./
RUN npm install
COPY backend/ ./
# Copy prisma schema for generation
RUN npx prisma generate --schema=src/prisma/schema.prisma
RUN npm run build
# Stage 3: Production
FROM node:20-alpine
WORKDIR /app
RUN apk add --no-cache nginx
# Copy backend
COPY --from=backend-build /app/backend/dist ./dist
COPY --from=backend-build /app/backend/node_modules ./node_modules
COPY --from=backend-build /app/backend/src/prisma ./prisma
COPY backend/package.json ./package.json
# Copy frontend build
COPY --from=frontend-build /app/frontend/dist ./public
# Nginx config
COPY nginx.conf /etc/nginx/nginx.conf
COPY start.sh ./
RUN chmod +x start.sh
EXPOSE 80
CMD ["./start.sh"]
+4
View File
@@ -0,0 +1,4 @@
import './instrumentation';
declare const app: import("express-serve-static-core").Express;
export default app;
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;AAuB3B,QAAA,MAAM,GAAG,6CAAY,CAAC;AA4DtB,eAAe,GAAG,CAAC"}
+79
View File
@@ -0,0 +1,79 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("./instrumentation");
const express_1 = __importDefault(require("express"));
const cors_1 = __importDefault(require("cors"));
const helmet_1 = __importDefault(require("helmet"));
const cookie_parser_1 = __importDefault(require("cookie-parser"));
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
const path_1 = __importDefault(require("path"));
const auth_1 = __importDefault(require("./routes/auth"));
const employees_1 = __importDefault(require("./routes/employees"));
const departments_1 = __importDefault(require("./routes/departments"));
const positions_1 = __importDefault(require("./routes/positions"));
const locations_1 = __importDefault(require("./routes/locations"));
const leave_1 = __importDefault(require("./routes/leave"));
const attendance_1 = __importDefault(require("./routes/attendance"));
const payroll_1 = __importDefault(require("./routes/payroll"));
const recruitment_1 = __importDefault(require("./routes/recruitment"));
const performance_1 = __importDefault(require("./routes/performance"));
const engagement_1 = __importDefault(require("./routes/engagement"));
const analytics_1 = __importDefault(require("./routes/analytics"));
const settings_1 = __importDefault(require("./routes/settings"));
const notifications_1 = __importDefault(require("./routes/notifications"));
const app = (0, express_1.default)();
const PORT = process.env.PORT || 5000;
// Security
app.use((0, helmet_1.default)({ contentSecurityPolicy: false }));
// CORS
app.use((0, cors_1.default)({
origin: [
'http://localhost:3000',
'http://localhost:5000',
'http://localhost:5173',
process.env.FRONTEND_URL || 'http://localhost:3000',
],
credentials: true,
}));
// Middleware
app.use(express_1.default.json({ limit: '10mb' }));
app.use((0, cookie_parser_1.default)());
// Rate limiting for auth
const authLimiter = (0, express_rate_limit_1.default)({
windowMs: 15 * 60 * 1000,
max: 20,
message: { error: 'Too many requests' },
});
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// API Routes
app.use('/api/v1/auth', authLimiter, auth_1.default);
app.use('/api/v1/employees', employees_1.default);
app.use('/api/v1/departments', departments_1.default);
app.use('/api/v1/positions', positions_1.default);
app.use('/api/v1/locations', locations_1.default);
app.use('/api/v1/leave', leave_1.default);
app.use('/api/v1/attendance', attendance_1.default);
app.use('/api/v1/payroll', payroll_1.default);
app.use('/api/v1/recruitment', recruitment_1.default);
app.use('/api/v1/performance', performance_1.default);
app.use('/api/v1/engagement', engagement_1.default);
app.use('/api/v1/analytics', analytics_1.default);
app.use('/api/v1/settings', settings_1.default);
app.use('/api/v1/notifications', notifications_1.default);
// Serve frontend in production
const frontendDist = path_1.default.join(__dirname, '../../public');
app.use(express_1.default.static(frontendDist));
app.get('*', (req, res) => {
res.sendFile(path_1.default.join(frontendDist, 'index.html'));
});
app.listen(PORT, () => {
console.log(`BetterHuman API running on port ${PORT}`);
});
exports.default = app;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=instrumentation.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":""}
+14
View File
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const sdk_node_1 = require("@opentelemetry/sdk-node");
const auto_instrumentations_node_1 = require("@opentelemetry/auto-instrumentations-node");
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
const sdk = new sdk_node_1.NodeSDK({
serviceName: 'betterhuman',
traceExporter: new exporter_trace_otlp_http_1.OTLPTraceExporter({
url: `${process.env.SIGNOZ_OTEL_ENDPOINT || 'http://100.64.0.10:4318'}/v1/traces`,
}),
instrumentations: [(0, auto_instrumentations_node_1.getNodeAutoInstrumentations)()],
});
sdk.start();
//# sourceMappingURL=instrumentation.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":";;AAAA,sDAAkD;AAClD,0FAAwF;AACxF,sFAA4E;AAE5E,MAAM,GAAG,GAAG,IAAI,kBAAO,CAAC;IACtB,WAAW,EAAE,aAAa;IAC1B,aAAa,EAAE,IAAI,4CAAiB,CAAC;QACnC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,yBAAyB,YAAY;KAClF,CAAC;IACF,gBAAgB,EAAE,CAAC,IAAA,wDAA2B,GAAE,CAAC;CAClD,CAAC,CAAC;AAEH,GAAG,CAAC,KAAK,EAAE,CAAC"}
+11
View File
@@ -0,0 +1,11 @@
export interface TokenPayload {
id: string;
companyId: string;
role: string;
employeeId?: string;
}
export declare function signAccessToken(payload: TokenPayload): string;
export declare function signRefreshToken(payload: TokenPayload): string;
export declare function verifyAccessToken(token: string): TokenPayload;
export declare function verifyRefreshToken(token: string): TokenPayload;
//# sourceMappingURL=jwt.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/lib/jwt.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAE7D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAE7D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAE9D"}
+25
View File
@@ -0,0 +1,25 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.signAccessToken = signAccessToken;
exports.signRefreshToken = signRefreshToken;
exports.verifyAccessToken = verifyAccessToken;
exports.verifyRefreshToken = verifyRefreshToken;
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const ACCESS_SECRET = process.env.JWT_SECRET || 'betterhuman-access-secret-2024';
const REFRESH_SECRET = process.env.JWT_REFRESH_SECRET || 'betterhuman-refresh-secret-2024';
function signAccessToken(payload) {
return jsonwebtoken_1.default.sign(payload, ACCESS_SECRET, { expiresIn: '15m' });
}
function signRefreshToken(payload) {
return jsonwebtoken_1.default.sign(payload, REFRESH_SECRET, { expiresIn: '7d' });
}
function verifyAccessToken(token) {
return jsonwebtoken_1.default.verify(token, ACCESS_SECRET);
}
function verifyRefreshToken(token) {
return jsonwebtoken_1.default.verify(token, REFRESH_SECRET);
}
//# sourceMappingURL=jwt.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/lib/jwt.ts"],"names":[],"mappings":";;;;;AAYA,0CAEC;AAED,4CAEC;AAED,8CAEC;AAED,gDAEC;AA1BD,gEAA+B;AAE/B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,gCAAgC,CAAC;AACjF,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,iCAAiC,CAAC;AAS3F,SAAgB,eAAe,CAAC,OAAqB;IACnD,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAqB;IACpD,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAgB,iBAAiB,CAAC,KAAa;IAC7C,OAAO,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAiB,CAAC;AAC1D,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa;IAC9C,OAAO,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAAiB,CAAC;AAC3D,CAAC"}
+4
View File
@@ -0,0 +1,4 @@
import { PrismaClient } from '@prisma/client';
export declare const prisma: PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
export default prisma;
//# sourceMappingURL=prisma.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/lib/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,eAAO,MAAM,MAAM,gIAEjB,CAAC;AAIH,eAAe,MAAM,CAAC"}
+12
View File
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.prisma = void 0;
const client_1 = require("@prisma/client");
const globalForPrisma = globalThis;
exports.prisma = globalForPrisma.prisma || new client_1.PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
if (process.env.NODE_ENV !== 'production')
globalForPrisma.prisma = exports.prisma;
exports.default = exports.prisma;
//# sourceMappingURL=prisma.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/lib/prisma.ts"],"names":[],"mappings":";;;AAAA,2CAA8C;AAE9C,MAAM,eAAe,GAAG,UAAiD,CAAC;AAE7D,QAAA,MAAM,GAAG,eAAe,CAAC,MAAM,IAAI,IAAI,qBAAY,CAAC;IAC/D,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;CACrF,CAAC,CAAC;AAEH,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;IAAE,eAAe,CAAC,MAAM,GAAG,cAAM,CAAC;AAE3E,kBAAe,cAAM,CAAC"}
+12
View File
@@ -0,0 +1,12 @@
import { Request, Response, NextFunction } from 'express';
export interface AuthRequest extends Request {
user?: {
id: string;
companyId: string;
role: string;
employeeId?: string;
};
}
export declare function requireAuth(req: AuthRequest, res: Response, next: NextFunction): Response<any, Record<string, any>>;
export declare function requireRole(roles: string[]): (req: AuthRequest, res: Response, next: NextFunction) => Response<any, Record<string, any>>;
//# sourceMappingURL=auth.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC1C,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,sCAc9E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IACjC,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,wCAS5D"}
+32
View File
@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.requireAuth = requireAuth;
exports.requireRole = requireRole;
const jwt_1 = require("../lib/jwt");
function requireAuth(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = authHeader.split(' ')[1];
try {
const payload = (0, jwt_1.verifyAccessToken)(token);
req.user = payload;
next();
}
catch (err) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
function requireRole(roles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
//# sourceMappingURL=auth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":";;AAYA,kCAcC;AAED,kCAUC;AArCD,oCAA+C;AAW/C,SAAgB,WAAW,CAAC,GAAgB,EAAE,GAAa,EAAE,IAAkB;IAC7E,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IAC7C,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACrD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,uBAAiB,EAAC,KAAK,CAAC,CAAC;QACzC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;QACnB,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;IACrE,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,KAAe;IACzC,OAAO,CAAC,GAAgB,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QAC7D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
+4
View File
@@ -0,0 +1,4 @@
import { Request, Response, NextFunction } from 'express';
import { ZodSchema } from 'zod';
export declare function validate(schema: ZodSchema): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>>;
//# sourceMappingURL=validate.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/middleware/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAEhC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,IAChC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,wCAWxD"}
+17
View File
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validate = validate;
function validate(schema) {
return (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation error',
details: result.error.errors,
});
}
req.body = result.data;
next();
};
}
//# sourceMappingURL=validate.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/middleware/validate.ts"],"names":[],"mappings":";;AAGA,4BAYC;AAZD,SAAgB,QAAQ,CAAC,MAAiB;IACxC,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,kBAAkB;gBACzB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;aAC7B,CAAC,CAAC;QACL,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACvB,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=analytics.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAkKxB,eAAe,MAAM,CAAC"}
+154
View File
@@ -0,0 +1,154 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /analytics/dashboard
router.get('/dashboard', auth_1.requireAuth, async (req, res) => {
try {
const companyId = req.user.companyId;
const today = new Date();
today.setHours(0, 0, 0, 0);
const [headcount, onLeave, pendingLeave, openJobs, latestPayroll,] = await Promise.all([
prisma_1.default.employee.count({ where: { companyId, status: { in: ['ACTIVE', 'PROBATION'] } } }),
prisma_1.default.leaveRequest.count({
where: {
status: 'APPROVED',
startDate: { lte: new Date() },
endDate: { gte: today },
employee: { companyId },
},
}),
prisma_1.default.leaveRequest.count({
where: { status: 'PENDING', employee: { companyId } },
}),
prisma_1.default.job.count({ where: { companyId, status: 'OPEN' } }),
prisma_1.default.payrollRun.findFirst({
where: { companyId, status: 'PROCESSED' },
orderBy: { createdAt: 'desc' },
}),
]);
return res.json({
headcount,
onLeave,
pendingLeave,
openJobs,
monthlyPayroll: latestPayroll?.totalNet || 0,
avgEngagement: 78, // placeholder
});
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /analytics/workforce
router.get('/workforce', auth_1.requireAuth, async (req, res) => {
try {
const companyId = req.user.companyId;
const employees = await prisma_1.default.employee.findMany({
where: { companyId, status: { not: 'TERMINATED' } },
include: {
department: { select: { name: true } },
},
});
// Department breakdown
const deptMap = {};
const genderMap = {};
const typeMap = {};
const tenureBuckets = { '0-1yr': 0, '1-3yr': 0, '3-5yr': 0, '5yr+': 0 };
const now = new Date();
employees.forEach(e => {
const dept = e.department?.name || 'Unassigned';
deptMap[dept] = (deptMap[dept] || 0) + 1;
const gender = e.gender || 'Not specified';
genderMap[gender] = (genderMap[gender] || 0) + 1;
typeMap[e.employmentType] = (typeMap[e.employmentType] || 0) + 1;
const tenure = (now.getTime() - e.startDate.getTime()) / (1000 * 60 * 60 * 24 * 365);
if (tenure < 1)
tenureBuckets['0-1yr']++;
else if (tenure < 3)
tenureBuckets['1-3yr']++;
else if (tenure < 5)
tenureBuckets['3-5yr']++;
else
tenureBuckets['5yr+']++;
});
return res.json({
departmentBreakdown: Object.entries(deptMap).map(([name, count]) => ({ name, count })),
genderRatio: Object.entries(genderMap).map(([name, count]) => ({ name, count })),
employmentTypes: Object.entries(typeMap).map(([name, count]) => ({ name, count })),
tenureBuckets: Object.entries(tenureBuckets).map(([name, count]) => ({ name, count })),
});
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /analytics/leave
router.get('/leave', auth_1.requireAuth, async (req, res) => {
try {
const companyId = req.user.companyId;
const requests = await prisma_1.default.leaveRequest.findMany({
where: { employee: { companyId }, status: { not: 'REJECTED' } },
include: {
policy: { select: { name: true, type: true } },
employee: { select: { department: { select: { name: true } } } },
},
});
const byType = {};
const byDept = {};
requests.forEach(r => {
const type = r.policy.name;
byType[type] = (byType[type] || 0) + r.days;
const dept = r.employee.department?.name || 'Unassigned';
if (!byDept[dept])
byDept[dept] = { total: 0, approved: 0 };
byDept[dept].total += r.days;
if (r.status === 'APPROVED')
byDept[dept].approved += r.days;
});
return res.json({
byType: Object.entries(byType).map(([name, days]) => ({ name, days })),
byDepartment: Object.entries(byDept).map(([name, data]) => ({ name, ...data })),
});
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /analytics/recruitment
router.get('/recruitment', auth_1.requireAuth, async (req, res) => {
try {
const companyId = req.user.companyId;
const [openJobs, totalCandidates, hiredCandidates] = await Promise.all([
prisma_1.default.job.count({ where: { companyId, status: 'OPEN' } }),
prisma_1.default.candidate.count({
where: { job: { companyId } },
}),
prisma_1.default.candidate.count({
where: { job: { companyId }, currentStage: 'HIRED' },
}),
]);
const stages = await prisma_1.default.candidate.groupBy({
by: ['currentStage'],
where: { job: { companyId } },
_count: true,
});
return res.json({
openJobs,
totalCandidates,
hiredCandidates,
conversionRate: totalCandidates > 0 ? Math.round((hiredCandidates / totalCandidates) * 100) : 0,
pipeline: stages.map(s => ({ stage: s.currentStage, count: s._count })),
});
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=analytics.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=attendance.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"attendance.d.ts","sourceRoot":"","sources":["../../src/routes/attendance.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAkIxB,eAAe,MAAM,CAAC"}
+124
View File
@@ -0,0 +1,124 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /attendance/today
router.get('/today', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.json(null);
const today = new Date();
today.setHours(0, 0, 0, 0);
const record = await prisma_1.default.attendanceRecord.findUnique({
where: { employeeId_date: { employeeId, date: today } },
});
return res.json(record);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /attendance/clock-in
router.post('/clock-in', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.status(400).json({ error: 'Employee not found' });
const today = new Date();
today.setHours(0, 0, 0, 0);
const existing = await prisma_1.default.attendanceRecord.findUnique({
where: { employeeId_date: { employeeId, date: today } },
});
if (existing) {
return res.status(409).json({ error: 'Already clocked in today' });
}
const now = new Date();
const isLate = now.getHours() > 9 || (now.getHours() === 9 && now.getMinutes() > 30);
const record = await prisma_1.default.attendanceRecord.create({
data: {
employeeId,
date: today,
clockIn: now,
status: 'PRESENT',
isLate,
},
});
return res.status(201).json(record);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /attendance/clock-out
router.post('/clock-out', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.status(400).json({ error: 'Employee not found' });
const today = new Date();
today.setHours(0, 0, 0, 0);
const existing = await prisma_1.default.attendanceRecord.findUnique({
where: { employeeId_date: { employeeId, date: today } },
});
if (!existing) {
return res.status(400).json({ error: 'Not clocked in today' });
}
if (existing.clockOut) {
return res.status(409).json({ error: 'Already clocked out' });
}
const now = new Date();
const hoursWorked = existing.clockIn
? (now.getTime() - existing.clockIn.getTime()) / (1000 * 60 * 60)
: 0;
const record = await prisma_1.default.attendanceRecord.update({
where: { id: existing.id },
data: { clockOut: now, hoursWorked: parseFloat(hoursWorked.toFixed(2)) },
});
return res.json(record);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /attendance
router.get('/', auth_1.requireAuth, async (req, res) => {
try {
const { employeeId, startDate, endDate, page = '1', limit = '30' } = req.query;
const skip = (parseInt(page) - 1) * parseInt(limit);
const where = {};
if (req.user.role === 'EMPLOYEE') {
where.employeeId = req.user.employeeId;
}
else if (employeeId) {
where.employeeId = employeeId;
}
if (startDate)
where.date = { ...where.date, gte: new Date(startDate) };
if (endDate)
where.date = { ...where.date, lte: new Date(endDate) };
const [records, total] = await Promise.all([
prisma_1.default.attendanceRecord.findMany({
where,
skip,
take: parseInt(limit),
include: {
employee: { select: { firstName: true, lastName: true, employeeCode: true } },
},
orderBy: { date: 'desc' },
}),
prisma_1.default.attendanceRecord.count({ where }),
]);
return res.json({ data: records, total });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=attendance.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=auth.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAoJxB,eAAe,MAAM,CAAC"}
+145
View File
@@ -0,0 +1,145 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const zod_1 = require("zod");
const prisma_1 = __importDefault(require("../lib/prisma"));
const jwt_1 = require("../lib/jwt");
const auth_1 = require("../middleware/auth");
const validate_1 = require("../middleware/validate");
const router = (0, express_1.Router)();
const loginSchema = zod_1.z.object({
email: zod_1.z.string().email(),
password: zod_1.z.string().min(1),
});
// POST /auth/login
router.post('/login', (0, validate_1.validate)(loginSchema), async (req, res) => {
try {
const { email, password } = req.body;
const user = await prisma_1.default.user.findFirst({
where: { email, isActive: true },
include: { employee: true },
});
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const valid = await bcryptjs_1.default.compare(password, user.passwordHash);
if (!valid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const payload = {
id: user.id,
companyId: user.companyId,
role: user.role,
employeeId: user.employee?.id,
};
const accessToken = (0, jwt_1.signAccessToken)(payload);
const refreshToken = (0, jwt_1.signRefreshToken)(payload);
// Store refresh token
await prisma_1.default.refreshToken.create({
data: {
userId: user.id,
token: refreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
},
});
// Update last login
await prisma_1.default.user.update({
where: { id: user.id },
data: { lastLoginAt: new Date() },
});
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
return res.json({
accessToken,
user: {
id: user.id,
email: user.email,
role: user.role,
companyId: user.companyId,
employeeId: user.employee?.id,
firstName: user.employee?.firstName,
lastName: user.employee?.lastName,
},
});
}
catch (err) {
console.error('Login error:', err);
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /auth/refresh
router.post('/refresh', async (req, res) => {
try {
const token = req.cookies?.refreshToken;
if (!token) {
return res.status(401).json({ error: 'No refresh token' });
}
const stored = await prisma_1.default.refreshToken.findUnique({ where: { token } });
if (!stored || stored.expiresAt < new Date()) {
return res.status(401).json({ error: 'Invalid refresh token' });
}
const payload = (0, jwt_1.verifyRefreshToken)(token);
const accessToken = (0, jwt_1.signAccessToken)({
id: payload.id,
companyId: payload.companyId,
role: payload.role,
employeeId: payload.employeeId,
});
return res.json({ accessToken });
}
catch (err) {
return res.status(401).json({ error: 'Invalid refresh token' });
}
});
// POST /auth/logout
router.post('/logout', async (req, res) => {
const token = req.cookies?.refreshToken;
if (token) {
await prisma_1.default.refreshToken.deleteMany({ where: { token } }).catch(() => { });
}
res.clearCookie('refreshToken');
return res.json({ message: 'Logged out' });
});
// GET /auth/me
router.get('/me', auth_1.requireAuth, async (req, res) => {
try {
const user = await prisma_1.default.user.findUnique({
where: { id: req.user.id },
include: {
employee: {
include: {
department: true,
position: true,
location: true,
manager: { select: { firstName: true, lastName: true } },
},
},
company: true,
},
});
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
return res.json({
id: user.id,
email: user.email,
role: user.role,
companyId: user.companyId,
company: user.company,
employee: user.employee,
});
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=auth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=departments.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"departments.d.ts","sourceRoot":"","sources":["../../src/routes/departments.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA2CxB,eAAe,MAAM,CAAC"}
+51
View File
@@ -0,0 +1,51 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
router.get('/', auth_1.requireAuth, async (req, res) => {
const departments = await prisma_1.default.department.findMany({
where: { companyId: req.user.companyId },
include: { _count: { select: { employees: true } } },
orderBy: { name: 'asc' },
});
return res.json(departments);
});
router.post('/', auth_1.requireAuth, async (req, res) => {
try {
const dept = await prisma_1.default.department.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(dept);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
router.patch('/:id', auth_1.requireAuth, async (req, res) => {
try {
const dept = await prisma_1.default.department.update({
where: { id: req.params.id },
data: req.body,
});
return res.json(dept);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
router.delete('/:id', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.department.delete({ where: { id: req.params.id } });
return res.json({ message: 'Deleted' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=departments.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"departments.js","sourceRoot":"","sources":["../../src/routes/departments.ts"],"names":[],"mappings":";;;;;AAAA,qCAA2C;AAC3C,2DAAmC;AACnC,6CAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACrE,MAAM,WAAW,GAAG,MAAM,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QACnD,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;QACzC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE;QACpD,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;KACzB,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACtE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,gBAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAC1C,IAAI,EAAE,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;SACtD,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC1E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,gBAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAC1C,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE;YAC5B,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC3E,IAAI,CAAC;QACH,MAAM,gBAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACjE,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=employees.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"employees.d.ts","sourceRoot":"","sources":["../../src/routes/employees.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAmPxB,eAAe,MAAM,CAAC"}
+241
View File
@@ -0,0 +1,241 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const zod_1 = require("zod");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const validate_1 = require("../middleware/validate");
const router = (0, express_1.Router)();
const createEmployeeSchema = zod_1.z.object({
firstName: zod_1.z.string().min(1),
lastName: zod_1.z.string().min(1),
workEmail: zod_1.z.string().email(),
departmentId: zod_1.z.string().optional(),
positionId: zod_1.z.string().optional(),
locationId: zod_1.z.string().optional(),
managerId: zod_1.z.string().optional(),
employmentType: zod_1.z.enum(['FULL_TIME', 'PART_TIME', 'CONTRACT', 'INTERN']).default('FULL_TIME'),
salary: zod_1.z.number().min(0).default(0),
phone: zod_1.z.string().optional(),
gender: zod_1.z.string().optional(),
startDate: zod_1.z.string().optional(),
pfApplicable: zod_1.z.boolean().default(true),
esiApplicable: zod_1.z.boolean().default(true),
});
// GET /employees
router.get('/', auth_1.requireAuth, async (req, res) => {
try {
const { search, departmentId, status, page = '1', limit = '20' } = req.query;
const skip = (parseInt(page) - 1) * parseInt(limit);
const where = {
companyId: req.user.companyId,
};
if (search) {
where.OR = [
{ firstName: { contains: search, mode: 'insensitive' } },
{ lastName: { contains: search, mode: 'insensitive' } },
{ workEmail: { contains: search, mode: 'insensitive' } },
{ employeeCode: { contains: search, mode: 'insensitive' } },
];
}
if (departmentId)
where.departmentId = departmentId;
if (status)
where.status = status;
const [employees, total] = await Promise.all([
prisma_1.default.employee.findMany({
where,
skip,
take: parseInt(limit),
include: {
department: { select: { name: true } },
position: { select: { name: true } },
location: { select: { name: true } },
manager: { select: { firstName: true, lastName: true } },
},
orderBy: { createdAt: 'desc' },
}),
prisma_1.default.employee.count({ where }),
]);
return res.json({
data: employees,
total,
page: parseInt(page),
limit: parseInt(limit),
totalPages: Math.ceil(total / parseInt(limit)),
});
}
catch (err) {
console.error(err);
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /employees/org-chart
router.get('/org-chart', auth_1.requireAuth, async (req, res) => {
try {
const employees = await prisma_1.default.employee.findMany({
where: { companyId: req.user.companyId, status: { not: 'TERMINATED' } },
select: {
id: true,
firstName: true,
lastName: true,
managerId: true,
position: { select: { name: true } },
department: { select: { name: true } },
},
});
const buildTree = (employees, parentId = null) => {
return employees
.filter(e => e.managerId === parentId)
.map(e => ({
...e,
children: buildTree(employees, e.id),
}));
};
const tree = buildTree(employees);
return res.json(tree);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /employees/:id
router.get('/:id', auth_1.requireAuth, async (req, res) => {
try {
const employee = await prisma_1.default.employee.findFirst({
where: { id: req.params.id, companyId: req.user.companyId },
include: {
department: true,
position: true,
location: true,
manager: { select: { id: true, firstName: true, lastName: true } },
reports: { select: { id: true, firstName: true, lastName: true, position: { select: { name: true } } } },
leaveBalances: { include: { policy: true } },
},
});
if (!employee) {
return res.status(404).json({ error: 'Employee not found' });
}
return res.json(employee);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /employees
router.post('/', auth_1.requireAuth, (0, validate_1.validate)(createEmployeeSchema), async (req, res) => {
try {
const companyId = req.user.companyId;
const data = req.body;
// Generate employee code
const count = await prisma_1.default.employee.count({ where: { companyId } });
const employeeCode = `EMP${String(count + 1).padStart(3, '0')}`;
// Create user account
const tempPassword = Math.random().toString(36).slice(-8);
const passwordHash = await bcryptjs_1.default.hash(tempPassword, 12);
const user = await prisma_1.default.user.create({
data: {
companyId,
email: data.workEmail,
passwordHash,
role: 'EMPLOYEE',
},
});
const employee = await prisma_1.default.employee.create({
data: {
userId: user.id,
companyId,
employeeCode,
firstName: data.firstName,
lastName: data.lastName,
workEmail: data.workEmail,
departmentId: data.departmentId || null,
positionId: data.positionId || null,
locationId: data.locationId || null,
managerId: data.managerId || null,
employmentType: data.employmentType,
salary: data.salary,
phone: data.phone || null,
gender: data.gender || null,
startDate: data.startDate ? new Date(data.startDate) : new Date(),
pfApplicable: data.pfApplicable,
esiApplicable: data.esiApplicable,
},
include: {
department: true,
position: true,
location: true,
},
});
// Initialize leave balances for current year
const policies = await prisma_1.default.leavePolicy.findMany({ where: { companyId, isActive: true } });
const year = new Date().getFullYear();
if (policies.length > 0) {
await prisma_1.default.leaveBalance.createMany({
data: policies.map(p => ({
employeeId: employee.id,
policyId: p.id,
year,
allocated: p.accrualDays * 12,
used: 0,
balance: p.accrualDays * 12,
})),
skipDuplicates: true,
});
}
return res.status(201).json({ ...employee, tempPassword });
}
catch (err) {
if (err.code === 'P2002') {
return res.status(409).json({ error: 'Email already exists' });
}
console.error(err);
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /employees/:id
router.patch('/:id', auth_1.requireAuth, async (req, res) => {
try {
const { id } = req.params;
const existing = await prisma_1.default.employee.findFirst({
where: { id, companyId: req.user.companyId },
});
if (!existing)
return res.status(404).json({ error: 'Employee not found' });
const employee = await prisma_1.default.employee.update({
where: { id },
data: {
...req.body,
updatedAt: new Date(),
},
include: {
department: true,
position: true,
location: true,
},
});
return res.json(employee);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// DELETE /employees/:id (soft delete)
router.delete('/:id', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.employee.update({
where: { id: req.params.id },
data: { status: 'TERMINATED' },
});
return res.json({ message: 'Employee terminated' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=employees.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=engagement.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"engagement.d.ts","sourceRoot":"","sources":["../../src/routes/engagement.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA+FxB,eAAe,MAAM,CAAC"}
+104
View File
@@ -0,0 +1,104 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /engagement/announcements
router.get('/announcements', auth_1.requireAuth, async (req, res) => {
try {
const announcements = await prisma_1.default.announcement.findMany({
where: { companyId: req.user.companyId },
orderBy: [{ isPinned: 'desc' }, { publishedAt: 'desc' }],
take: 20,
});
return res.json(announcements);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /engagement/announcements
router.post('/announcements', auth_1.requireAuth, async (req, res) => {
try {
const announcement = await prisma_1.default.announcement.create({
data: {
...req.body,
companyId: req.user.companyId,
authorId: req.user.employeeId,
},
});
return res.status(201).json(announcement);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /engagement/recognition
router.get('/recognition', auth_1.requireAuth, async (req, res) => {
try {
const recognitions = await prisma_1.default.recognition.findMany({
where: { isPublic: true },
include: {
giver: { select: { firstName: true, lastName: true, department: { select: { name: true } } } },
receiver: { select: { firstName: true, lastName: true } },
},
orderBy: { createdAt: 'desc' },
take: 20,
});
return res.json(recognitions);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /engagement/recognition
router.post('/recognition', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.status(400).json({ error: 'Employee not found' });
const recognition = await prisma_1.default.recognition.create({
data: {
...req.body,
giverId: employeeId,
companyId: req.user.companyId,
},
});
return res.status(201).json(recognition);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /engagement/surveys
router.get('/surveys', auth_1.requireAuth, async (req, res) => {
try {
const surveys = await prisma_1.default.survey.findMany({
where: { companyId: req.user.companyId },
include: { _count: { select: { responses: true } } },
orderBy: { createdAt: 'desc' },
});
return res.json(surveys);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /engagement/surveys
router.post('/surveys', auth_1.requireAuth, async (req, res) => {
try {
const survey = await prisma_1.default.survey.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(survey);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=engagement.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"engagement.js","sourceRoot":"","sources":["../../src/routes/engagement.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=leave.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"leave.d.ts","sourceRoot":"","sources":["../../src/routes/leave.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAyNxB,eAAe,MAAM,CAAC"}
+211
View File
@@ -0,0 +1,211 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /leave/policies
router.get('/policies', auth_1.requireAuth, async (req, res) => {
const policies = await prisma_1.default.leavePolicy.findMany({
where: { companyId: req.user.companyId },
});
return res.json(policies);
});
// POST /leave/policies
router.post('/policies', auth_1.requireAuth, async (req, res) => {
try {
const policy = await prisma_1.default.leavePolicy.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(policy);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /leave/balances
router.get('/balances', auth_1.requireAuth, async (req, res) => {
try {
const { employeeId } = req.query;
const empId = employeeId || req.user.employeeId;
if (!empId)
return res.status(400).json({ error: 'Employee not found' });
const year = new Date().getFullYear();
const balances = await prisma_1.default.leaveBalance.findMany({
where: { employeeId: empId, year },
include: { policy: true },
});
return res.json(balances);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /leave/requests
router.get('/requests', auth_1.requireAuth, async (req, res) => {
try {
const { status, employeeId, page = '1', limit = '20' } = req.query;
const skip = (parseInt(page) - 1) * parseInt(limit);
const where = {};
if (req.user.role === 'EMPLOYEE') {
where.employeeId = req.user.employeeId;
}
else if (req.user.role === 'MANAGER') {
// Get team members
const reports = await prisma_1.default.employee.findMany({
where: { managerId: req.user.employeeId },
select: { id: true },
});
const ids = [req.user.employeeId, ...reports.map(r => r.id)];
where.employeeId = { in: ids };
}
if (status)
where.status = status;
if (employeeId && req.user.role !== 'EMPLOYEE')
where.employeeId = employeeId;
const [requests, total] = await Promise.all([
prisma_1.default.leaveRequest.findMany({
where,
skip,
take: parseInt(limit),
include: {
employee: { select: { firstName: true, lastName: true, employeeCode: true } },
policy: { select: { name: true, type: true } },
},
orderBy: { createdAt: 'desc' },
}),
prisma_1.default.leaveRequest.count({ where }),
]);
return res.json({ data: requests, total, page: parseInt(page), totalPages: Math.ceil(total / parseInt(limit)) });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /leave/requests
router.post('/requests', auth_1.requireAuth, async (req, res) => {
try {
const { policyId, startDate, endDate, reason } = req.body;
const employeeId = req.user.employeeId;
if (!employeeId)
return res.status(400).json({ error: 'Employee not found' });
const start = new Date(startDate);
const end = new Date(endDate);
const days = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)) + 1;
// Check balance
const year = start.getFullYear();
const balance = await prisma_1.default.leaveBalance.findUnique({
where: { employeeId_policyId_year: { employeeId, policyId, year } },
});
if (!balance || balance.balance < days) {
return res.status(400).json({ error: 'Insufficient leave balance' });
}
// Check overlap
const overlap = await prisma_1.default.leaveRequest.findFirst({
where: {
employeeId,
status: { in: ['PENDING', 'APPROVED'] },
OR: [
{ startDate: { lte: end }, endDate: { gte: start } },
],
},
});
if (overlap) {
return res.status(409).json({ error: 'Leave request overlaps with existing request' });
}
const request = await prisma_1.default.leaveRequest.create({
data: { employeeId, policyId, startDate: start, endDate: end, days, reason, status: 'PENDING' },
include: { policy: true, employee: { select: { firstName: true, lastName: true } } },
});
return res.status(201).json(request);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /leave/requests/:id/approve
router.patch('/requests/:id/approve', auth_1.requireAuth, async (req, res) => {
try {
const request = await prisma_1.default.leaveRequest.findUnique({ where: { id: req.params.id } });
if (!request)
return res.status(404).json({ error: 'Request not found' });
if (request.status !== 'PENDING')
return res.status(400).json({ error: 'Request already processed' });
await prisma_1.default.leaveRequest.update({
where: { id: req.params.id },
data: {
status: 'APPROVED',
approverId: req.user.employeeId,
approvedAt: new Date(),
approverNote: req.body.note,
},
});
// Deduct from balance
const year = request.startDate.getFullYear();
await prisma_1.default.leaveBalance.update({
where: {
employeeId_policyId_year: {
employeeId: request.employeeId,
policyId: request.policyId,
year,
},
},
data: {
used: { increment: request.days },
balance: { decrement: request.days },
},
});
return res.json({ message: 'Approved' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /leave/requests/:id/reject
router.patch('/requests/:id/reject', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.leaveRequest.update({
where: { id: req.params.id },
data: {
status: 'REJECTED',
approverId: req.user.employeeId,
approverNote: req.body.note,
},
});
return res.json({ message: 'Rejected' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /leave/calendar
router.get('/calendar', auth_1.requireAuth, async (req, res) => {
try {
const { month, year } = req.query;
const y = parseInt(year) || new Date().getFullYear();
const m = parseInt(month) || new Date().getMonth() + 1;
const startOfMonth = new Date(y, m - 1, 1);
const endOfMonth = new Date(y, m, 0);
const requests = await prisma_1.default.leaveRequest.findMany({
where: {
status: 'APPROVED',
employee: { companyId: req.user.companyId },
startDate: { lte: endOfMonth },
endDate: { gte: startOfMonth },
},
include: {
employee: { select: { firstName: true, lastName: true } },
policy: { select: { name: true } },
},
});
return res.json(requests);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=leave.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=locations.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../../src/routes/locations.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAuCxB,eAAe,MAAM,CAAC"}
+47
View File
@@ -0,0 +1,47 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
router.get('/', auth_1.requireAuth, async (req, res) => {
const locations = await prisma_1.default.location.findMany({
where: { companyId: req.user.companyId },
orderBy: { name: 'asc' },
});
return res.json(locations);
});
router.post('/', auth_1.requireAuth, async (req, res) => {
try {
const loc = await prisma_1.default.location.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(loc);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
router.patch('/:id', auth_1.requireAuth, async (req, res) => {
try {
const loc = await prisma_1.default.location.update({ where: { id: req.params.id }, data: req.body });
return res.json(loc);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
router.delete('/:id', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.location.delete({ where: { id: req.params.id } });
return res.json({ message: 'Deleted' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=locations.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"locations.js","sourceRoot":"","sources":["../../src/routes/locations.ts"],"names":[],"mappings":";;;;;AAAA,qCAA2C;AAC3C,2DAAmC;AACnC,6CAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACrE,MAAM,SAAS,GAAG,MAAM,gBAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC/C,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;QACzC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;KACzB,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvC,IAAI,EAAE,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;SACtD,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC1E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3F,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC3E,IAAI,CAAC;QACH,MAAM,gBAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=notifications.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/routes/notifications.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAuCxB,eAAe,MAAM,CAAC"}
+48
View File
@@ -0,0 +1,48 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /notifications
router.get('/', auth_1.requireAuth, async (req, res) => {
try {
const notifications = await prisma_1.default.notification.findMany({
where: { userId: req.user.id },
orderBy: [{ isRead: 'asc' }, { createdAt: 'desc' }],
take: 50,
});
return res.json(notifications);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /notifications/:id/read
router.patch('/:id/read', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.notification.update({ where: { id: req.params.id }, data: { isRead: true } });
return res.json({ message: 'Marked as read' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /notifications/read-all
router.post('/read-all', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.notification.updateMany({
where: { userId: req.user.id, isRead: false },
data: { isRead: true },
});
return res.json({ message: 'All marked as read' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=notifications.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/routes/notifications.ts"],"names":[],"mappings":";;;;;AAAA,qCAA2C;AAC3C,2DAAmC;AACnC,6CAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,qBAAqB;AACrB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACrE,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,MAAM,gBAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;YACvD,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE;YAC/B,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;YACnD,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,gCAAgC;AAChC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC/E,IAAI,CAAC;QACH,MAAM,gBAAM,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC3F,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,+BAA+B;AAC/B,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,gBAAM,CAAC,YAAY,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;YAC9C,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;SACvB,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=payroll.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"payroll.d.ts","sourceRoot":"","sources":["../../src/routes/payroll.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAoKxB,eAAe,MAAM,CAAC"}
+160
View File
@@ -0,0 +1,160 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
function calculateIndiaPayroll(grossSalary, pfApplicable, esiApplicable) {
const basic = grossSalary * 0.40;
const hra = grossSalary * 0.20;
const transport = 1600;
const pfBasic = Math.min(basic, 15000);
const pfEmployee = pfApplicable ? pfBasic * 0.12 : 0;
const pfEmployer = pfApplicable ? pfBasic * 0.12 : 0;
const esiEmployee = (esiApplicable && grossSalary <= 21000) ? grossSalary * 0.0075 : 0;
const esiEmployer = (esiApplicable && grossSalary <= 21000) ? grossSalary * 0.0325 : 0;
const professionalTax = grossSalary > 15000 ? 200 : 0;
const annualGross = grossSalary * 12;
const tds = annualGross > 750000 ? (annualGross - 750000) * 0.20 / 12 : 0;
const totalDeductions = pfEmployee + esiEmployee + tds + professionalTax;
const netPay = grossSalary - totalDeductions;
return {
grossPay: grossSalary,
netPay: parseFloat(netPay.toFixed(2)),
basicSalary: parseFloat(basic.toFixed(2)),
hra: parseFloat(hra.toFixed(2)),
transport,
pfEmployee: parseFloat(pfEmployee.toFixed(2)),
pfEmployer: parseFloat(pfEmployer.toFixed(2)),
esiEmployee: parseFloat(esiEmployee.toFixed(2)),
esiEmployer: parseFloat(esiEmployer.toFixed(2)),
tds: parseFloat(tds.toFixed(2)),
professionalTax,
totalDeductions: parseFloat(totalDeductions.toFixed(2)),
};
}
// GET /payroll/runs
router.get('/runs', auth_1.requireAuth, async (req, res) => {
try {
const runs = await prisma_1.default.payrollRun.findMany({
where: { companyId: req.user.companyId },
include: { _count: { select: { payslips: true } } },
orderBy: { createdAt: 'desc' },
});
return res.json(runs);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /payroll/runs
router.post('/runs', auth_1.requireAuth, async (req, res) => {
try {
const { name, periodStart, periodEnd } = req.body;
const run = await prisma_1.default.payrollRun.create({
data: {
companyId: req.user.companyId,
name,
periodStart: new Date(periodStart),
periodEnd: new Date(periodEnd),
status: 'DRAFT',
},
});
return res.status(201).json(run);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /payroll/runs/:id/process
router.post('/runs/:id/process', auth_1.requireAuth, async (req, res) => {
try {
const run = await prisma_1.default.payrollRun.findUnique({ where: { id: req.params.id } });
if (!run)
return res.status(404).json({ error: 'Payroll run not found' });
const employees = await prisma_1.default.employee.findMany({
where: {
companyId: req.user.companyId,
status: { in: ['ACTIVE', 'PROBATION'] },
},
});
// Delete existing payslips for this run
await prisma_1.default.payslip.deleteMany({ where: { payrollRunId: run.id } });
let totalGross = 0;
let totalNet = 0;
let totalDeductions = 0;
const payslipsData = employees.map(emp => {
const calc = calculateIndiaPayroll(emp.salary, emp.pfApplicable, emp.esiApplicable);
totalGross += calc.grossPay;
totalNet += calc.netPay;
totalDeductions += calc.totalDeductions;
return {
payrollRunId: run.id,
employeeId: emp.id,
...calc,
};
});
await prisma_1.default.payslip.createMany({ data: payslipsData });
await prisma_1.default.payrollRun.update({
where: { id: run.id },
data: {
status: 'PROCESSED',
totalGross: parseFloat(totalGross.toFixed(2)),
totalNet: parseFloat(totalNet.toFixed(2)),
totalDeductions: parseFloat(totalDeductions.toFixed(2)),
},
});
return res.json({ message: `Processed ${employees.length} payslips`, totalGross, totalNet });
}
catch (err) {
console.error(err);
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /payroll/runs/:id
router.get('/runs/:id', auth_1.requireAuth, async (req, res) => {
try {
const run = await prisma_1.default.payrollRun.findUnique({
where: { id: req.params.id },
include: {
payslips: {
include: {
employee: {
select: { firstName: true, lastName: true, employeeCode: true, department: { select: { name: true } } },
},
},
},
},
});
if (!run)
return res.status(404).json({ error: 'Not found' });
return res.json(run);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /payroll/payslips (employee view)
router.get('/payslips', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.json([]);
const payslips = await prisma_1.default.payslip.findMany({
where: { employeeId },
include: {
payrollRun: { select: { name: true, periodStart: true, periodEnd: true, status: true } },
},
orderBy: { generatedAt: 'desc' },
});
return res.json(payslips);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=payroll.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=performance.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/routes/performance.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAmHxB,eAAe,MAAM,CAAC"}
+124
View File
@@ -0,0 +1,124 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /performance/cycles
router.get('/cycles', auth_1.requireAuth, async (req, res) => {
try {
const cycles = await prisma_1.default.reviewCycle.findMany({
where: { companyId: req.user.companyId },
include: { _count: { select: { reviews: true } } },
orderBy: { createdAt: 'desc' },
});
return res.json(cycles);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /performance/cycles
router.post('/cycles', auth_1.requireAuth, async (req, res) => {
try {
const cycle = await prisma_1.default.reviewCycle.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(cycle);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /performance/cycles/:id/reviews
router.get('/cycles/:id/reviews', auth_1.requireAuth, async (req, res) => {
try {
const reviews = await prisma_1.default.performanceReview.findMany({
where: { cycleId: req.params.id },
include: {
reviewee: { select: { firstName: true, lastName: true } },
reviewer: { select: { firstName: true, lastName: true } },
},
});
return res.json(reviews);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /performance/reviews/:id
router.patch('/reviews/:id', auth_1.requireAuth, async (req, res) => {
try {
const review = await prisma_1.default.performanceReview.update({
where: { id: req.params.id },
data: { ...req.body, submittedAt: new Date(), status: 'SUBMITTED' },
});
return res.json(review);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /performance/goals
router.get('/goals', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.json([]);
const goals = await prisma_1.default.goal.findMany({
where: { employeeId },
orderBy: { createdAt: 'desc' },
});
return res.json(goals);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /performance/goals
router.post('/goals', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.status(400).json({ error: 'Employee not found' });
const goal = await prisma_1.default.goal.create({
data: { ...req.body, employeeId },
});
return res.status(201).json(goal);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /performance/goals/:id
router.patch('/goals/:id', auth_1.requireAuth, async (req, res) => {
try {
const goal = await prisma_1.default.goal.update({ where: { id: req.params.id }, data: req.body });
return res.json(goal);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /performance/feedback
router.get('/feedback', auth_1.requireAuth, async (req, res) => {
try {
const employeeId = req.user.employeeId;
if (!employeeId)
return res.json([]);
const feedback = await prisma_1.default.feedback.findMany({
where: { receiverId: employeeId },
include: { giver: { select: { firstName: true, lastName: true } } },
orderBy: { createdAt: 'desc' },
});
return res.json(feedback);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=performance.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"performance.js","sourceRoot":"","sources":["../../src/routes/performance.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=positions.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"positions.d.ts","sourceRoot":"","sources":["../../src/routes/positions.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAuCxB,eAAe,MAAM,CAAC"}
+47
View File
@@ -0,0 +1,47 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
router.get('/', auth_1.requireAuth, async (req, res) => {
const positions = await prisma_1.default.position.findMany({
where: { companyId: req.user.companyId },
orderBy: { name: 'asc' },
});
return res.json(positions);
});
router.post('/', auth_1.requireAuth, async (req, res) => {
try {
const pos = await prisma_1.default.position.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(pos);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
router.patch('/:id', auth_1.requireAuth, async (req, res) => {
try {
const pos = await prisma_1.default.position.update({ where: { id: req.params.id }, data: req.body });
return res.json(pos);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
router.delete('/:id', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.position.delete({ where: { id: req.params.id } });
return res.json({ message: 'Deleted' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=positions.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"positions.js","sourceRoot":"","sources":["../../src/routes/positions.ts"],"names":[],"mappings":";;;;;AAAA,qCAA2C;AAC3C,2DAAmC;AACnC,6CAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACrE,MAAM,SAAS,GAAG,MAAM,gBAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC/C,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;QACzC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;KACzB,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvC,IAAI,EAAE,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;SACtD,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC1E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3F,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC3E,IAAI,CAAC;QACH,MAAM,gBAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=recruitment.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"recruitment.d.ts","sourceRoot":"","sources":["../../src/routes/recruitment.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAiGxB,eAAe,MAAM,CAAC"}
+106
View File
@@ -0,0 +1,106 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /recruitment/jobs
router.get('/jobs', auth_1.requireAuth, async (req, res) => {
try {
const { status } = req.query;
const where = { companyId: req.user.companyId };
if (status)
where.status = status;
const jobs = await prisma_1.default.job.findMany({
where,
include: {
department: { select: { name: true } },
location: { select: { name: true } },
_count: { select: { candidates: true } },
},
orderBy: { createdAt: 'desc' },
});
return res.json(jobs);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /recruitment/jobs
router.post('/jobs', auth_1.requireAuth, async (req, res) => {
try {
const job = await prisma_1.default.job.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(job);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /recruitment/jobs/:id
router.patch('/jobs/:id', auth_1.requireAuth, async (req, res) => {
try {
const job = await prisma_1.default.job.update({ where: { id: req.params.id }, data: req.body });
return res.json(job);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /recruitment/jobs/:id/candidates
router.get('/jobs/:id/candidates', auth_1.requireAuth, async (req, res) => {
try {
const candidates = await prisma_1.default.candidate.findMany({
where: { jobId: req.params.id },
include: { interviews: true },
orderBy: { appliedAt: 'desc' },
});
return res.json(candidates);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /recruitment/jobs/:id/candidates
router.post('/jobs/:id/candidates', auth_1.requireAuth, async (req, res) => {
try {
const candidate = await prisma_1.default.candidate.create({
data: { ...req.body, jobId: req.params.id, currentStage: 'APPLIED' },
});
return res.status(201).json(candidate);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /recruitment/candidates/:id/stage
router.patch('/candidates/:id/stage', auth_1.requireAuth, async (req, res) => {
try {
const candidate = await prisma_1.default.candidate.update({
where: { id: req.params.id },
data: { currentStage: req.body.stage, notes: req.body.notes },
});
return res.json(candidate);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /recruitment/candidates/:id/interviews
router.post('/candidates/:id/interviews', auth_1.requireAuth, async (req, res) => {
try {
const interview = await prisma_1.default.interviewRound.create({
data: { ...req.body, candidateId: req.params.id },
});
return res.status(201).json(interview);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=recruitment.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"recruitment.js","sourceRoot":"","sources":["../../src/routes/recruitment.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=settings.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/routes/settings.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA2BxB,eAAe,MAAM,CAAC"}
+36
View File
@@ -0,0 +1,36 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /settings/company
router.get('/company', auth_1.requireAuth, async (req, res) => {
try {
const company = await prisma_1.default.company.findUnique({
where: { id: req.user.companyId },
});
return res.json(company);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /settings/company
router.patch('/company', auth_1.requireAuth, async (req, res) => {
try {
const company = await prisma_1.default.company.update({
where: { id: req.user.companyId },
data: req.body,
});
return res.json(company);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=settings.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/routes/settings.ts"],"names":[],"mappings":";;;;;AAAA,qCAA2C;AAC3C,2DAAmC;AACnC,6CAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,wBAAwB;AACxB,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC5E,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,gBAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YAC9C,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;SACnC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,0BAA0B;AAC1B,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,kBAAW,EAAE,KAAK,EAAE,GAAgB,EAAE,GAAa,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,gBAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAC1C,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,IAAK,CAAC,SAAS,EAAE;YAClC,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../acorn/bin/acorn
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../mime/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../prisma/build/index.js
+1
View File
@@ -0,0 +1 @@
../@grpc/proto-loader/build/bin/proto-loader-gen-types.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../resolve/bin/resolve
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../semver/bin/semver.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../ts-node/dist/bin.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../ts-node/dist/bin-cwd.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../ts-node/dist/bin-esm.js
+1
View File
@@ -0,0 +1 @@
../ts-node/dist/bin-script.js
+1
View File
@@ -0,0 +1 @@
../ts-node/dist/bin-transpile.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../ts-node/dist/bin-script-deprecated.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../typescript/bin/tsc
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../typescript/bin/tsserver
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../uuid/dist/bin/uuid
+4222
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
export * from "./index"
+1
View File
@@ -0,0 +1 @@
module.exports = { ...require('.') }

Some files were not shown because too many files have changed in this diff Show More