Skip to content

Commit 82909ac

Browse files
Sai Sridhar Tarraclaude
authored andcommitted
Fix: colors→blue, logo→blue, Stripe portal URL, CSV export error handling
Colors: - Revert tailwind primary palette back to blue (#2563eb primary-600) - Revert all -violet- classes back to -blue- across 12 files Logo (Threadly): - Update icon.svg, logo.svg, logo-dark.svg gradients purple→blue (#3b82f6→#1d4ed8) - Wordmark accent 'ly': purple→blue (#2563eb); dark variant→#60a5fa - Third thread line shortened for visual depth (43→37) in both icon + logo Stripe: - Fix create_billing_portal_session: replace hardcoded https://inboxiq.app/billing with dynamic FRONTEND_URL from settings CSV Export: - Add session guard (throw if no access_token) - Add HTTP status check before calling .blob() — was silently trying to parse error responses as binary data - Append/remove anchor to DOM for reliable cross-browser download trigger - Rename downloaded file from inboxiq-export-* to threadly-export-* Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 58ec19e commit 82909ac

18 files changed

Lines changed: 72 additions & 67 deletions

File tree

backend/services/stripe_service.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@ async def create_billing_portal_session(customer_id: str) -> str | None:
119119
subscription. Returns the portal URL.
120120
"""
121121
try:
122+
frontend_url = settings.FRONTEND_URL.rstrip("/") if settings.FRONTEND_URL else "http://localhost:3000"
122123
session = stripe.billing_portal.Session.create(
123124
customer=customer_id,
124-
return_url="https://inboxiq.app/billing",
125+
return_url=f"{frontend_url}/billing",
125126
)
126127
return session.url
127128
except Exception as exc:

frontend/components/CategoryBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const categoryConfig: Record<
2626
},
2727
needs_response: {
2828
label: 'Needs Response',
29-
classes: 'bg-violet-100 dark:bg-violet-900/20 text-violet-700 dark:text-violet-400 border border-violet-200 dark:border-violet-800',
29+
classes: 'bg-blue-100 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 border border-blue-200 dark:border-blue-800',
3030
Icon: MessageSquare,
3131
},
3232
follow_up: {

frontend/components/EmailCard.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ interface EmailCardProps {
2020

2121
// Gradient colors for avatars based on first letter
2222
const AVATAR_GRADIENTS = [
23-
'from-violet-400 to-violet-600',
24-
'from-violet-400 to-violet-600',
23+
'from-blue-400 to-blue-600',
24+
'from-blue-400 to-blue-600',
2525
'from-emerald-400 to-emerald-600',
2626
'from-rose-400 to-rose-600',
2727
'from-amber-400 to-amber-600',
@@ -155,7 +155,7 @@ export default function EmailCard({ email, className, onDismiss, selected, onTog
155155
{/* Snooze button */}
156156
<button
157157
onClick={handleSnoozeClick}
158-
className="opacity-0 group-hover:opacity-100 rounded p-0.5 text-gray-300 dark:text-gray-600 hover:text-violet-400 hover:bg-violet-50 dark:hover:bg-violet-900/20 transition-all duration-150"
158+
className="opacity-0 group-hover:opacity-100 rounded p-0.5 text-gray-300 dark:text-gray-600 hover:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-all duration-150"
159159
title="Snooze"
160160
>
161161
<AlarmClock className="h-3.5 w-3.5" />
@@ -200,7 +200,7 @@ export default function EmailCard({ email, className, onDismiss, selected, onTog
200200
</span>
201201
)}
202202
{isSnoozed && (
203-
<span className="inline-flex items-center gap-1 rounded-full bg-violet-50 px-2 py-0.5 text-xs font-medium text-violet-600 border border-violet-100">
203+
<span className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-600 border border-blue-100">
204204
<AlarmClock className="h-3 w-3" />
205205
Snoozed
206206
</span>

frontend/components/SnoozeModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default function SnoozeModal({ emailId, currentSnooze, onSnoozed, onClose
7979
{/* Header */}
8080
<div className="flex items-center justify-between px-5 py-4 border-b border-gray-100 dark:border-gray-700">
8181
<div className="flex items-center gap-2">
82-
<AlarmClock className="h-4 w-4 text-violet-600" />
82+
<AlarmClock className="h-4 w-4 text-blue-600" />
8383
<h2 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Snooze email</h2>
8484
</div>
8585
<button

frontend/lib/api.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,20 @@ export const emailsApi = {
205205

206206
exportCSV: async (): Promise<void> => {
207207
const { data: { session } } = await supabase.auth.getSession();
208+
if (!session?.access_token) throw new Error('Not authenticated');
208209
const base = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
209210
const res = await fetch(`${base}/api/emails/export`, {
210-
headers: { Authorization: `Bearer ${session?.access_token}` },
211+
headers: { Authorization: `Bearer ${session.access_token}` },
211212
});
213+
if (!res.ok) throw new Error(`Export failed: ${res.status}`);
212214
const blob = await res.blob();
213215
const url = URL.createObjectURL(blob);
214216
const a = document.createElement('a');
215217
a.href = url;
216-
a.download = `inboxiq-export-${new Date().toISOString().split('T')[0]}.csv`;
218+
a.download = `threadly-export-${new Date().toISOString().split('T')[0]}.csv`;
219+
document.body.appendChild(a);
217220
a.click();
221+
document.body.removeChild(a);
218222
URL.revokeObjectURL(url);
219223
},
220224

frontend/pages/admin/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export default function AdminPage() {
127127
icon={BarChart2}
128128
label="Emails Today"
129129
value={stats.emails_today}
130-
color="bg-violet-600"
130+
color="bg-blue-600"
131131
/>
132132
</div>
133133

frontend/pages/analytics/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ type SenderInsight = {
3636
};
3737

3838
const SENDER_GRADIENTS = [
39-
'from-violet-400 to-violet-600',
40-
'from-violet-400 to-violet-600',
39+
'from-blue-400 to-blue-600',
40+
'from-blue-400 to-blue-600',
4141
'from-emerald-400 to-emerald-600',
4242
'from-rose-400 to-rose-600',
4343
'from-amber-400 to-amber-600',
@@ -383,7 +383,7 @@ export default function AnalyticsPage() {
383383
{(loading || responseTime) && (
384384
<div className="card p-5 animate-slide-up">
385385
<div className="flex items-center gap-2 mb-5">
386-
<TrendingUp className="h-4 w-4 text-violet-600" />
386+
<TrendingUp className="h-4 w-4 text-blue-600" />
387387
<h2 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Response Time Analytics</h2>
388388
{responseTime && (
389389
<span className="ml-auto text-xs text-gray-500 dark:text-gray-400">
@@ -407,7 +407,7 @@ export default function AnalyticsPage() {
407407
</div>
408408
<div className="h-2 w-full rounded-full bg-gray-100 dark:bg-gray-700 overflow-hidden">
409409
<div
410-
className="h-full rounded-full bg-violet-500 transition-all duration-700"
410+
className="h-full rounded-full bg-blue-500 transition-all duration-700"
411411
style={{ width: `${Math.min((hours / (responseTime.overall_avg_hours * 2 || 1)) * 100, 100)}%` }}
412412
/>
413413
</div>

frontend/pages/billing/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ const PLANS: Plan[] = [
7272

7373
const statusConfig: Record<string, { label: string; classes: string }> = {
7474
active: { label: 'Active', classes: 'bg-green-50 text-green-700 border border-green-200' },
75-
trialing: { label: 'Trial', classes: 'bg-violet-50 text-violet-700 border border-violet-200' },
75+
trialing: { label: 'Trial', classes: 'bg-blue-50 text-blue-700 border border-blue-200' },
7676
past_due: { label: 'Past Due', classes: 'bg-red-50 text-red-700 border border-red-200' },
7777
canceled: { label: 'Canceled', classes: 'bg-gray-100 text-gray-600 border border-gray-200' },
7878
none: { label: 'Free', classes: 'bg-gray-100 text-gray-600 border border-gray-200' },

frontend/pages/crm/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import toast from 'react-hot-toast';
2727
// ─── Constants ───────────────────────────────────────────────────────────────
2828

2929
const SENDER_GRADIENTS = [
30-
'from-violet-400 to-violet-600',
31-
'from-violet-400 to-violet-600',
30+
'from-blue-400 to-blue-600',
31+
'from-blue-400 to-blue-600',
3232
'from-emerald-400 to-emerald-600',
3333
'from-rose-400 to-rose-600',
3434
'from-amber-400 to-amber-600',
@@ -49,7 +49,7 @@ const CATEGORY_CONFIG: Record<string, { label: string; classes: string }> = {
4949
},
5050
needs_response: {
5151
label: 'Needs Response',
52-
classes: 'bg-violet-100 dark:bg-violet-900/20 text-violet-700 dark:text-violet-400 border border-violet-200 dark:border-violet-800',
52+
classes: 'bg-blue-100 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 border border-blue-200 dark:border-blue-800',
5353
},
5454
follow_up: {
5555
label: 'Follow Up',
@@ -388,9 +388,9 @@ interface PipelineCard {
388388
}
389389

390390
const PIPELINE_STAGES: { id: PipelineStage; label: string; color: string; bg: string }[] = [
391-
{ id: 'new_lead', label: 'New Lead', color: 'text-violet-700 dark:text-violet-300', bg: 'bg-violet-50 dark:bg-violet-900/20 border-violet-200 dark:border-violet-700' },
391+
{ id: 'new_lead', label: 'New Lead', color: 'text-blue-700 dark:text-blue-300', bg: 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-700' },
392392
{ id: 'contacted', label: 'Contacted', color: 'text-amber-700 dark:text-amber-300', bg: 'bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-700' },
393-
{ id: 'proposal', label: 'Proposal Sent', color: 'text-violet-700 dark:text-violet-300', bg: 'bg-violet-50 dark:bg-violet-900/20 border-violet-200 dark:border-violet-700' },
393+
{ id: 'proposal', label: 'Proposal Sent', color: 'text-blue-700 dark:text-blue-300', bg: 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-700' },
394394
{ id: 'won', label: 'Won', color: 'text-emerald-700 dark:text-emerald-300', bg: 'bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-700' },
395395
{ id: 'lost', label: 'Lost', color: 'text-gray-600 dark:text-gray-400', bg: 'bg-gray-50 dark:bg-gray-800 border-gray-200 dark:border-gray-700' },
396396
];

frontend/pages/email/[id].tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ function getFileColor(mimeType: string): string {
7777
if (mimeType.startsWith('image/')) return 'text-pink-500 bg-pink-50';
7878
if (mimeType.includes('pdf')) return 'text-red-500 bg-red-50';
7979
if (mimeType.includes('spreadsheet') || mimeType.includes('excel')) return 'text-green-600 bg-green-50';
80-
if (mimeType.includes('word') || mimeType.includes('document')) return 'text-violet-500 bg-violet-50';
80+
if (mimeType.includes('word') || mimeType.includes('document')) return 'text-blue-500 bg-blue-50';
8181
return 'text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700';
8282
}
8383

@@ -550,7 +550,7 @@ export default function EmailDetailPage() {
550550
className="btn-secondary text-sm"
551551
title="Snooze"
552552
>
553-
<AlarmClock className="h-4 w-4 text-violet-500" />
553+
<AlarmClock className="h-4 w-4 text-blue-500" />
554554
<span className="hidden sm:inline ml-1.5">Snooze</span>
555555
</button>
556556

@@ -563,7 +563,7 @@ export default function EmailDetailPage() {
563563
>
564564
{loadingThreadSummary
565565
? <Loader2 className="h-4 w-4 animate-spin mr-1.5" />
566-
: <Layers className="h-4 w-4 mr-1.5 text-violet-500" />}
566+
: <Layers className="h-4 w-4 mr-1.5 text-blue-500" />}
567567
<span className="hidden sm:inline">Thread</span>
568568
</button>
569569

@@ -727,36 +727,36 @@ export default function EmailDetailPage() {
727727

728728
{/* Thread Summary Card */}
729729
{threadSummary && threadSummary.status === 'summarized' && (
730-
<div className="mt-4 rounded-lg border border-violet-200 dark:border-violet-700 bg-violet-50 dark:bg-violet-900/20 px-4 py-3">
730+
<div className="mt-4 rounded-lg border border-blue-200 dark:border-blue-700 bg-blue-50 dark:bg-blue-900/20 px-4 py-3">
731731
<div className="flex items-start justify-between gap-3">
732732
<div className="flex items-start gap-2 min-w-0 flex-1">
733-
<Layers className="h-4 w-4 text-violet-600 dark:text-violet-400 flex-shrink-0 mt-0.5" />
733+
<Layers className="h-4 w-4 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5" />
734734
<div className="min-w-0 flex-1">
735-
<p className="text-sm font-semibold text-violet-800 dark:text-violet-300">
735+
<p className="text-sm font-semibold text-blue-800 dark:text-blue-300">
736736
Thread Summary ({threadSummary.thread_length} emails)
737737
</p>
738-
<p className="mt-1 text-xs text-violet-700 dark:text-violet-400 leading-relaxed">
738+
<p className="mt-1 text-xs text-blue-700 dark:text-blue-400 leading-relaxed">
739739
{threadSummary.summary}
740740
</p>
741741
{threadSummary.key_points.length > 0 && (
742742
<ul className="mt-2 space-y-0.5">
743743
{threadSummary.key_points.map((pt, i) => (
744-
<li key={i} className="text-xs text-violet-700 dark:text-violet-400 flex items-start gap-1.5">
744+
<li key={i} className="text-xs text-blue-700 dark:text-blue-400 flex items-start gap-1.5">
745745
<span className="flex-shrink-0 mt-0.5"></span>{pt}
746746
</li>
747747
))}
748748
</ul>
749749
)}
750750
{threadSummary.next_action && (
751-
<p className="mt-2 text-xs font-medium text-violet-800 dark:text-violet-300">
751+
<p className="mt-2 text-xs font-medium text-blue-800 dark:text-blue-300">
752752
Next: {threadSummary.next_action}
753753
</p>
754754
)}
755755
</div>
756756
</div>
757757
<button
758758
onClick={() => setThreadSummary(null)}
759-
className="flex-shrink-0 p-1 rounded text-violet-500 hover:text-violet-700 dark:hover:text-violet-300 hover:bg-violet-100 dark:hover:bg-violet-800/40 transition-colors"
759+
className="flex-shrink-0 p-1 rounded text-blue-500 hover:text-blue-700 dark:hover:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-800/40 transition-colors"
760760
>
761761
<X className="h-3.5 w-3.5" />
762762
</button>
@@ -1001,7 +1001,7 @@ export default function EmailDetailPage() {
10011001
{/* Reply Editor */}
10021002
<div className="animate-slide-up stagger-4">
10031003
<div className="flex items-center gap-2 mb-3">
1004-
<MessageSquare className="h-4 w-4 text-violet-600" />
1004+
<MessageSquare className="h-4 w-4 text-blue-600" />
10051005
<h2 className="text-base font-semibold text-gray-900 dark:text-gray-100">AI Reply Draft</h2>
10061006
</div>
10071007
<ReplyEditor emailId={email.id} draft={replyDraft} onSent={() => mutateEmail()} />

0 commit comments

Comments
 (0)