Skip to content

Commit 37e9f10

Browse files
committed
add sign in/sign up page along with supabase connection and reusable components for further development
1 parent af3c157 commit 37e9f10

33 files changed

+7518
-1
lines changed

.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env*.local
29+
.env
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

README.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,51 @@
1-
"# flow-builder"
1+
"Flow Builder
2+
3+
A simple app built with Next.js 14, TypeScript, Tailwind CSS, and Supabase.
4+
5+
## Features
6+
7+
- ✅ User Sign Up
8+
- ✅ User Sign In
9+
- ✅ User Sign Out
10+
- ✅ Supabase for authentication backend
11+
12+
## Tech Stack
13+
14+
- **Frontend Framework**: Next.js 14 (App Router)
15+
- **Language**: TypeScript
16+
- **Styling**: Tailwind CSS
17+
- **Authentication**: Supabase Auth
18+
- **Package Manager**: npm
19+
20+
## Getting Started
21+
22+
### Prerequisites
23+
24+
- Node.js 18+ installed
25+
- A Supabase account (free tier works fine)
26+
27+
### 1. Clone and Install
28+
29+
```bash
30+
# Install dependencies
31+
npm install
32+
```
33+
34+
### 4. Run the Development Server
35+
36+
```bash
37+
npm run dev
38+
```
39+
40+
Open [http://localhost:3000](http://localhost:3000) in your browser.
41+
42+
For my cute devs:
43+
By default, Supabase requires email confirmation. To disable this for development:
44+
45+
1. Go to your Supabase project
46+
2. Navigate to Authentication → Settings
47+
3. Disable "Enable email confirmations"
48+
49+
## License
50+
51+
MIT"

app/api/health/route.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
3+
/**
4+
* Example API route
5+
* GET /api/health - Check if the API is running
6+
*/
7+
export async function GET(request: NextRequest) {
8+
return NextResponse.json({
9+
status: 'ok',
10+
timestamp: new Date().toISOString(),
11+
message: 'API is running',
12+
});
13+
}

app/api/user/profile/route.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import { supabase } from '@/lib/supabase';
3+
4+
/**
5+
* Example protected API route
6+
* GET /api/user/profile - Get current user profile
7+
*/
8+
export async function GET(request: NextRequest) {
9+
try {
10+
// Get the authorization header
11+
const token = request.headers.get('authorization')?.replace('Bearer ', '');
12+
13+
if (!token) {
14+
return NextResponse.json(
15+
{ error: 'No authorization token provided' },
16+
{ status: 401 }
17+
);
18+
}
19+
20+
// Verify the user with Supabase
21+
const { data: { user }, error } = await supabase.auth.getUser(token);
22+
23+
if (error || !user) {
24+
return NextResponse.json(
25+
{ error: 'Invalid or expired token' },
26+
{ status: 401 }
27+
);
28+
}
29+
30+
// Return user data
31+
return NextResponse.json({
32+
id: user.id,
33+
email: user.email,
34+
created_at: user.created_at,
35+
});
36+
} catch (error) {
37+
console.error('Profile API error:', error);
38+
return NextResponse.json(
39+
{ error: 'Internal server error' },
40+
{ status: 500 }
41+
);
42+
}
43+
}
44+
45+
/**
46+
* PATCH /api/user/profile - Update user profile
47+
*/
48+
export async function PATCH(request: NextRequest) {
49+
try {
50+
const token = request.headers.get('authorization')?.replace('Bearer ', '');
51+
52+
if (!token) {
53+
return NextResponse.json(
54+
{ error: 'No authorization token provided' },
55+
{ status: 401 }
56+
);
57+
}
58+
59+
const { data: { user }, error: authError } = await supabase.auth.getUser(token);
60+
61+
if (authError || !user) {
62+
return NextResponse.json(
63+
{ error: 'Invalid or expired token' },
64+
{ status: 401 }
65+
);
66+
}
67+
68+
// Get the update data from request body
69+
const updates = await request.json();
70+
71+
// Update user metadata (example)
72+
const { data, error } = await supabase.auth.updateUser({
73+
data: updates,
74+
});
75+
76+
if (error) {
77+
return NextResponse.json(
78+
{ error: error.message },
79+
{ status: 400 }
80+
);
81+
}
82+
83+
return NextResponse.json({
84+
message: 'Profile updated successfully',
85+
user: data.user,
86+
});
87+
} catch (error) {
88+
console.error('Profile update error:', error);
89+
return NextResponse.json(
90+
{ error: 'Internal server error' },
91+
{ status: 500 }
92+
);
93+
}
94+
}

app/auth/signin/page.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import { useRouter } from 'next/navigation';
5+
import { useAuth } from '@/contexts';
6+
import { Button, Input, Card, CardHeader, CardContent, CardTitle } from '@/components/ui';
7+
import { Alert } from '@/components/ui/Alert';
8+
import Link from 'next/link';
9+
10+
export default function SignIn() {
11+
const [email, setEmail] = useState('');
12+
const [password, setPassword] = useState('');
13+
const [localError, setLocalError] = useState('');
14+
const router = useRouter();
15+
const { signIn } = useAuth();
16+
const [isLoading, setIsLoading] = useState(false);
17+
18+
const handleSignIn = async (e: React.FormEvent) => {
19+
e.preventDefault();
20+
setLocalError('');
21+
setIsLoading(true);
22+
23+
try {
24+
await signIn(email, password);
25+
router.push('/dashboard');
26+
} catch (error: any) {
27+
setLocalError(error.message || 'An error occurred during sign in');
28+
} finally {
29+
setIsLoading(false);
30+
}
31+
};
32+
33+
return (
34+
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 p-4">
35+
<Card className="w-full max-w-md">
36+
<CardHeader>
37+
<CardTitle>Sign In</CardTitle>
38+
</CardHeader>
39+
40+
<CardContent>
41+
{localError && <Alert variant="error" message={localError} />}
42+
43+
<form onSubmit={handleSignIn} className="space-y-4">
44+
<Input
45+
id="email"
46+
type="email"
47+
label="Email"
48+
value={email}
49+
onChange={(e) => setEmail(e.target.value)}
50+
placeholder="[email protected]"
51+
required
52+
/>
53+
54+
<Input
55+
id="password"
56+
type="password"
57+
label="Password"
58+
value={password}
59+
onChange={(e) => setPassword(e.target.value)}
60+
placeholder="••••••••"
61+
required
62+
/>
63+
64+
<Button
65+
type="submit"
66+
variant="primary"
67+
className="w-full"
68+
isLoading={isLoading}
69+
>
70+
Sign In
71+
</Button>
72+
</form>
73+
74+
<p className="mt-6 text-center text-gray-600">
75+
Don't have an account?{' '}
76+
<Link href="/auth/signup" className="text-indigo-600 hover:text-indigo-800 font-medium">
77+
Sign Up
78+
</Link>
79+
</p>
80+
81+
<p className="mt-2 text-center">
82+
<Link href="/" className="text-sm text-gray-500 hover:text-gray-700">
83+
← Back to home
84+
</Link>
85+
</p>
86+
</CardContent>
87+
</Card>
88+
</div>
89+
);
90+
}

0 commit comments

Comments
 (0)