diff --git a/backend/server3.js b/backend/server3.js index 502d4fa..ae14c7c 100644 --- a/backend/server3.js +++ b/backend/server3.js @@ -1667,29 +1667,38 @@ app.post( } const userId = req.userId; - const usageMonth = new Date().toISOString().slice(0, 7); - const userPlanRow = await db.get(` - SELECT is_premium, is_pro_premium - FROM user_profile - WHERE user_id = ? - `, [userId]); - - let userPlan = 'basic'; // default - if (userPlanRow?.is_pro_premium) userPlan = 'pro'; - else if (userPlanRow?.is_premium) userPlan = 'premium'; - + const now = new Date(); + const currentWeek = getWeekNumber(now); // Function defined below - if (userPlan === 'premium') { - const usageRow = await db.get( - `SELECT usage_count FROM feature_usage - WHERE user_id = ? AND feature_name = 'resume_optimize' AND usage_month = ?`, - [userId, usageMonth] + const userProfile = await db.get( + `SELECT is_premium, is_pro_premium, resume_optimizations_used, resume_limit_reset, resume_booster_count + FROM user_profile + WHERE user_id = ?`, + [userId] + ); + + let userPlan = 'basic'; + if (userProfile?.is_pro_premium) userPlan = 'pro'; + else if (userProfile?.is_premium) userPlan = 'premium'; + + const weeklyLimits = { basic: 1, premium: 2, pro: 5 }; + const userWeeklyLimit = weeklyLimits[userPlan] || 0; + + let resetDate = new Date(userProfile.resume_limit_reset); + if (!userProfile.resume_limit_reset || now > resetDate) { + resetDate = new Date(now); + resetDate.setDate(now.getDate() + 7); + await db.run( + `UPDATE user_profile SET resume_optimizations_used = 0, resume_limit_reset = ? WHERE user_id = ?`, + [resetDate.toISOString(), userId] ); - const usageCount = usageRow?.usage_count || 0; + userProfile.resume_optimizations_used = 0; + } - if (usageCount >= MAX_MONTHLY_REWRITES_PREMIUM) { - return res.status(403).json({ error: 'Monthly limit reached. Upgrade to Pro.' }); - } + const totalLimit = userWeeklyLimit + (userProfile.resume_booster_count || 0); + + if (userProfile.resume_optimizations_used >= totalLimit) { + return res.status(403).json({ error: 'Weekly resume optimization limit reached. Consider purchasing a booster pack.' }); } const filePath = req.file.path; @@ -1715,29 +1724,16 @@ app.post( const optimizedResume = completion?.choices?.[0]?.message?.content?.trim() || ''; - if (userPlan === 'premium') { - const existing = await db.get( - `SELECT usage_count FROM feature_usage - WHERE user_id = ? AND feature_name = 'resume_optimize' AND usage_month = ?`, - [userId, usageMonth] - ); - if (existing) { - await db.run( - `UPDATE feature_usage SET usage_count = usage_count + 1 - WHERE user_id = ? AND feature_name = 'resume_optimize' AND usage_month = ?`, - [userId, usageMonth] - ); - } else { - await db.run( - `INSERT INTO feature_usage (user_id, feature_name, usage_month, usage_count) - VALUES (?, 'resume_optimize', ?, 1)`, - [userId, usageMonth] - ); - } - } - + await db.run( + `UPDATE user_profile SET resume_optimizations_used = resume_optimizations_used + 1 WHERE user_id = ?`, + [userId] + ); + + // Calculate remaining optimizations + const remainingOptimizations = totalLimit - (userProfile.resume_optimizations_used + 1); + fs.unlinkSync(filePath); - res.json({ optimizedResume }); + res.json({ optimizedResume, remainingOptimizations }); } catch (err) { console.error('Error optimizing resume:', err); res.status(500).json({ error: 'Failed to optimize resume.' }); @@ -1745,6 +1741,58 @@ app.post( } ); +app.get( + '/api/premium/resume/remaining', + authenticatePremiumUser, + async (req, res) => { + try { + const userId = req.userId; + const now = new Date(); + + const userProfile = await db.get( + `SELECT is_premium, is_pro_premium, resume_optimizations_used, resume_limit_reset, resume_booster_count + FROM user_profile + WHERE user_id = ?`, + [userId] + ); + + let userPlan = 'basic'; + if (userProfile?.is_pro_premium) userPlan = 'pro'; + else if (userProfile?.is_premium) userPlan = 'premium'; + + const weeklyLimits = { basic: 1, premium: 2, pro: 5 }; + const userWeeklyLimit = weeklyLimits[userPlan] || 0; + + let resetDate = new Date(userProfile.resume_limit_reset); + if (!userProfile.resume_limit_reset || now > resetDate) { + resetDate = new Date(now); + resetDate.setDate(now.getDate() + 7); + await db.run( + `UPDATE user_profile SET resume_optimizations_used = 0, resume_limit_reset = ? WHERE user_id = ?`, + [resetDate.toISOString(), userId] + ); + userProfile.resume_optimizations_used = 0; + } + + const totalLimit = userWeeklyLimit + (userProfile.resume_booster_count || 0); + const remainingOptimizations = totalLimit - userProfile.resume_optimizations_used; + + res.json({ remainingOptimizations, resetDate }); + } catch (err) { + console.error('Error fetching remaining optimizations:', err); + res.status(500).json({ error: 'Failed to fetch remaining optimizations.' }); + } + } +); + + +// Helper function to get the week number +function getWeekNumber(date) { + const oneJan = new Date(date.getFullYear(), 0, 1); + const numberOfDays = Math.floor((date - oneJan) / (24 * 60 * 60 * 1000)); + return Math.ceil((date.getDay() + 1 + numberOfDays) / 7); +} + /* ------------------------------------------------------------------ FALLBACK (404 for unmatched routes) diff --git a/src/components/ResumeRewrite.js b/src/components/ResumeRewrite.js index 9789ed3..af0afc5 100644 --- a/src/components/ResumeRewrite.js +++ b/src/components/ResumeRewrite.js @@ -1,6 +1,5 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import axios from 'axios'; -// If you still want file-saver + docx, import them here function ResumeRewrite() { const [resumeFile, setResumeFile] = useState(null); @@ -8,11 +7,31 @@ function ResumeRewrite() { const [jobDescription, setJobDescription] = useState(''); const [optimizedResume, setOptimizedResume] = useState(''); const [error, setError] = useState(''); + const [remainingOptimizations, setRemainingOptimizations] = useState(null); + const [resetDate, setResetDate] = useState(null); const handleFileChange = (e) => { setResumeFile(e.target.files[0]); }; + const fetchRemainingOptimizations = async () => { + try { + const token = localStorage.getItem('token'); + const res = await axios.get('/api/premium/resume/remaining', { + headers: { Authorization: `Bearer ${token}` }, + }); + setRemainingOptimizations(res.data.remainingOptimizations); + setResetDate(new Date(res.data.resetDate).toLocaleDateString()); + } catch (err) { + console.error('Error fetching optimizations:', err); + setError('Could not fetch optimization limits.'); + } + }; + + useEffect(() => { + fetchRemainingOptimizations(); + }, []); + const handleSubmit = async (e) => { e.preventDefault(); if (!resumeFile || !jobTitle.trim() || !jobDescription.trim()) { @@ -36,101 +55,62 @@ function ResumeRewrite() { setOptimizedResume(res.data.optimizedResume || ''); setError(''); + + // Refresh remaining optimizations after optimizing + fetchRemainingOptimizations(); } catch (err) { console.error('Resume optimization error:', err); setError(err.response?.data?.error || 'Failed to optimize resume.'); } }; - // Optional: Download as docx - // const handleDownloadDocx = () => { ... } - return (

Resume Optimizer

+ {remainingOptimizations !== null && ( +
+ {remainingOptimizations} Resume Optimizations Remaining This Week + {resetDate && (Resets on {resetDate})} +
+ )} +
- {/* File Upload */}
- - Upload Resume (PDF or DOCX): +
- {/* Job Title */}
- - setJobTitle(e.target.value)} - className="w-full border rounded px-3 py-2 focus:outline-none - focus:ring focus:ring-blue-200" + + setJobTitle(e.target.value)} + className="w-full border rounded px-3 py-2 focus:outline-none focus:ring focus:ring-blue-200" placeholder="e.g., Software Engineer" />
- {/* Job Description */}
- -