Skip to content

Commit 701168d

Browse files
feat: stabilize frontend, fix lint errors, and enhance auth flow
1 parent de1418d commit 701168d

19 files changed

Lines changed: 314 additions & 72 deletions

File tree

frontend/app/auth/register/page.tsx

Lines changed: 107 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
import React, { useState } from 'react';
44
import Link from 'next/link';
5-
import { motion } from 'framer-motion';
5+
import { useRouter } from 'next/navigation';
6+
import { useAuthStore } from '@/store/authStore';
7+
import { useMutation } from '@tanstack/react-query';
8+
import axios from 'axios';
9+
import { toast } from 'sonner';
610
import {
711
Github,
812
Chrome,
@@ -12,11 +16,62 @@ import {
1216
Mail,
1317
User,
1418
Building2,
15-
ShieldCheck
19+
ShieldCheck,
20+
Loader2
1621
} from 'lucide-react';
1722

23+
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api/v1';
24+
1825
export default function RegisterPage() {
19-
const [isLoading, setIsLoading] = useState(false);
26+
const router = useRouter();
27+
const login = useAuthStore((state) => state.login);
28+
29+
// Form State
30+
const [fullName, setFullName] = useState('');
31+
const [email, setEmail] = useState('');
32+
const [companyName, setCompanyName] = useState('');
33+
const [password, setPassword] = useState('');
34+
35+
const { mutate: handleRegister, isPending } = useMutation({
36+
mutationFn: async () => {
37+
const response = await axios.post(`${API_URL}/auth/register`, {
38+
name: fullName,
39+
email,
40+
password,
41+
companyName
42+
});
43+
return response.data;
44+
},
45+
onSuccess: (data) => {
46+
console.log("Registration success:", data);
47+
// Assuming the API returns user and token structure similar to login
48+
// If the API automatically logs them in, great.
49+
// If not, we might need to redirect to login or auto-login.
50+
// Based on typical auth flows, we usually get a token back.
51+
if (data.token && data.user) {
52+
login(data.user, data.token);
53+
toast.success("Workspace created successfully!");
54+
router.push('/dashboard');
55+
} else {
56+
toast.success("Account created! Please log in.");
57+
router.push('/auth/login');
58+
}
59+
},
60+
onError: (error: any) => {
61+
console.error("Registration error:", error);
62+
const message = error.response?.data?.error || "Registration failed. Please try again.";
63+
toast.error(message);
64+
}
65+
});
66+
67+
const onSubmit = (e: React.FormEvent) => {
68+
e.preventDefault();
69+
if (!fullName || !email || !companyName || !password) {
70+
toast.error("Please fill in all fields");
71+
return;
72+
}
73+
handleRegister();
74+
};
2075

2176
return (
2277
<div className="min-h-screen bg-slate-950 flex flex-col lg:flex-row overflow-hidden font-sans">
@@ -83,26 +138,59 @@ export default function RegisterPage() {
83138
<div className="relative flex justify-center text-xs uppercase font-black tracking-[0.3em]"><span className="bg-slate-950 px-4 text-slate-700">Or with email</span></div>
84139
</div>
85140

86-
<form className="space-y-6">
141+
<form onSubmit={onSubmit} className="space-y-6">
87142
<div className="grid grid-cols-2 gap-4">
88-
<InputField icon={User} label="Full Name" placeholder="Alex Rivera" />
89-
<InputField icon={Mail} label="Work Email" placeholder="alex@company.com" />
143+
<InputField
144+
icon={User}
145+
label="Full Name"
146+
placeholder="Alex Rivera"
147+
value={fullName}
148+
onChange={(e: any) => setFullName(e.target.value)}
149+
/>
150+
<InputField
151+
icon={Mail}
152+
label="Work Email"
153+
placeholder="alex@company.com"
154+
type="email"
155+
value={email}
156+
onChange={(e: any) => setEmail(e.target.value)}
157+
/>
90158
</div>
91-
<InputField icon={Building2} label="Company Name" placeholder="Acme Inc." />
92-
<InputField icon={Lock} label="Password" placeholder="••••••••" type="password" />
159+
<InputField
160+
icon={Building2}
161+
label="Company Name"
162+
placeholder="Acme Inc."
163+
value={companyName}
164+
onChange={(e: any) => setCompanyName(e.target.value)}
165+
/>
166+
<InputField
167+
icon={Lock}
168+
label="Password"
169+
placeholder="••••••••"
170+
type="password"
171+
value={password}
172+
onChange={(e: any) => setPassword(e.target.value)}
173+
/>
93174

94175
<div className="flex items-center space-x-3 p-4 bg-blue-600/5 border border-blue-500/10 rounded-2xl">
95176
<ShieldCheck className="w-5 h-5 text-blue-500" />
96177
<p className="text-[10px] font-bold text-blue-400 uppercase tracking-widest">Auto-KYC enabled. Verification takes 3-5 mins.</p>
97178
</div>
98179

99-
<Link
100-
href="/dashboard"
101-
className="flex w-full justify-center items-center space-x-3 bg-white text-black py-5 rounded-2xl font-black text-lg hover:bg-slate-200 transition-all active:scale-95 shadow-xl shadow-white/5 mt-8"
180+
<button
181+
type="submit"
182+
disabled={isPending}
183+
className="flex w-full justify-center items-center space-x-3 bg-white text-black py-5 rounded-2xl font-black text-lg hover:bg-slate-200 transition-all active:scale-95 shadow-xl shadow-white/5 mt-8 disabled:opacity-50 disabled:cursor-not-allowed"
102184
>
103-
<span>Initialize My Console</span>
104-
<ArrowRight className="w-5 h-5" />
105-
</Link>
185+
{isPending ? (
186+
<Loader2 className="w-5 h-5 animate-spin" />
187+
) : (
188+
<>
189+
<span>Initialize My Console</span>
190+
<ArrowRight className="w-5 h-5" />
191+
</>
192+
)}
193+
</button>
106194
</form>
107195

108196
<p className="text-center text-sm font-bold text-slate-500">
@@ -123,23 +211,26 @@ export default function RegisterPage() {
123211

124212
function SocialButton({ icon: Icon, label }: any) {
125213
return (
126-
<button className="flex items-center justify-center space-x-3 bg-white/5 border border-white/10 p-4 rounded-2xl hover:bg-white/10 transition-all group">
214+
<button type="button" className="flex items-center justify-center space-x-3 bg-white/5 border border-white/10 p-4 rounded-2xl hover:bg-white/10 transition-all group">
127215
<Icon className="w-5 h-5 text-slate-400 group-hover:text-white transition-colors" />
128216
<span className="text-sm font-bold text-white">{label}</span>
129217
</button>
130218
)
131219
}
132220

133-
function InputField({ icon: Icon, label, placeholder, type = "text" }: any) {
221+
function InputField({ icon: Icon, label, placeholder, type = "text", value, onChange }: any) {
134222
return (
135223
<div className="space-y-3">
136224
<label className="text-[10px] font-black text-slate-500 uppercase tracking-widest px-1">{label}</label>
137225
<div className="relative">
138226
<Icon className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-600" />
139227
<input
140228
type={type}
229+
value={value}
230+
onChange={onChange}
141231
className="w-full bg-slate-900 border border-white/5 rounded-2xl p-4 pl-12 text-sm text-white focus:outline-none focus:ring-2 focus:ring-blue-600/50 transition-all placeholder:text-slate-700"
142232
placeholder={placeholder}
233+
required
143234
/>
144235
</div>
145236
</div>

frontend/app/dashboard/admin/reconciliation/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useState, useEffect } from 'react';
3+
import React, { useState, useEffect } from 'react';
44
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
55
import { Button } from "@/components/ui/button";
66
import { Input } from "@/components/ui/input";
@@ -72,7 +72,7 @@ export default function ReconciliationPage() {
7272

7373
// Send to Backend with Idempotency Key
7474
const idempotencyKey = crypto.randomUUID();
75-
const response = await api.post('/admin/reconcile/run', {
75+
const response = await api.post<{ data: any }>('/admin/reconcile/run', {
7676
date,
7777
records
7878
}, {

frontend/app/dashboard/business/bulk-issuance/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export default function BulkIssuancePage() {
182182
</CardHeader>
183183
<CardContent className="space-y-4 text-sm text-slate-600">
184184
<p>1. Download the CSV template.</p>
185-
<p>2. Fill in the employee details. Setting a role of 'manager' gives admin access.</p>
185+
<p>2. Fill in the employee details. Setting a role of &apos;manager&apos; gives admin access.</p>
186186
<p>3. Upload the file to provision accounts.</p>
187187
<div className="bg-amber-50 text-amber-800 p-3 rounded border border-amber-200 text-xs">
188188
<strong>Note:</strong> Employees will need to reset their password via email (simulated) to access their new dashboard.

frontend/app/dashboard/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function DashboardPage() {
3636
</div>
3737
<h4 className="text-xl font-bold mb-3 tracking-tight text-white">AI Insights</h4>
3838
<p className="text-sm text-slate-400 font-medium leading-relaxed mb-6">
39-
Your spending on "Cloud Infrastructure" is up <span className="text-blue-400 font-bold">12%</span> this month. We recommend reviewing your reserved instances.
39+
Your spending on &quot;Cloud Infrastructure&quot; is up <span className="text-blue-400 font-bold">12%</span> this month. We recommend reviewing your reserved instances.
4040
</p>
4141
<button className="text-sm font-bold text-blue-500 hover:text-white flex items-center space-x-2 transition-all">
4242
<span>View Recommendations</span>

frontend/app/dashboard/profile/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
'use client';
22

3-
import { useState, useEffect } from 'react';
3+
import React, { useState, useEffect } from 'react';
44
import { DashboardShell } from '@/components/layout/DashboardShell';
55
import { Button } from '@/components/ui/button';
66
import { Badge } from '@/components/ui/badge';
77
import { Input } from '@/components/ui/input';
88
import { Label } from '@/components/ui/label';
99
import {
10-
User, Mail, Phone, Building, Calendar,
11-
CreditCard, Camera, Save, X, Edit2, AlertCircle
10+
User, Building, Calendar,
11+
Camera, Save, X, Edit2, AlertCircle, Shield
1212
} from 'lucide-react';
1313
import { ProfileSkeleton } from '@/components/ui/skeleton';
1414
import { ErrorDisplay } from '@/components/ui/error';
@@ -97,7 +97,7 @@ export default function ProfilePage() {
9797
}
9898

9999
setIsSaving(true);
100-
const loadingToast = toast.loading('Saving profile changes...');
100+
toast.loading('Saving profile changes...');
101101

102102
try {
103103
const token = localStorage.getItem('token');

frontend/app/dashboard/settings/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ export default function SettingsPage() {
762762
<h3 className="font-bold">Important: Secure Your Key</h3>
763763
</div>
764764
<p className="text-xs text-amber-300/80">
765-
For security reasons, we can only show this key once. If you lose it, you'll need to generate a new one.
765+
For security reasons, we can only show this key once. If you lose it, you&apos;ll need to generate a new one.
766766
</p>
767767
<div className="flex gap-2">
768768
<input

frontend/app/dashboard/transactions/page.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ import {
1111
ArrowUpRight,
1212
Calendar,
1313
ChevronLeft,
14-
ChevronRight,
15-
Loader2
14+
ChevronRight
1615
} from 'lucide-react';
17-
import { motion, AnimatePresence } from 'framer-motion';
16+
import { motion } from 'framer-motion';
1817
import { cn } from '@/lib/utils';
1918

2019
interface Transaction {
@@ -29,7 +28,6 @@ interface Transaction {
2928

3029
export default function TransactionsPage() {
3130
const [transactions, setTransactions] = useState<Transaction[]>([]);
32-
const [loading, setLoading] = useState(true);
3331
const [searchTerm, setSearchTerm] = useState('');
3432

3533
useEffect(() => {
@@ -38,7 +36,6 @@ export default function TransactionsPage() {
3836

3937
const fetchTransactions = async () => {
4038
try {
41-
setLoading(true);
4239
const res = await api.get<any>('/transactions');
4340
if (res.success && res.history) {
4441
// Map backend response to frontend interface if needed
@@ -48,8 +45,6 @@ export default function TransactionsPage() {
4845
}
4946
} catch (error) {
5047
console.error(error);
51-
} finally {
52-
setLoading(false);
5348
}
5449
};
5550

frontend/app/login/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useState } from 'react';
3+
import React, { useState } from 'react';
44
import { api } from '@/lib/api';
55
import { useAuth } from '@/contexts/AuthContext';
66
import { Loader2 } from 'lucide-react';
@@ -125,8 +125,8 @@ export default function LoginPage() {
125125
</button>
126126
</form>
127127

128-
<p className="text-center text-sm text-slate-500">
129-
Don't have an account? <Link href="/register" className="text-blue-400 hover:underline">Apply now</Link>
128+
<p className="mt-2 text-center text-sm text-gray-600">
129+
Don&apos;t have an account?{' '}<Link href="/register" className="text-blue-400 hover:underline">Apply now</Link>
130130
</p>
131131
</div>
132132
</div>

frontend/components/funding/FundingWizard.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"use client";
22

33
import { useState } from "react";
4-
import { ArrowRight, Building2, CheckCircle, Globe, Loader2, ShieldCheck, Wallet } from "lucide-react";
4+
import { ArrowRight, Building2, CheckCircle, Globe, Loader2, ShieldCheck } from "lucide-react";
55
import { toast } from "sonner";
6-
import api from "@/lib/api"; // Assuming default export is axios instance or similar wrapper
6+
import { api } from "@/lib/api"; // Assuming default export is axios instance or similar wrapper
77

88
interface FundingSession {
99
type: string;
@@ -115,7 +115,7 @@ export function FundingWizard() {
115115
</div>
116116

117117
<div className="bg-blue-500/10 border border-blue-500/20 rounded-xl p-4 text-sm text-blue-300">
118-
<p>ℹ️ Since this is a specialized demo environment, clicking "Connect" will simulate the successful OAuth/SDK flow for {sessionData.provider}.</p>
118+
<p>ℹ️ Since this is a specialized demo environment, clicking &quot;Connect&quot; will simulate the successful OAuth/SDK flow for {sessionData.provider}.</p>
119119
</div>
120120

121121
<button
@@ -142,7 +142,7 @@ export function FundingWizard() {
142142
<CheckCircle className="w-8 h-8 text-white" />
143143
</div>
144144
<h2 className="text-2xl font-bold text-white">Account Connected!</h2>
145-
<p className="text-slate-400">Your funding source is active.</p>
145+
<p className="text-sm text-yellow-800">You are in Sandbox Mode. Use &quot;plaid_sandbox&quot; credentials.</p>
146146
</div>
147147

148148
<div className="bg-slate-800/50 border border-slate-700 rounded-xl p-6 space-y-4">

frontend/components/kyc/KYCWizard.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@
33
import { useState } from "react";
44
import { ArrowRight, CheckCircle, Fingerprint, Loader2, ScanFace, Shield, UploadCloud } from "lucide-react";
55
import { toast } from "sonner";
6-
import api from "@/lib/api";
7-
8-
interface VerificationSession {
9-
success: boolean;
10-
verification: any;
11-
sdkToken?: string;
12-
}
6+
import { api } from "@/lib/api";
137

148
export function KYCWizard() {
159
const [step, setStep] = useState<"intro" | "tier" | "processing" | "success">("intro");
@@ -56,7 +50,7 @@ export function KYCWizard() {
5650
setStep("success");
5751
toast.success("Identity Verified Successfully!");
5852

59-
} catch (err) {
53+
} catch {
6054
toast.error("Verification processing failed");
6155
} finally {
6256
setLoading(false);

0 commit comments

Comments
 (0)