Skip to content

Commit b553452

Browse files
committed
existing demo content
1 parent ed898a5 commit b553452

4 files changed

Lines changed: 388 additions & 180 deletions

File tree

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import { OpenInNew } from '@mui/icons-material';
2+
import {
3+
Box,
4+
TextField,
5+
Button,
6+
FormGroup,
7+
FormControlLabel,
8+
Checkbox,
9+
useTheme,
10+
Link,
11+
Container,
12+
FormControl,
13+
MenuItem,
14+
Select,
15+
SelectChangeEvent,
16+
} from '@mui/material';
17+
import SearchIcon from '@mui/icons-material/Search';
18+
import { useState } from 'react';
19+
20+
enum AuthTypeEnum {
21+
BASIC = 'Basic Auth',
22+
BEARER = 'Bearer Token',
23+
OAUTH = 'Oauth Client Credentials Grant',
24+
CUSTOM = 'Custom Headers (e.g. API Key)',
25+
}
26+
27+
export default function GbfsFeedSearchInput(): React.ReactElement {
28+
const theme = useTheme();
29+
const [autoDiscoveryUrlInput, setAutoDiscoveryUrlInput] =
30+
useState<string>('');
31+
const [requiresAuth, setRequiresAuth] = useState(false);
32+
const [authType, setAuthType] = useState<string>('');
33+
const [basicAuthUsername, setBasicAuthUsername] = useState<string>('');
34+
const [basicAuthPassword, setBasicAuthPassword] = useState<string>('');
35+
const [bearerAuthValue, setBearerAuthValue] = useState<string>('');
36+
const [oauthClientId, setOauthClientId] = useState<string>('');
37+
const [oauthClientSecret, setOauthClientSecret] = useState<string>('');
38+
const [oauthTokenUrl, setOauthTokenUrl] = useState<string>('');
39+
40+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
41+
setRequiresAuth(event.target.checked);
42+
};
43+
44+
const handleAuthTypeChange = (event: SelectChangeEvent<string>) => {
45+
setAuthType(event.target.value);
46+
setBasicAuthUsername('');
47+
setBasicAuthPassword('');
48+
setBearerAuthValue('');
49+
setOauthClientId('');
50+
setOauthClientSecret('');
51+
setOauthTokenUrl('');
52+
};
53+
54+
const isSubmitBoxDisabled = (): boolean => {
55+
if (!autoDiscoveryUrlInput) return true;
56+
if (requiresAuth) {
57+
if (!authType) return true;
58+
if (authType === AuthTypeEnum.BASIC) {
59+
if (!basicAuthUsername || !basicAuthPassword) return true;
60+
}
61+
if (authType === AuthTypeEnum.BEARER) {
62+
if (!bearerAuthValue) return true;
63+
}
64+
if (authType === AuthTypeEnum.OAUTH) {
65+
if (!oauthClientId || !oauthClientSecret || !oauthTokenUrl)
66+
return true;
67+
}
68+
}
69+
return false;
70+
};
71+
72+
const validateGBFSFeed = (): void => {
73+
// 1. dispatch action with url and auth details (state -> loading)
74+
// once done then
75+
// 2. navigate to /gbfs-validator?AutoDiscoveryUrl=url
76+
77+
// or
78+
79+
// navigate to /gbfs-validator?AutoDiscoveryUrl=url&auth details
80+
// store the auth details in context
81+
// let the GbfsValidator component handle the loading state
82+
}
83+
84+
return (
85+
<Box
86+
id='input-box'
87+
sx={{
88+
padding: 2,
89+
marginTop: 2,
90+
backgroundColor: theme.palette.background.default,
91+
border: '3px solid ' + theme.palette.text.primary,
92+
borderRadius: '5px',
93+
}}
94+
>
95+
<Container
96+
id='input-box-header'
97+
component={'form'}
98+
sx={{
99+
display: 'flex',
100+
justifyContent: 'space-between',
101+
alignItems: 'center',
102+
p: {xs: 0}
103+
}}
104+
>
105+
<TextField
106+
variant='outlined'
107+
placeholder='eg: https://example.com/gbfs.json'
108+
sx={{ width: '100%', mr: 2 }}
109+
onChange={(e) => setAutoDiscoveryUrlInput(e.target.value)}
110+
InputProps={{
111+
startAdornment: <SearchIcon sx={{mr:1}}></SearchIcon>,
112+
}}
113+
/>
114+
<Button
115+
variant='contained'
116+
color='primary'
117+
sx={{ p: '12px' }}
118+
component={Link}
119+
href={`/gbfs-validator?AutoDiscoveryUrl=${encodeURIComponent(
120+
autoDiscoveryUrlInput,
121+
)}`}
122+
disabled={isSubmitBoxDisabled()}
123+
type='submit'
124+
onClick={() => validateGBFSFeed()}
125+
>
126+
Validate
127+
</Button>
128+
</Container>
129+
<FormGroup>
130+
<FormControlLabel
131+
control={<Checkbox checked={requiresAuth} onChange={handleChange} />}
132+
label='Requires Authentication'
133+
/>
134+
{requiresAuth && (
135+
<FormControl>
136+
<Select
137+
value={authType}
138+
onChange={handleAuthTypeChange}
139+
displayEmpty
140+
inputProps={{ 'aria-label': 'Without label' }}
141+
>
142+
<MenuItem value={''}>
143+
<em>Select Authentication Type</em>
144+
</MenuItem>
145+
<MenuItem value={AuthTypeEnum.BASIC}>Basic Auth</MenuItem>
146+
<MenuItem value={AuthTypeEnum.BEARER}>Bearer Token</MenuItem>
147+
<MenuItem value={AuthTypeEnum.OAUTH}>Oauth Client Credentials Grant</MenuItem>
148+
</Select>
149+
</FormControl>
150+
)}
151+
152+
{requiresAuth && authType === AuthTypeEnum.BASIC && (
153+
<Box sx={{ display: 'flex', gap: 2, mt: 2, }}>
154+
<TextField
155+
size='small'
156+
variant='outlined'
157+
label='Username'
158+
placeholder='Enter Username'
159+
fullWidth
160+
onChange={(e) => setBasicAuthUsername(e.target.value)}
161+
/>
162+
<TextField
163+
size='small'
164+
variant='outlined'
165+
label='Password'
166+
placeholder='Enter Password'
167+
type='password'
168+
fullWidth
169+
onChange={(e) => setBasicAuthPassword(e.target.value)}
170+
/>
171+
</Box>
172+
)}
173+
174+
{requiresAuth && authType === AuthTypeEnum.BEARER && (
175+
<TextField
176+
size='small'
177+
variant='outlined'
178+
label='Token'
179+
placeholder='Enter Bearer Token'
180+
sx={{ mt: 2 }}
181+
fullWidth
182+
onChange={(e) => setBearerAuthValue(e.target.value)}
183+
184+
/>
185+
)}
186+
187+
{requiresAuth && authType === AuthTypeEnum.OAUTH && (
188+
<Box sx={{ display: 'flex', gap: 2, mt: 2, }}>
189+
<TextField
190+
size='small'
191+
variant='outlined'
192+
placeholder='Client Id'
193+
label='Client Id'
194+
fullWidth
195+
onChange={(e) => setOauthClientId(e.target.value)}
196+
/>
197+
<TextField
198+
size='small'
199+
variant='outlined'
200+
placeholder='Enter Client Secret'
201+
label='Client Secret'
202+
fullWidth
203+
onChange={(e) => setOauthClientSecret(e.target.value)}
204+
/>
205+
<TextField
206+
size='small'
207+
variant='outlined'
208+
placeholder='Enter Token Url'
209+
label='Token Url'
210+
fullWidth
211+
onChange={(e) => setOauthTokenUrl(e.target.value)}
212+
/>
213+
</Box>
214+
)}
215+
</FormGroup>
216+
<Box id='cta-buttons' sx={{ display: 'flex', gap: 2, mt: 2 }}>
217+
<Button variant='text' color='primary'>
218+
Broswe GBFS Feeds
219+
</Button>
220+
<Button variant='text' color='primary' endIcon={<OpenInNew />}>
221+
View GBFS Validator API Docs
222+
</Button>
223+
</Box>
224+
</Box>
225+
);
226+
}

