feat: forgot password flow, member page fixes, country name display
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m7s
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m7s
Password reset: - /forgot-password page: enter email, receive reset link via email - /reset-password?token=xxx page: set new password with validation - user.requestPasswordReset: generates token, sends styled email - user.resetPassword: validates token, hashes new password - Does NOT trigger re-onboarding — only resets the password - 30-minute token expiry, cleared after use - Added passwordResetToken/passwordResetExpiresAt to User model Member detail page fixes: - Hide "Expertise & Capacity" card for applicants/audience roles - Show country names with flag emojis instead of raw ISO codes - Login "Forgot password?" now links to /forgot-password page Project detail page: - Team member details show full country names with flags Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,10 +62,10 @@ import {
|
||||
ThumbsDown,
|
||||
Globe,
|
||||
Building2,
|
||||
Flag,
|
||||
FileText,
|
||||
FolderOpen,
|
||||
} from 'lucide-react'
|
||||
import { getCountryName, getCountryFlag } from '@/lib/countries'
|
||||
|
||||
export default function MemberDetailPage() {
|
||||
const params = useParams()
|
||||
@@ -266,19 +266,19 @@ export default function MemberDetailPage() {
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{user.nationality && (
|
||||
<div className="flex items-start gap-2">
|
||||
<Flag className="h-4 w-4 mt-0.5 text-muted-foreground shrink-0" />
|
||||
<span className="text-lg mt-0.5 shrink-0" role="img">{getCountryFlag(user.nationality)}</span>
|
||||
<div>
|
||||
<p className="text-xs font-medium text-muted-foreground">Nationality</p>
|
||||
<p className="text-sm">{user.nationality}</p>
|
||||
<p className="text-sm">{getCountryName(user.nationality)}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{user.country && (
|
||||
<div className="flex items-start gap-2">
|
||||
<Globe className="h-4 w-4 mt-0.5 text-muted-foreground shrink-0" />
|
||||
<span className="text-lg mt-0.5 shrink-0" role="img">{getCountryFlag(user.country)}</span>
|
||||
<div>
|
||||
<p className="text-xs font-medium text-muted-foreground">Country of Residence</p>
|
||||
<p className="text-sm">{user.country}</p>
|
||||
<p className="text-sm">{getCountryName(user.country)}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -447,7 +447,8 @@ export default function MemberDetailPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Expertise & Capacity */}
|
||||
{/* Expertise & Capacity — only for jury/mentor/observer/admin roles */}
|
||||
{!['APPLICANT', 'AUDIENCE'].includes(user.role) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
@@ -494,6 +495,7 @@ export default function MemberDetailPage() {
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Mentor Assignments Section */}
|
||||
|
||||
@@ -77,6 +77,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { formatDateOnly } from '@/lib/utils'
|
||||
import { getCountryName, getCountryFlag } from '@/lib/countries'
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>
|
||||
@@ -517,7 +518,11 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
const isLastLead =
|
||||
member.role === 'LEAD' &&
|
||||
project.teamMembers.filter((m: { role: string }) => m.role === 'LEAD').length <= 1
|
||||
const details = [member.user.nationality, member.user.institution, member.user.country].filter(Boolean)
|
||||
const details = [
|
||||
member.user.nationality ? `${getCountryFlag(member.user.nationality)} ${getCountryName(member.user.nationality)}` : null,
|
||||
member.user.institution,
|
||||
member.user.country && member.user.country !== member.user.nationality ? `${getCountryFlag(member.user.country)} ${getCountryName(member.user.country)}` : null,
|
||||
].filter(Boolean)
|
||||
return (
|
||||
<div key={member.id} className="flex items-center gap-3 p-3 rounded-lg border">
|
||||
{member.role === 'LEAD' ? (
|
||||
|
||||
Reference in New Issue
Block a user