-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 009904c
Showing
44 changed files
with
7,708 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# editorconfig.org | ||
root = true | ||
|
||
[*] | ||
charset = utf-8 | ||
end_of_line = lf | ||
indent_size = 2 | ||
indent_style = space | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
dist/* | ||
.cache | ||
public | ||
node_modules | ||
*.esm.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/eslintrc", | ||
"root": true, | ||
"extends": [ | ||
"next/core-web-vitals", | ||
"prettier", | ||
"plugin:tailwindcss/recommended" | ||
], | ||
"plugins": ["tailwindcss"], | ||
"rules": { | ||
"@next/next/no-html-link-for-pages": "off", | ||
"react/jsx-key": "off", | ||
"tailwindcss/no-custom-classname": "off" | ||
}, | ||
"settings": { | ||
"tailwindcss": { | ||
"callees": ["cn"], | ||
"config": "tailwind.config.js" | ||
}, | ||
"next": { | ||
"rootDir": ["./"] | ||
} | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": ["*.ts", "*.tsx"], | ||
"parser": "@typescript-eslint/parser" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
node_modules | ||
.pnp | ||
.pnp.js | ||
|
||
# testing | ||
coverage | ||
|
||
# next.js | ||
.next/ | ||
out/ | ||
build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
.pnpm-debug.log* | ||
|
||
# local env files | ||
.env.local | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
|
||
# turbo | ||
.turbo | ||
|
||
.contentlayer | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
cache | ||
.cache | ||
package.json | ||
package-lock.json | ||
public | ||
CHANGELOG.md | ||
.yarn | ||
dist | ||
node_modules | ||
.next | ||
build | ||
.contentlayer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"typescript.tsdk": "../../node_modules/.pnpm/[email protected]/node_modules/typescript/lib", | ||
"typescript.enablePromptUseWorkspaceTsdk": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# next-template | ||
|
||
A Next.js 13 template for building apps with Radix UI and Tailwind CSS. | ||
|
||
## Usage | ||
|
||
```bash | ||
npx create-next-app -e https://github.com/shadcn/next-template | ||
``` | ||
|
||
## Features | ||
|
||
- Next.js 13 App Directory | ||
- Radix UI Primitives | ||
- Tailwind CSS | ||
- Icons from [Lucide](https://lucide.dev) | ||
- Dark mode with `next-themes` | ||
- Tailwind CSS class sorting, merging and linting. | ||
|
||
## License | ||
|
||
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
'use client' | ||
|
||
import { useEffect, useState } from 'react' | ||
import { useRouter } from 'next/navigation' | ||
import { useToast } from '@/hooks/use-toast' | ||
|
||
export default function AuthCallback() { | ||
const router = useRouter() | ||
const { toast } = useToast() | ||
const [isProcessing, setIsProcessing] = useState(true) | ||
|
||
useEffect(() => { | ||
const handleCallback = async () => { | ||
try { | ||
const params = new URLSearchParams(window.location.search) | ||
|
||
// Check for direct token response first (implicit flow) | ||
const accessToken = params.get('access_token') | ||
const idToken = params.get('id_token') | ||
|
||
if (accessToken) { | ||
// We received tokens directly | ||
localStorage.setItem('access_token', accessToken) | ||
if (idToken) localStorage.setItem('id_token', idToken) | ||
|
||
// Fetch user info | ||
const userResponse = await fetch(`${process.env.NEXT_PUBLIC_PEHCHAN_URL}/api/auth/userinfo`, { | ||
headers: { | ||
'Authorization': `Bearer ${accessToken}` | ||
} | ||
}) | ||
|
||
if (!userResponse.ok) { | ||
throw new Error('Failed to fetch user info') | ||
} | ||
|
||
const userInfo = await userResponse.json() | ||
localStorage.setItem('user_info', JSON.stringify(userInfo)) | ||
|
||
toast({ | ||
title: "Login successful", | ||
description: "Welcome to FBR Tax Portal" | ||
}) | ||
|
||
router.push('/dashboard') | ||
return | ||
} | ||
|
||
// If no direct tokens, check for authorization code flow | ||
const code = params.get('code') | ||
const state = params.get('state') | ||
const error = params.get('error') | ||
const storedState = sessionStorage.getItem('auth_state') | ||
|
||
// Check for errors | ||
if (error) { | ||
throw new Error(error) | ||
} | ||
|
||
// For code flow | ||
if (code) { | ||
// Validate state if present | ||
if (state && state !== storedState) { | ||
throw new Error('Invalid state parameter') | ||
} | ||
|
||
// Exchange code for tokens | ||
const tokenResponse = await fetch(`${process.env.NEXT_PUBLIC_PEHCHAN_URL}/api/token`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
code, | ||
client_id: process.env.NEXT_PUBLIC_CLIENT_ID, | ||
redirect_uri: `${window.location.origin}/auth/callback`, | ||
grant_type: 'authorization_code', | ||
}), | ||
}) | ||
|
||
if (!tokenResponse.ok) { | ||
throw new Error('Token exchange failed') | ||
} | ||
|
||
const tokens = await tokenResponse.json() | ||
|
||
// Store tokens and proceed as before | ||
localStorage.setItem('access_token', tokens.access_token) | ||
localStorage.setItem('id_token', tokens.id_token) | ||
|
||
// Fetch user info | ||
const userResponse = await fetch(`${process.env.NEXT_PUBLIC_PEHCHAN_URL}/api/auth/userinfo`, { | ||
headers: { | ||
'Authorization': `Bearer ${tokens.access_token}` | ||
} | ||
}) | ||
|
||
if (!userResponse.ok) { | ||
throw new Error('Failed to fetch user info') | ||
} | ||
|
||
const userInfo = await userResponse.json() | ||
localStorage.setItem('user_info', JSON.stringify(userInfo)) | ||
|
||
toast({ | ||
title: "Login successful", | ||
description: "Welcome to FBR Tax Portal" | ||
}) | ||
|
||
router.push('/dashboard') | ||
return | ||
} | ||
|
||
// If we get here, no valid authentication data was received | ||
throw new Error('No valid authentication data received') | ||
|
||
} catch (error) { | ||
console.error('Auth callback error:', error) | ||
toast({ | ||
variant: "destructive", | ||
title: "Authentication Failed", | ||
description: error instanceof Error ? error.message : "An error occurred during login" | ||
}) | ||
router.push('/') | ||
} finally { | ||
sessionStorage.removeItem('auth_state') | ||
setIsProcessing(false) | ||
} | ||
} | ||
|
||
handleCallback() | ||
}, [router, toast]) | ||
|
||
if (isProcessing) { | ||
return ( | ||
<div className="min-h-screen flex items-center justify-center"> | ||
<div className="text-center"> | ||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900 mx-auto mb-4"></div> | ||
<p className="text-gray-600">Processing your login...</p> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
return null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
'use client' | ||
|
||
import { useEffect, useState } from 'react' | ||
import { useRouter } from 'next/navigation' | ||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" | ||
import { Button } from "@/components/ui/button" | ||
import { useToast } from '@/hooks/use-toast' | ||
|
||
interface UserInfo { | ||
sub: string | ||
email: string | ||
name: string | ||
profile?: { | ||
cnic: string | ||
phone: string | ||
} | ||
} | ||
|
||
export default function Dashboard() { | ||
const [userInfo, setUserInfo] = useState<UserInfo | null>(null) | ||
const router = useRouter() | ||
const { toast } = useToast() | ||
|
||
useEffect(() => { | ||
// Check authentication | ||
const accessToken = localStorage.getItem('access_token') | ||
const storedUserInfo = localStorage.getItem('user_info') | ||
|
||
if (!accessToken || !storedUserInfo) { | ||
router.push('/') | ||
return | ||
} | ||
|
||
setUserInfo(JSON.parse(storedUserInfo)) | ||
}, [router]) | ||
|
||
const handleLogout = () => { | ||
localStorage.clear() | ||
toast({ | ||
title: "Logged out", | ||
description: "You have been successfully logged out" | ||
}) | ||
router.push('/') | ||
} | ||
|
||
if (!userInfo) { | ||
return null | ||
} | ||
|
||
return ( | ||
<div className="max-w-4xl mx-auto p-6"> | ||
<Card className="mb-6"> | ||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> | ||
<CardTitle className="text-2xl font-bold">FBR Tax Portal</CardTitle> | ||
<Button | ||
variant="destructive" | ||
onClick={handleLogout} | ||
> | ||
Logout | ||
</Button> | ||
</CardHeader> | ||
</Card> | ||
|
||
<div className="grid md:grid-cols-2 gap-6"> | ||
<Card> | ||
<CardHeader> | ||
<CardTitle>User Information</CardTitle> | ||
</CardHeader> | ||
<CardContent className="space-y-2"> | ||
<div className="grid grid-cols-3 gap-4"> | ||
<span className="font-semibold">Name:</span> | ||
<span className="col-span-2">{userInfo.name}</span> | ||
</div> | ||
<div className="grid grid-cols-3 gap-4"> | ||
<span className="font-semibold">Email:</span> | ||
<span className="col-span-2">{userInfo.email}</span> | ||
</div> | ||
<div className="grid grid-cols-3 gap-4"> | ||
<span className="font-semibold">CNIC:</span> | ||
<span className="col-span-2">{userInfo.profile?.cnic}</span> | ||
</div> | ||
<div className="grid grid-cols-3 gap-4"> | ||
<span className="font-semibold">Phone:</span> | ||
<span className="col-span-2">{userInfo.profile?.phone}</span> | ||
</div> | ||
</CardContent> | ||
</Card> | ||
|
||
<Card> | ||
<CardHeader> | ||
<CardTitle>Tax Filing Status</CardTitle> | ||
</CardHeader> | ||
<CardContent> | ||
<p className="text-sm text-gray-600 mb-4"> | ||
You can now proceed with your tax filing. Your information has been pre-filled from your Pehchan ID. | ||
</p> | ||
<Button className="w-full">Start Tax Filing</Button> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.