Skip to content

deploy: final fix#98

Merged
SaranHiruthikM merged 1 commit into
mainfrom
chore/final
Mar 10, 2026
Merged

deploy: final fix#98
SaranHiruthikM merged 1 commit into
mainfrom
chore/final

Conversation

@SaranHiruthikM
Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings March 10, 2026 21:29
@SaranHiruthikM SaranHiruthikM merged commit 028eef7 into main Mar 10, 2026
3 of 5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR appears aimed at finalizing deployment readiness by adding CI/CD automation, improving multi-language UX (including dynamic translation of user-generated/dynamic strings), and introducing Twilio-based SMS notifications across key backend flows (auth, negotiations, orders, advisories).

Changes:

  • Add dynamic translation utilities/components and expand i18n resources; propagate language preference into demand/recommendation requests.
  • Add Twilio SMS notification service and wire it into auth/login, negotiations, orders, and advisories; add advisory update route.
  • Introduce a GitHub Actions CI/CD workflow for backend/frontend build checks and Render deploy hooks.

Reviewed changes

Copilot reviewed 27 out of 28 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
frontend/src/services/translation.service.js New client-side translation service with cache + API fallbacks
frontend/src/hooks/useDynamicTranslation.js Hook to translate dynamic text using TranslationService
frontend/src/components/common/DynamicText.jsx Component wrapper to render translated dynamic strings (static-key-first, then API)
frontend/src/i18n/i18n.js i18n initialization adjusted to respect saved language; adds many new translation keys
frontend/src/App.jsx Prevents refresh from resetting language by prioritizing localStorage language
frontend/src/services/recommendation.service.js Sends language param to backend demand/recommendation endpoints
frontend/src/services/auth.service.js Stores token/user in localStorage on register/login when returned by backend
frontend/src/pages/Login.jsx Navigates directly to dashboard after login (OTP flow removed)
frontend/src/pages/Register.jsx Navigates directly to dashboard after register (OTP flow removed)
frontend/src/pages/Dashboard.jsx Replaces some hardcoded strings with i18n keys / DynamicText rendering
frontend/src/pages/orders/OrderHistory.jsx i18n keys + DynamicText for crop name/status in order history
frontend/src/pages/Marketplace.jsx Replaces marketplace headings/labels/placeholders with i18n keys
frontend/src/components/marketplace/CropCard.jsx Uses i18n keys and DynamicText for crop names/details
frontend/src/components/schemes/AiCropDoctor.jsx Minor layout tweak (h-full -> h-fit)
backend/src/services/notificationService.ts New Twilio SMS service + message helpers (welcome/login/order/advisory/etc.)
backend/src/controllers/authController.ts Registration/login now return JWT directly and send SMS alerts (OTP bypassed)
backend/src/controllers/orderController.ts Sends SMS on order creation/accept/status update
backend/src/controllers/instantBuyController.ts Sends SMS to farmer for instant-buy orders
backend/src/controllers/negotiationController.ts Sends SMS alerts on negotiation start/respond actions
backend/src/controllers/advisoryController.ts Sends SMS alerts to targeted farmers on advisory create/update
backend/src/routes/advisoryRoutes.ts Adds PATCH /advisory/:id route for updates
backend/src/controllers/demandController.ts Accepts language query param and passes into demand services
backend/src/services/demandService.ts Adds language-aware prompting; adjusts Groq handling for demand analysis
backend/src/controllers/prices.controller.ts Uses default query to route forecast analysis through LLM path
backend/test_sms.ts Adds standalone Twilio/SMS debug script
backend/package.json Adds Twilio dependency and types
backend/package-lock.json Lockfile updated for Twilio + transitive deps
.github/workflows/ci-cd.yml Adds GitHub Actions workflow for backend/frontend CI and Render deploy trigger
Files not reviewed (1)
  • backend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const jsonResponse = JSON.parse(cleanContent);

if (jsonResponse.explanation) explanation = jsonResponse.explanation;
if (jsonResponse.sellRecommendation) sellRecommendation = jsonResponse.sellRecommendation;
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sellRecommendation is replaced wholesale with the LLM response. Since the prompt no longer requests a trend field, this will typically drop the existing heuristic trend value — but the frontend reads sellRecommendation.trend (e.g., in DemandForecast). Either include trend in the required JSON schema again, or merge the AI fields into the existing object while preserving trend.

Suggested change
if (jsonResponse.sellRecommendation) sellRecommendation = jsonResponse.sellRecommendation;
if (jsonResponse.sellRecommendation) {
sellRecommendation = {
...sellRecommendation,
...jsonResponse.sellRecommendation,
};
}

