diff --git a/app/components/TaskRow.tsx b/app/components/TaskRow.tsx index 10d1b5a..db07607 100644 --- a/app/components/TaskRow.tsx +++ b/app/components/TaskRow.tsx @@ -142,7 +142,7 @@ const ErrorList = ({ errors }: any) => { color="error" /> @@ -155,7 +155,7 @@ const ErrorList = ({ errors }: any) => { ); }; -const TaskTableRow = ({ validation }: any) => { +const TaskTableRow = ({ session, name: taskName, validation }: any) => { const { name, status, valid, errors } = validation; const [open, setOpen] = React.useState(false); @@ -265,7 +265,7 @@ const TaskRow = ({ session, task }: TaskRowProps) => { - { validations.map(v => ) } + { validations.map(v => ) } diff --git a/app/components/ValidationConfig.tsx b/app/components/ValidationConfig.tsx index 3800aaf..121f8bb 100644 --- a/app/components/ValidationConfig.tsx +++ b/app/components/ValidationConfig.tsx @@ -52,7 +52,7 @@ const ruleOptions = [ label: 'Every stop place is referenced', }, { - value: 'everyStopPointHaveAnArrivalAndDepartureTime', + value: 'everyStopPointHaveArrivalAndDepartureTime', label: 'Every stop point have an arrival and departure time', }, { @@ -90,14 +90,14 @@ type ValidationConfigProps = { const ValidationConfig = (props: ValidationConfigProps) => { const { session, onValidate } = props; - const [schema, setSchema] = React.useState('netex'); + const [schema, setSchema] = React.useState('netex@1.2'); const [rules, setRules] = React.useState([ 'everyLineIsReferenced', 'everyScheduledStopPointHasAName', 'everyStopPlaceHasACorrectStopPlaceType', 'everyStopPlaceHasAName', 'everyStopPlaceIsReferenced', - 'everyStopPointHaveAnArrivalAndDepartureTime', + 'everyStopPointHaveArrivalAndDepartureTime', 'frameDefaultsHaveALocaleAndTimeZone', 'locationsAreReferencingTheSamePoint', 'passingTimesHaveIncreasingTimes', @@ -180,6 +180,12 @@ const ValidationConfig = (props: ValidationConfigProps) => { } } + React.useState(() => { + if (session.files.length) { + setCanValidate(true); + } + }, [session, setCanValidate]); + return ( @@ -204,9 +210,10 @@ const ValidationConfig = (props: ValidationConfigProps) => { value={schema} onChange={handleSelectChange} > - NeTEx - NeTEx Light - EPIP + NeTEx (v1.2) + NeTEx Light (v1.2) + EPIP (v1.1.1) + EPIP Light (v1.1.1) diff --git a/app/components/ValidationResult.tsx b/app/components/ValidationResult.tsx index 6209c3a..192aba7 100644 --- a/app/components/ValidationResult.tsx +++ b/app/components/ValidationResult.tsx @@ -1,5 +1,5 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import { Box, Button, ButtonGroup, Grid, Menu, MenuItem, Skeleton, Stack, Typography } from '@mui/material'; +import { Box, Button, ButtonGroup, Divider, Grid, Menu, MenuItem, Skeleton, Stack, Typography } from '@mui/material'; import Link from 'next/link'; import { useRouter } from 'next/router'; import React from 'react'; @@ -32,7 +32,7 @@ type ValidationResultProps = { const ValidationResult = (props: ValidationResultProps) => { const { session } = props; const router = useRouter(); - const message = useSubscription(session ? `progress/${session.id}` : ''); + const documentStatus = useSubscription(session ? `sessions/${session.id}/documents/+` : ''); const [tasks, setTasks] = React.useState([]); const { setSession } = useSessionStore(); const [ errorOpen, setErrorOpen ] = React.useState(false); @@ -41,6 +41,28 @@ const ValidationResult = (props: ValidationResultProps) => { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); + React.useEffect(() => { + if (!documentStatus) { + return; + } + + const data = documentStatus.d; + const taskIndex = tasks.findIndex(t => t.originalName === data.document); + + if (taskIndex === -1) { + setTasks([ + ...tasks, + { + name: truncName(data.document), + originalName: data.document, + valid: false, + status: documentStatus.t === 'VALIDATE_DOCUMENT_START' ? 'running' : 'complete', + validations: [], + } + ].sort((a: any, b: any) => a.name > b.name ? 1 : -1)); + } + }, [documentStatus]); + const handleValidateAnother = () => { apiClient.createSession() .then(session => { @@ -65,47 +87,30 @@ const ValidationResult = (props: ValidationResultProps) => { }; React.useEffect(() => { - if (!session) { + if (!session || !session.results) { return; } - if (session.status !== 'running') { - const tasks = session.results.map(v => { - return { - name: truncName(v.name), - originalName: v.name, + const tasks = session.results.map(v => { + const running = v.validations.find(v => !v.valid && !v.errors); + + return { + name: truncName(v.name), + originalName: v.name, + valid: v.valid, + status: running ? 'running' : 'complete', + validations: v.validations.map((v: any) => ({ + name: v.name, valid: v.valid, - status: 'complete', - validations: v.validations.map((v: any) => ({ - name: v.name, - valid: v.valid, - errors: v.errors || [], - })), - } - }) - .sort((a, b) => a.name > b.name ? 1 : -1); - - setTasks(tasks); - } else if (message) { - const tasks = message.map((p: any) => { - return { - name: truncName(p.name), - originalName: p.name, - valid: p.status === 'valid', - status: p.status === 'running' ? 'running' : 'complete', - validations: Object.keys(p.jobStatus).map((k) => ({ - name: k, - valid: p.jobStatus[k] === 'valid', - status: p.jobStatus[k], - errors: [], - })), - }; - }) - .sort((a: any, b: any) => a.name > b.name ? 1 : -1) + status: running ? 'running' : 'complete', + errors: v.errors || [], + })), + } + }) + .sort((a, b) => a.name > b.name ? 1 : -1); - setTasks(tasks); - } - }, [message, session]); + setTasks(tasks); + }, [session]); return ( @@ -135,6 +140,7 @@ const ValidationResult = (props: ValidationResultProps) => { )} + diff --git a/app/hooks/useMqttClient.ts b/app/hooks/useMqttClient.ts index 9a300a2..b46a448 100644 --- a/app/hooks/useMqttClient.ts +++ b/app/hooks/useMqttClient.ts @@ -11,8 +11,13 @@ const useMqttClient = () => { export const useSubscription = (topic: string) => { const [message, setMessage] = React.useState(null); + const pattern = new RegExp(`^${topic.replace(/[+]/g, '[^\\/]+').replace(/[#]/g, '.+')}$`); const handler = React.useCallback((topic: string, payload: any) => { + if (!topic.match(pattern)) { + return; + } + try { setMessage(JSON.parse(payload.toString())); } catch (err) { @@ -21,7 +26,7 @@ export const useSubscription = (topic: string) => { }, [topic]); React.useEffect(() => { - mqttClient.subscribe(topic); // TODO unsubscribe + mqttClient.subscribe(topic); mqttClient.on('message', handler); return () => { diff --git a/app/pages/index.tsx b/app/pages/index.tsx index caedb37..47d0227 100644 --- a/app/pages/index.tsx +++ b/app/pages/index.tsx @@ -49,7 +49,7 @@ const Home: NextPage = () => { return ( - Greenlight | NeTEx validation + NeTEx validation | Greenlight