Add dynamic apply wizard customization with admin settings UI
- Create wizard config types, utilities, and defaults (wizard-config.ts) - Add admin apply settings page with drag-and-drop step ordering, dropdown option management, feature toggles, welcome message customization, and custom field builder with select/multiselect options editor - Build dynamic apply wizard component with animated step transitions, mobile-first responsive design, and config-driven form validation - Update step components to accept dynamic config (categories, ocean issues, field visibility, feature flags) - Replace hardcoded enum validation with string-based validation for admin-configurable dropdown values, with safe enum casting at storage layer - Add wizard template system (model, router, admin UI) with built-in MOPC Classic preset - Add program wizard config CRUD procedures to program router - Update application router getConfig to return wizardConfig, submit handler to store custom field data in metadataJson - Add edition-based apply page, project pool page, and supporting routers - Fix CSS (invalid sm:fixed-none), Enter key handler (skip textarea), safe area insets for notched phones, buildStepsArray field visibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -78,9 +78,21 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
id: projectId,
|
||||
})
|
||||
|
||||
// Fetch files
|
||||
// Fetch files (flat list for backward compatibility)
|
||||
const { data: files } = trpc.file.listByProject.useQuery({ projectId })
|
||||
|
||||
// Fetch grouped files by round (if project has a roundId)
|
||||
const { data: groupedFiles } = trpc.file.listByProjectForRound.useQuery(
|
||||
{ projectId, roundId: project?.roundId || '' },
|
||||
{ enabled: !!project?.roundId }
|
||||
)
|
||||
|
||||
// Fetch available rounds for upload selector (if project has a programId)
|
||||
const { data: rounds } = trpc.round.listByProgram.useQuery(
|
||||
{ programId: project?.programId || '' },
|
||||
{ enabled: !!project?.programId }
|
||||
)
|
||||
|
||||
// Fetch assignments
|
||||
const { data: assignments } = trpc.assignment.listByProject.useQuery({
|
||||
projectId,
|
||||
@@ -492,7 +504,9 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{files && files.length > 0 ? (
|
||||
{groupedFiles && groupedFiles.length > 0 ? (
|
||||
<FileViewer groupedFiles={groupedFiles} />
|
||||
) : files && files.length > 0 ? (
|
||||
<FileViewer
|
||||
projectId={projectId}
|
||||
files={files.map((f) => ({
|
||||
@@ -516,8 +530,13 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
<p className="text-sm font-medium mb-3">Upload New Files</p>
|
||||
<FileUpload
|
||||
projectId={projectId}
|
||||
roundId={project.roundId || undefined}
|
||||
availableRounds={rounds?.map((r: { id: string; name: string }) => ({ id: r.id, name: r.name }))}
|
||||
onUploadComplete={() => {
|
||||
utils.file.listByProject.invalidate({ projectId })
|
||||
if (project.roundId) {
|
||||
utils.file.listByProjectForRound.invalidate({ projectId, roundId: project.roundId })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user