Copilot uses AI. Check for mistakes.
Comment on lines 5 to 9
getDemandForecast: async (cropId, location = 'Coimbatore') => {
const language = localStorage.getItem('i18nextLng') || "en";
const response = await api.get('/demand/forecast', {
params: { crop: cropId, location }
params: { crop: cropId, location, language }
});
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frontend sends language as an i18next code (e.g. en, ta), but the backend prompts in demandService treat this value as a human language name (default 'English'). Passing codes like ta/en reduces the chance the LLM will respond in the intended language; map codes to names (or switch backend to accept codes explicitly).

Copilot uses AI. Check for mistakes.
const client = getClient();
if (!client) {
console.warn('Twilio credentials not configured. SMS not sent.');
console.log(`[SMS SIMULATION] To: ${to}, Body: ${body}`);
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Twilio credentials are missing, this logs the full SMS body and recipient ([SMS SIMULATION] ...). These messages can contain sensitive user data (e.g., login alerts, order details). Avoid logging SMS bodies/phone numbers in non-dev environments, or redact them.

Suggested change
console.log(`[SMS SIMULATION] To: ${to}, Body: ${body}`);
const env = process.env.NODE_ENV;
if (env === 'development' || env === 'test') {
console.log(`[SMS SIMULATION] To: ${to}, Body: ${body}`);
} else {
console.log('[SMS SIMULATION] SMS not sent due to missing Twilio credentials.');
}

Copilot uses AI. Check for mistakes.

// Basic formatting for Indian numbers if standard 10-digit
// 1. Remove all whitespace and non-numeric chars except '+'
let formattedTo = to.replace(/[\s-]/g, '');
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says non-numeric chars are removed, but the implementation only strips whitespace and dashes (/[\s-]/g). This will fail for common formats like parentheses or dots. Either update the regex to match the comment/intent, or update the comment to reflect the actual behavior.

Suggested change
let formattedTo = to.replace(/[\s-]/g, '');
let formattedTo = to.replace(/[^+\d]/g, '');

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +66
const farmers = await User.find({
role: 'FARMER',
state: advisory.state,
isActive: true
});

console.log(`[Advisory] Found ${farmers.length} farmers in ${advisory.state} for alert.`);

// Send SMS to each farmer (Promise.all might send too fast, so iteration is safer for rate limits but slower)
for (const farmer of farmers) {
if (farmer.phoneNumber) {
await sendAdvisoryAlert(farmer.phoneNumber, {
type: advisory.type,
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SMS delivery is performed inline (awaited) inside the HTTP request. For large farmer sets this can significantly slow the request and risks timeouts/rate-limits. Consider enqueueing notifications to a background job (or at least using a bounded concurrency limiter) and returning the HTTP response without waiting on all SMS sends.

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +106
// NOTIFICATION: Send SMS to Farmer (New Order)
try {
console.log(`[OrderController] Begin SMS logic for Order: ${order._id}`);
// Fetch fresh User documents to ensure we have phone numbers
const farmer = await User.findById(order.farmerId);
const buyer = await User.findById(order.buyerId);

// Debug Logs
if (!farmer) console.warn(`[OrderController] Farmer not found for ID: ${order.farmerId}`);
if (!farmer?.phoneNumber) console.warn(`[OrderController] Farmer has no phone number: ${order.farmerId}`);

if (farmer && farmer.phoneNumber) {
console.log(`[OrderController] Sending New Order SMS to Farmer: ${farmer.phoneNumber}`);
await sendNewOrderAlert(farmer.phoneNumber, {
buyerName: buyer?.fullName || 'Buyer',
crop: crop.name || 'Crop',
quantity: order.quantity,
unit: 'kg' // Assuming default kg, or can be fetched from Crop
});
console.log(`[OrderController] SMS sent successfully.`);
}
} catch (smsErr) {
console.error("[OrderController] SMS Error:", smsErr);
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order creation waits on multiple DB lookups plus an awaited SMS send. This adds latency to the critical order path and can cause user-visible slowdowns when Twilio is slow. Consider making SMS sending asynchronous (queue/background) or at minimum not awaiting it before responding.

Suggested change
// NOTIFICATION: Send SMS to Farmer (New Order)
try {
console.log(`[OrderController] Begin SMS logic for Order: ${order._id}`);
// Fetch fresh User documents to ensure we have phone numbers
const farmer = await User.findById(order.farmerId);
const buyer = await User.findById(order.buyerId);
// Debug Logs
if (!farmer) console.warn(`[OrderController] Farmer not found for ID: ${order.farmerId}`);
if (!farmer?.phoneNumber) console.warn(`[OrderController] Farmer has no phone number: ${order.farmerId}`);
if (farmer && farmer.phoneNumber) {
console.log(`[OrderController] Sending New Order SMS to Farmer: ${farmer.phoneNumber}`);
await sendNewOrderAlert(farmer.phoneNumber, {
buyerName: buyer?.fullName || 'Buyer',
crop: crop.name || 'Crop',
quantity: order.quantity,
unit: 'kg' // Assuming default kg, or can be fetched from Crop
});
console.log(`[OrderController] SMS sent successfully.`);
}
} catch (smsErr) {
console.error("[OrderController] SMS Error:", smsErr);
}
// NOTIFICATION: Send SMS to Farmer (New Order) - fire-and-forget to avoid delaying response
void (async () => {
try {
console.log(`[OrderController] Begin SMS logic for Order: ${order._id}`);
// Fetch fresh User documents to ensure we have phone numbers
const farmer = await User.findById(order.farmerId);
const buyer = await User.findById(order.buyerId);
// Debug Logs
if (!farmer) {
console.warn(`[OrderController] Farmer not found for ID: ${order.farmerId}`);
}
if (!farmer?.phoneNumber) {
console.warn(`[OrderController] Farmer has no phone number: ${order.farmerId}`);
}
if (farmer && farmer.phoneNumber) {
console.log(`[OrderController] Sending New Order SMS to Farmer: ${farmer.phoneNumber}`);
await sendNewOrderAlert(farmer.phoneNumber, {
buyerName: buyer?.fullName || "Buyer",
crop: crop.name || "Crop",
quantity: order.quantity,
unit: "kg", // Assuming default kg, or can be fetched from Crop
});
console.log(`[OrderController] SMS sent successfully.`);
}
} catch (smsErr) {
console.error("[OrderController] SMS Error:", smsErr);
}
})();

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +118
const chatCompletion = await groq.chat.completions.create({
messages: [{ role: 'user', content: prompt }],
model: MODEL_NAME,
response_format: { type: "json_object" }
}, { timeout: 15000 }); // 15 second timeout
temperature: 0.1,
});
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getDemandAnalysis removed the explicit Groq request timeout that getCropRecommendations still uses. Without a timeout, a slow/hung upstream call can tie up the request handler longer than expected. Consider restoring a bounded timeout (or using an AbortController) for this call as well.

Copilot uses AI. Check for mistakes.
Comment thread backend/package.json
Comment on lines 39 to 44
"@google/generative-ai": "^0.24.1",
"@prisma/client": "^6.19.2",
"@types/socket.io": "^3.0.1",
"@types/twilio": "^3.19.2",
"bcrypt": "^6.0.0",
"cloudinary": "^2.9.0",
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@types/twilio is a TypeScript-only dependency and should typically be in devDependencies (it isn't needed at runtime). Consider moving it to devDependencies to keep the production install footprint smaller.

Copilot uses AI. Check for mistakes.
<div className="flex items-center text-sm font-medium text-nature-500 mt-2">
<MapPin className="w-4 h-4 mr-1.5 text-nature-400" />
<span className="truncate">{crop.location?.district || 'Unknown'}, {crop.location?.state || 'Location'}</span>
<span className="truncate">{crop.location?.district || t('dynamic.unknown')} , {crop.location?.state || t('common.location')}</span>
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t('dynamic.unknown') is referenced here, but there is no dynamic.unknown key in the i18n resources (only dynamic.crops.unknown / dynamic.crops.unknown crop). This will render the key string in the UI; use an existing key or add the missing translation entry.

Suggested change
<span className="truncate">{crop.location?.district || t('dynamic.unknown')} , {crop.location?.state || t('common.location')}</span>
<span className="truncate">{crop.location?.district || t('dynamic.crops.unknown')} , {crop.location?.state || t('common.location')}</span>

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +22
// Check localStorage cache
const localCache = localStorage.getItem('translation_cache');
const localMap = localCache ? JSON.parse(localCache) : {};
if (localMap[cacheKey]) {
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON.parse(localStorage.getItem('translation_cache')) is unguarded. If localStorage is unavailable (privacy mode) or the cached value is corrupted, this will throw and break rendering/translation. Wrap the read/parse in a try/catch (and reset the cache on parse failure).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants