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
30 changes: 30 additions & 0 deletions backend/models/SenderReputation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const mongoose = require('mongoose');

const SenderReputationSchema = new mongoose.Schema({
email: { type: String, required: true, index: true },
domain: { type: String, required: true, index: true },
score: { type: Number, default: 50, min: 0, max: 100 },
spamReports: { type: Number, default: 0 },
hamReports: { type: Number, default: 0 },
lastSeen: { type: Date, default: Date.now }
});

SenderReputationSchema.methods.recordHam = function() {
this.hamReports += 1;
this.score = Math.min(100, this.score + 2);
return this.save();
};

SenderReputationSchema.methods.recordSpam = function() {
this.spamReports += 1;
this.score = Math.max(0, this.score - 5);
return this.save();
};

SenderReputationSchema.methods.getLevel = function() {
if (this.score >= 70) return 'trusted';
if (this.score >= 40) return 'neutral';
return 'suspicious';
};

module.exports = mongoose.model('SenderReputation', SenderReputationSchema);
13 changes: 13 additions & 0 deletions backend/models/UserFeedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const mongoose = require('mongoose');

const UserFeedbackSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
text: { type: String, required: true },
predicted_label: { type: String, required: true },
correct_label: { type: String, required: true },
sender: { type: String },
confidence: { type: Number, default: 0 },
created_at: { type: Date, default: Date.now }
});

module.exports = mongoose.model('UserFeedback', UserFeedbackSchema);
39 changes: 39 additions & 0 deletions frontend/src/components/EmailScannerDashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
checkConnectionStatus();
handleOAuthCallback();
refreshImapStatus();
}, []);

Check warning on line 44 in frontend/src/components/EmailScannerDashboard.jsx

View workflow job for this annotation

GitHub Actions / Frontend (npm test)

React Hook useEffect has a missing dependency: 'handleOAuthCallback'. Either include it or remove the dependency array

const checkConnectionStatus = async () => {
try {
Expand Down Expand Up @@ -118,6 +118,29 @@
}
};

const handleFeedback = async (email, correctLabel) => {
try {
const token = localStorage.getItem('token');
await axios.post('/api/feedback', {

Check failure on line 124 in frontend/src/components/EmailScannerDashboard.jsx

View workflow job for this annotation

GitHub Actions / Frontend (npm test)

'axios' is not defined
text: email.body,
predicted_label: email.prediction,
correct_label: correctLabel,
sender: email.sender,
confidence: email.confidence
}, {
headers: { Authorization: `Bearer ${token}` }
});

setEmails(prev => prev.map(e =>

Check failure on line 134 in frontend/src/components/EmailScannerDashboard.jsx

View workflow job for this annotation

GitHub Actions / Frontend (npm test)

'setEmails' is not defined
e.id === email.id ? { ...e, feedbackGiven: true } : e
));

toast.success('Thank you for your feedback!');

Check failure on line 138 in frontend/src/components/EmailScannerDashboard.jsx

View workflow job for this annotation

GitHub Actions / Frontend (npm test)

'toast' is not defined
} catch (error) {

Check failure on line 139 in frontend/src/components/EmailScannerDashboard.jsx

View workflow job for this annotation

GitHub Actions / Frontend (npm test)

'error' is defined but never used
toast.error('Failed to save feedback');

Check failure on line 140 in frontend/src/components/EmailScannerDashboard.jsx

View workflow job for this annotation

GitHub Actions / Frontend (npm test)

'toast' is not defined
}
};

const handleScan = async (provider) => {
if (!provider) return;
setScanning(true);
Expand Down Expand Up @@ -361,6 +384,22 @@
)}
</div>
</div>
<div className="email-actions">
{!email.feedbackGiven && (
<>
<button onClick={() => handleFeedback(email, 'ham')}>
✅ Mark as Safe
</button>
<button onClick={() => handleFeedback(email, 'spam')}>
🚫 Mark as Spam
</button>
</>
)}
{email.feedbackGiven && (
<span className="feedback-thanks">✅ Thanks for your feedback!</span>
)}
</div>

{/* IMAP Card */}
<div className={`p-5 rounded-2xl border transition-all duration-300 ${
isDark ? "bg-slate-900/40 border-slate-800" : "bg-white/45 border-slate-200"
Expand Down
Loading