Skip to content

Commit 117d5bd

Browse files
authored
Merge pull request #391 from bruin-tennis-consulting/matchFormUpdate
Updated upload-match.js
2 parents 3558577 + 6124ea0 commit 117d5bd

4 files changed

Lines changed: 383 additions & 62 deletions

File tree

app/styles/Upload.module.css

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,47 @@
2121
.form {
2222
display: flex;
2323
flex-direction: column;
24+
gap: 0.75rem;
25+
padding: 1.5rem 1.75rem;
26+
border: 1px solid #ddd;
27+
border-radius: 8px;
28+
background-color: #ffffff;
29+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
30+
max-width: 720px;
2431
}
2532

26-
.form label select,
27-
.form label input {
28-
margin-left: 0.5rem; /* Adjust the margin-left as needed */
33+
/* Shared control styling: all text-like inputs match react-select (RJSF: label and input are siblings, not nested) */
34+
.form input:not([type='checkbox']):not([type='radio']) {
35+
min-height: 32px;
36+
padding: 4px 10px;
37+
border: 1px solid #ccc;
38+
border-radius: 4px;
39+
font-size: 0.95rem;
40+
margin-left: 0.5rem;
41+
box-sizing: border-box;
42+
}
43+
44+
.form input:not([type='checkbox']):not([type='radio']):focus {
45+
outline: none;
46+
border-color: #2684ff;
47+
box-shadow: 0 0 0 1px #2684ff;
48+
}
49+
50+
.form select {
51+
margin-left: 0.5rem;
52+
}
53+
54+
/* react-select: match text inputs and integrate with form layout */
55+
.form :global(.rjsf-select__control) {
56+
min-height: 32px;
57+
border: 1px solid #ccc;
58+
border-radius: 4px;
59+
font-size: 0.95rem;
60+
}
61+
62+
.form :global(.rjsf-select__control--is-focused) {
63+
border-color: #2684ff;
64+
box-shadow: 0 0 0 1px #2684ff;
2965
}
3066

3167
.errorContainer {

app/upload-match/page.js

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'
44
import Form from '@rjsf/core'
55
import validator from '@rjsf/validator-ajv8'
66
import { dataURItoBlob } from '@rjsf/utils'
7+
import ReactSelect from 'react-select'
78

89
import getTeams from '@/app/services/getTeams.js'
910
import { searchableProperties } from '@/app/services/searchableProperties.js'
@@ -18,6 +19,55 @@ import {
1819
uiSchema as baseUiSchema
1920
} from '@/app/services/matchSchemas.js'
2021

22+
/** SelectWidget wrapping react-select for enum fields. Replaces native <select>. */
23+
function ReactSelectWidget({
24+
id,
25+
value,
26+
options = {},
27+
required,
28+
disabled,
29+
readonly,
30+
multiple = false,
31+
onChange,
32+
placeholder = 'Select...'
33+
}) {
34+
const { enumOptions = [] } = options
35+
const opts = Array.isArray(enumOptions) ? enumOptions : []
36+
37+
const getSelectValue = () => {
38+
if (multiple) {
39+
const arr = Array.isArray(value) ? value : value != null ? [value] : []
40+
return arr.map((v) => opts.find((o) => o.value === v)).filter(Boolean)
41+
}
42+
if (value == null || value === '') return null
43+
return opts.find((o) => o.value === value) ?? null
44+
}
45+
46+
const handleChange = (selected) => {
47+
if (multiple) {
48+
const arr = Array.isArray(selected) ? selected : []
49+
onChange(arr.map((o) => o.value))
50+
} else {
51+
onChange(selected ? selected.value : undefined)
52+
}
53+
}
54+
55+
return (
56+
<ReactSelect
57+
inputId={id}
58+
value={getSelectValue()}
59+
options={opts}
60+
onChange={handleChange}
61+
isMulti={multiple}
62+
isDisabled={disabled || readonly}
63+
isClearable={!required}
64+
placeholder={placeholder}
65+
classNamePrefix="rjsf-select"
66+
menuPosition="fixed"
67+
/>
68+
)
69+
}
70+
2171
export default function UploadMatchForm() {
2272
const setCollections = useState([])[1]
2373

@@ -30,15 +80,29 @@ export default function UploadMatchForm() {
3080
const [localUiSchema, setLocalUiSchema] = useState(baseUiSchema)
3181
const [uploadProgress, setUploadProgress] = useState(0)
3282
const [isUploading, setIsUploading] = useState(false)
83+
const [mounted, setMounted] = useState(false)
3384

3485
const { userProfile } = useAuth()
3586

87+
useEffect(() => {
88+
setMounted(true)
89+
}, [])
90+
3691
useEffect(() => {
3792
const fetchCollectionsAndTeams = async () => {
3893
try {
3994
const allTeams = await getTeams()
95+
96+
const uclaTeams = allTeams.filter(
97+
// Dropdown for only UCLA teams (filter)
98+
(team) =>
99+
team.name.includes('University of California, Los Angeles') &&
100+
(team.name.endsWith('(M)') || team.name.endsWith('(W)'))
101+
)
102+
40103
setTeams(allTeams)
41104
const teamNames = allTeams.map((team) => team.name)
105+
const uclaNames = uclaTeams.map((team) => team.name)
42106

43107
// Assuming userProfile.collections contains the collection names
44108
const userCollections = userProfile?.collections || []
@@ -51,7 +115,7 @@ export default function UploadMatchForm() {
51115
...prevSchema.properties,
52116
clientTeam: {
53117
...prevSchema.properties.clientTeam,
54-
enum: teamNames
118+
enum: uclaNames
55119
},
56120
opponentTeam: {
57121
...prevSchema.properties.opponentTeam,
@@ -293,6 +357,20 @@ export default function UploadMatchForm() {
293357
}
294358
}
295359

360+
if (!mounted) {
361+
return (
362+
<div className={styles.container}>
363+
<div>
364+
<h1 className={styles.title}>Upload Match</h1>
365+
<h3>
366+
Make sure you add the player in &apos;Upload Team&apos; before this!
367+
</h3>
368+
<p style={{ marginTop: '1rem', color: '#666' }}>Loading form...</p>
369+
</div>
370+
</div>
371+
)
372+
}
373+
296374
return (
297375
<div className={styles.container}>
298376
<div>
@@ -302,13 +380,15 @@ export default function UploadMatchForm() {
302380
</h3>
303381

304382
<Form
383+
className={styles.form}
305384
schema={schema}
306385
uiSchema={localUiSchema}
307386
formData={formData}
308387
onChange={handleChange}
309388
onSubmit={handleSubmit}
310389
validator={validator}
311390
disabled={isUploading}
391+
widgets={{ SelectWidget: ReactSelectWidget }}
312392
/>
313393

314394
{isUploading && (

0 commit comments

Comments
 (0)