feat: applicant onboarding, bulk invite, team management enhancements
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m50s
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m50s
- Add nationality/institution fields to User model with migration - Applicant onboarding wizard (name, photo, nationality, country, institution, bio, project logo, preferences) - Project logo upload from applicant context with team membership verification - APPLICANT redirects in set-password, onboarding, and auth layout - Mask evaluation round names as "Evaluation Round 1/2/..." for applicants - Extend inviteTeamMember with nationality/country/institution/sendInvite fields - Admin getApplicants query with search/filter/pagination - Admin bulkInviteApplicants mutation with token generation and emails - Applicants tab on Members page with bulk select and floating invite bar Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,8 @@ import {
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import { CountrySelect } from '@/components/ui/country-select'
|
||||
import { Checkbox as CheckboxPrimitive } from '@/components/ui/checkbox'
|
||||
import {
|
||||
Users,
|
||||
UserPlus,
|
||||
@@ -64,6 +66,10 @@ const inviteSchema = z.object({
|
||||
email: z.string().email('Invalid email address'),
|
||||
role: z.enum(['MEMBER', 'ADVISOR']),
|
||||
title: z.string().optional(),
|
||||
nationality: z.string().optional(),
|
||||
country: z.string().optional(),
|
||||
institution: z.string().optional(),
|
||||
sendInvite: z.boolean().default(true),
|
||||
})
|
||||
|
||||
type InviteFormData = z.infer<typeof inviteSchema>
|
||||
@@ -129,6 +135,10 @@ export default function ApplicantTeamPage() {
|
||||
email: '',
|
||||
role: 'MEMBER',
|
||||
title: '',
|
||||
nationality: '',
|
||||
country: '',
|
||||
institution: '',
|
||||
sendInvite: true,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -280,6 +290,42 @@ export default function ApplicantTeamPage() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Nationality</Label>
|
||||
<CountrySelect
|
||||
value={form.watch('nationality') || ''}
|
||||
onChange={(v) => form.setValue('nationality', v)}
|
||||
placeholder="Select nationality"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Country of Residence</Label>
|
||||
<CountrySelect
|
||||
value={form.watch('country') || ''}
|
||||
onChange={(v) => form.setValue('country', v)}
|
||||
placeholder="Select country"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="institution">Institution (optional)</Label>
|
||||
<Input
|
||||
id="institution"
|
||||
placeholder="e.g., Ocean Research Institute"
|
||||
{...form.register('institution')}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckboxPrimitive
|
||||
id="sendInvite"
|
||||
checked={form.watch('sendInvite')}
|
||||
onCheckedChange={(checked) => form.setValue('sendInvite', !!checked)}
|
||||
/>
|
||||
<Label htmlFor="sendInvite" className="text-sm font-normal cursor-pointer">
|
||||
Send platform invite email
|
||||
</Label>
|
||||
</div>
|
||||
<div className="rounded-lg bg-muted/50 border p-3 text-sm">
|
||||
<p className="font-medium mb-1">What invited members can do:</p>
|
||||
<ul className="list-disc list-inside space-y-1 text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user