Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions check_reference_id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { Prisma } = require('@prisma/client');
console.log("Notification fields in Prisma client:");
// This might not work as easily in JS depending on how Prisma exports types
// But we can check the runtime metadata if available
// Or just try to create one
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function test() {
try {
console.log("Attempting to create a notification with referenceId...");
// We'll use a non-existent userId to fail eventually, but check the validation error
await prisma.notification.create({
data: {
userId: '00000000-0000-0000-0000-000000000000',
message: 'Test',
referenceId: 'test'
}
});
} catch (error) {
console.log("Error message:", error.message);
if (error.message.includes("Unknown argument 'referenceId'")) {
console.log("FAILURE: referenceId is NOT in the client");
} else if (error.message.includes("Foreign key constraint fails")) {
console.log("SUCCESS: referenceId is recognized (foreign key failed as expected)");
} else {
console.log("Other error:", error);
}
} finally {
await prisma.$disconnect();
}
}

test();
8 changes: 5 additions & 3 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,10 @@ model Notification {
userId String @db.Uuid
user User @relation(fields: [userId], references: [userId])

message String @db.Text
type String? @db.VarChar(50) // 'daily_update', 'payment', etc.
isRead Boolean @default(false)
message String @db.Text
type String? @db.VarChar(50) // 'daily_update', 'payment', etc.
referenceId String? @db.VarChar(255) @map("reference_id")
isRead Boolean @default(false)

createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
Expand Down Expand Up @@ -439,6 +440,7 @@ model Payment {
paymentMethod PaymentMethod @default(cash)
paymentBreakup Json? @default("[]") @map("payment_breakup")
paymentDate String @db.VarChar(10)
receiptNumber String? @map("receipt_number") @db.VarChar(50)
remarks String? @db.Text

// File upload fields
Expand Down
8 changes: 7 additions & 1 deletion src/config/prisma.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import { config } from "./env";

const connectionString = config.DATABASE_PUBLIC_URL || config.DATABASE_URL;

const pool = new Pool({ connectionString });
const pool = new Pool({
connectionString,
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000,
ssl: connectionString.includes('localhost') ? false : { rejectUnauthorized: false }
});
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });

Expand Down
15 changes: 13 additions & 2 deletions src/email/emailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,22 @@ export async function sendEmail({ to, subject, html, event }: SendEmailOptions):
let status = 'sent';
let errorMessage: string | undefined;

// DEVELOPMENT OVERRIDE:
// If in development, force send to the verified testing email address
// This bypasses the "Can only send testing emails to your own email address" restriction.
const actualRecipient = process.env.NODE_ENV === 'development'
? 'jahnaviveeranala08@gmail.com'
: to;

if (process.env.NODE_ENV === 'development' && actualRecipient !== to) {
console.log(`[Email] 👨‍💻 Dev Mode: Redirecting email from ${to} to verified tester ${actualRecipient}`);
}

try {
const { data, error } = await resend.emails.send({
from: process.env.EMAIL_FROM || 'onboarding@resend.dev',
to,
subject,
to: actualRecipient,
subject: `[DEV to: ${to}] ${subject}`, // Keep original destination in subject for tracking
html,
});

Expand Down
64 changes: 64 additions & 0 deletions src/modules/daily-updates/daily-updates.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1413,3 +1413,67 @@ export const addFeedback = async (req: RequestWithUser, res: Response) => {
});
}
};

/**
* Mark a construction stage as complete for a project (Supervisor only)
*/
export const markStageComplete = async (req: RequestWithUser, res: Response) => {
try {
const { projectId, stage } = req.body;

if (!req.user || req.user.role !== 'supervisor') {
return res.status(401).json({ success: false, message: "Unauthorized: Supervisor access required" });
}

if (!projectId || !stage) {
return res.status(400).json({ success: false, message: "Project ID and Stage Name are required" });
}

const supervisor = await supervisorService.getSupervisorByUserId(req.user.userId);
const supervisorId = supervisor.supervisorId;

const result = await DailyUpdatesServices.markStageComplete(projectId, stage, supervisorId);

return res.status(200).json({
success: true,
message: result.message,
data: result
});
} catch (error) {
return res.status(400).json({
success: false,
message: error instanceof Error ? error.message : String(error),
});
}
};

/**
* Approve a construction stage for a project (Customer only)
*/
export const approveStage = async (req: RequestWithUser, res: Response) => {
try {
const { projectId, stage } = req.body;

if (!req.user || req.user.role !== 'customer') {
return res.status(401).json({ success: false, message: "Unauthorized: Customer access required" });
}

if (!projectId || !stage) {
return res.status(400).json({ success: false, message: "Project ID and Stage Name are required" });
}

const userId = req.user.userId;
const result = await DailyUpdatesServices.approveStage(projectId, stage, userId);

return res.status(200).json({
success: true,
message: result.message,
data: result
});
} catch (error) {
return res.status(400).json({
success: false,
message: error instanceof Error ? error.message : String(error),
});
}
};
6 changes: 6 additions & 0 deletions src/modules/daily-updates/daily-updates.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ router.put("/:dailyUpdateId/reject", authenticate, authorizeRoles("customer"), D
// Add feedback to daily update (Customer only)
router.post("/:dailyUpdateId/feedback", authenticate, authorizeRoles("customer"), DailyUpdatesController.addFeedback);

// Mark stage as complete (Supervisor)
router.post("/stage/mark-complete", authenticate, authorizeRoles("supervisor"), DailyUpdatesController.markStageComplete);

// Approve stage (Customer)
router.post("/stage/approve", authenticate, authorizeRoles("customer"), DailyUpdatesController.approveStage);

// Get daily update by ID (General access, maybe restricted later?)
router.get("/:dailyUpdateId", authenticate, authorizeRoles("admin", "supervisor", "customer"), DailyUpdatesController.getDailyUpdateById);

Expand Down
Loading