web-app/src/app/screens/GbfsValidator/ValidationReport.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ export default function ValidationReport(): React.ReactElement {
4242
mb: 2,
4343
}}
4444
>
45-
<Typography variant='h4' sx={{ fontWeight: 700, mb: 2 }}>
46-
Validation Report
47-
</Typography>
48-
<Typography variant='body1' sx={{ fontWeight: 700, mb: 2, ml: 2 }}>
49-
https://tor.publicbikesystem.net/customer/gbfs/v2/gbfs.json
50-
</Typography>
51-
<Typography variant='body1' sx={{ fontWeight: 700, mb: 2, ml: 2 }}>
52-
Validator Version: {validationResult.summary.validatorVersion}
53-
</Typography>
54-
<Typography variant='body1' sx={{ fontWeight: 700, mb: 2, ml: 2, color: theme.palette.error.main }}>
55-
Invalid GBFS Feed
56-
</Typography>
5745
</Box>
5846

5947
<Box
@@ -78,7 +66,13 @@ export default function ValidationReport(): React.ReactElement {
7866
<Typography variant='h5' sx={{ fontWeight: 700, p: 2, mb: 2}}>
7967
GBFS Feed Files Summary
8068
</Typography>
81-
<Typography variant='h6' sx={{ mb: 1, ml: 2 }}>
69+
<Typography variant='body1' sx={{ fontWeight: 700, ml: 2 }}>
70+
Validator Version: {validationResult.summary.validatorVersion}
71+
</Typography>
72+
<Typography variant='body1' sx={{ fontWeight: 700, ml: 2, color: theme.palette.error.main }}>
73+
Invalid GBFS Feed
74+
</Typography>
75+
<Typography variant='h6' sx={{ mb: 1, ml: 2 }}>
8276
Total Errors: <b>3</b>
8377
</Typography>
8478

0 commit comments

Comments
 (0)