Skip to content

Commit

Permalink
Merge pull request #30 from PoulavBhowmick03/main
Browse files Browse the repository at this point in the history
Added a settings page, settings route with REST functionality and migrated the db
  • Loading branch information
mo-jaber authored Nov 2, 2024
2 parents 9a9eb5e + 373fb80 commit a3e31a4
Show file tree
Hide file tree
Showing 16 changed files with 763 additions and 61 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,25 @@ bash
npx knex migrate:latest
```

This will apply all pending migrations to your local database. You can revert changes using:
## Error running the migrations

In case you encounter an error during the migration process, do the following steps:

1. Run the following command to source the env variables:

```sh
bash
source .env
```
2. Run the migration scripts again

3. If this doesn't work, run the following command:

```sh
POSTGRES_URL="..." pnpm knex migrate:latest
```

This will apply all pending migrations to your local database. You can revert changes using:
```sh
npx knex migrate:rollback
```

Expand Down
7 changes: 6 additions & 1 deletion app/(dashboard)/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
'use client';

import { TooltipProvider } from '@/components/ui/tooltip';
import { ClerkProvider } from '@clerk/nextjs';

export default function Providers({ children }: { children: React.ReactNode }) {
return <TooltipProvider>{children}</TooltipProvider>;
return (
<ClerkProvider>
<TooltipProvider>{children}</TooltipProvider>
</ClerkProvider>
);
}
201 changes: 185 additions & 16 deletions app/(dashboard)/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,188 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '@/components/ui/card';
'use client'

import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useUser } from '@clerk/nextjs'
import axios from 'axios'
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { AlertCircle, User, Lock, Trash2 } from 'lucide-react'
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"

interface UserData {
id: number
username: string
email: string
theme: string
role: string
}

export default function SettingsPage() {
const { user } = useUser()
const router = useRouter()

const [userData, setUserData] = useState<UserData | null>(null)
const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | null>(null)

useEffect(() => {
const fetchUserData = async () => {
try {
const response = await axios.get('/api/settings')
setUserData(response.data.data)
} catch (err: any) {
setError(err.response?.data?.error || 'Failed to fetch user data')
} finally {
setLoading(false)
}
}

fetchUserData()
}, [])

const handleUpdateProfile = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setError(null)
const form = e.currentTarget
const formData = new FormData(form)
const username = formData.get('username') as string
const email = formData.get('email') as string

try {
const response = await axios.put('/api/settings', { username, email })
setUserData((prev) => prev ? { ...prev, username, email } : prev)
alert(response.data.message)
} catch (err: any) {
setError(err.response?.data?.error || 'Failed to update profile')
}
}

const handleChangePassword = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setError(null)
const form = e.currentTarget
const formData = new FormData(form)
const currentPassword = formData.get('currentPassword') as string
const newPassword = formData.get('newPassword') as string

try {
const response = await axios.post('/api/settings', { currentPassword, newPassword })
alert(response.data.message)
form.reset()
} catch (err: any) {
setError(err.response?.data?.error || 'Failed to change password')
}
}

const handleDeleteAccount = async () => {
const confirmDelete = confirm('Are you sure you want to delete your account? This action cannot be undone.')
if (!confirmDelete) return

const password = prompt('Please enter your password to confirm deletion:')
if (!password) {
alert('Password is required to delete your account.')
return
}

try {
const response = await axios.delete('/api/settings', { data: { password } })
alert(response.data.message)
router.push('/') // Redirect to homepage after deletion
} catch (err: any) {
setError(err.response?.data?.error || 'Failed to delete account')
}
}

if (loading) return <div className="flex justify-center items-center h-screen">Loading...</div>
if (!userData) return <div className="flex justify-center items-center h-screen">No user data available.</div>

return (
<Card>
<CardHeader>
<CardTitle>Settings</CardTitle>
<CardDescription>Settings items.</CardDescription>
</CardHeader>
<CardContent></CardContent>
</Card>
);
}
<div className="container mx-auto p-4 max-w-4xl">
<h1 className="text-3xl font-bold mb-6">Account Settings</h1>

{error && (
<Alert variant="destructive" className="mb-6">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
)}

<Tabs defaultValue="profile" className="space-y-6">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
<TabsTrigger value="danger">Danger Zone</TabsTrigger>
</TabsList>

<TabsContent value="profile">
<Card>
<CardHeader>
<CardTitle>Profile Information</CardTitle>
<CardDescription>Update your account profile information here.</CardDescription>
</CardHeader>
<form onSubmit={handleUpdateProfile}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="username">Username</Label>
<Input id="username" name="username" defaultValue={userData.username} required />
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" name="email" type="email" defaultValue={userData.email} required />
</div>
</CardContent>
<CardFooter>
<Button type="submit">Update Profile</Button>
</CardFooter>
</form>
</Card>
</TabsContent>

<TabsContent value="security">
<Card>
<CardHeader>
<CardTitle>Change Password</CardTitle>
<CardDescription>Ensure your account is using a long, random password to stay secure.</CardDescription>
</CardHeader>
<form onSubmit={handleChangePassword}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="currentPassword">Current Password</Label>
<Input id="currentPassword" name="currentPassword" type="password" required />
</div>
<div className="space-y-2">
<Label htmlFor="newPassword">New Password</Label>
<Input id="newPassword" name="newPassword" type="password" required />
</div>
</CardContent>
<CardFooter>
<Button type="submit">Change Password</Button>
</CardFooter>
</form>
</Card>
</TabsContent>

<TabsContent value="danger">
<Card>
<CardHeader>
<CardTitle>Delete Account</CardTitle>
<CardDescription>Once you delete your account, there is no going back. Please be certain.</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4">
Deleting your account will remove all of your information from our database. This cannot be undone.
</p>
<Button variant="destructive" onClick={handleDeleteAccount}>
<Trash2 className="mr-2 h-4 w-4" /> Delete Account
</Button>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
)
}
4 changes: 3 additions & 1 deletion app/(dashboard)/user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export async function User() {
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>
<Link href="/settings">Settings</Link>
</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
{user ? (
Expand Down
Loading

0 comments on commit a3e31a4

Please sign in to comment.