## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
84 lines
3.8 KiB
TypeScript
84 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { updateProfile, changePassword } from '@/app/actions/profile'
|
|
import { toast } from 'sonner'
|
|
|
|
export function ProfileForm({ user }: { user: any }) {
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Profile Information</CardTitle>
|
|
<CardDescription>Update your display name and other public information.</CardDescription>
|
|
</CardHeader>
|
|
<form action={async (formData) => {
|
|
const result = await updateProfile({ name: formData.get('name') as string })
|
|
if (result?.error) {
|
|
toast.error('Failed to update profile')
|
|
} else {
|
|
toast.success('Profile updated')
|
|
}
|
|
}}>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<label htmlFor="name" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Display Name</label>
|
|
<Input id="name" name="name" defaultValue={user.name} />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label htmlFor="email" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Email</label>
|
|
<Input id="email" value={user.email} disabled className="bg-muted" />
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button type="submit">Save Changes</Button>
|
|
</CardFooter>
|
|
</form>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Change Password</CardTitle>
|
|
<CardDescription>Update your password. You will need your current password.</CardDescription>
|
|
</CardHeader>
|
|
<form action={async (formData) => {
|
|
const result = await changePassword(formData)
|
|
if (result?.error) {
|
|
const msg = '_form' in result.error
|
|
? result.error._form[0]
|
|
: result.error.currentPassword?.[0] || result.error.newPassword?.[0] || result.error.confirmPassword?.[0] || 'Failed to change password'
|
|
toast.error(msg)
|
|
} else {
|
|
toast.success('Password changed successfully')
|
|
// Reset form manually or redirect
|
|
const form = document.querySelector('form#password-form') as HTMLFormElement
|
|
form?.reset()
|
|
}
|
|
}} id="password-form">
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<label htmlFor="currentPassword" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Current Password</label>
|
|
<Input id="currentPassword" name="currentPassword" type="password" required />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label htmlFor="newPassword" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">New Password</label>
|
|
<Input id="newPassword" name="newPassword" type="password" required minLength={6} />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label htmlFor="confirmPassword" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Confirm Password</label>
|
|
<Input id="confirmPassword" name="confirmPassword" type="password" required minLength={6} />
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button type="submit">Update Password</Button>
|
|
</CardFooter>
|
|
</form>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|