-
Notifications
You must be signed in to change notification settings - Fork 0
feat: gtfs feature tracker + updated header / footer section #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
ffb4887
trip planner images
Alessandro100 cd41fde
new header which includes tools
Alessandro100 f1931ad
gtfs feature tracker
Alessandro100 a566cf9
Potential fix for pull request finding
Alessandro100 ed6003e
fix: use locale-aware Link for GTFS Feature Tracker in mobile drawer
Copilot cc27a4d
fix: use aria-hidden on decorative placeholder box in GtfsFeatureTracker
Copilot 287bb30
Potential fix for pull request finding
Alessandro100 da8422d
error handling on csv pull
Alessandro100 0613623
translation keys
Alessandro100 5dd0c91
lint fix
Alessandro100 f3d0cc5
stronger messaging for single selected feature
Alessandro100 ec335a3
feature tracker button position updated
Alessandro100 a8e44ec
update comment
Alessandro100 ec63120
style update
Alessandro100 b473ac3
extra protection logic feeds search
Alessandro100 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
src/app/[locale]/gtfs-feature-tracker/components/GtfsFeatureTracker.helpers.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| /* eslint-disable */ | ||
| import type { ReactElement } from 'react'; | ||
| import CheckCircleIcon from '@mui/icons-material/CheckCircle'; | ||
| import CancelIcon from '@mui/icons-material/Cancel'; | ||
| import InfoIcon from '@mui/icons-material/Info'; | ||
| import AutorenewIcon from '@mui/icons-material/Autorenew'; | ||
| import RemoveIcon from '@mui/icons-material/Remove'; | ||
| import type { | ||
| Feature, | ||
| Consumer, | ||
| MdLinkToken, | ||
| UrlToken, | ||
| FileToken, | ||
| FieldToken, | ||
| Token, | ||
| } from './types'; | ||
|
|
||
| // Many of these helper functions are based on parsing freeform text from the CSV, so they include some normalization and best-effort handling of unexpected values | ||
| // Once the data will come from the database with a well-defined schema, some of these parsing/normalization functions can be simplified or removed | ||
|
|
||
| export function getStatusText(raw: string): string { | ||
| const n = raw.toLowerCase().trim(); | ||
| if (n.startsWith('yes - for every feed')) return 'Every feed'; | ||
| if (n.startsWith('yes - for some feeds')) return 'Some feeds'; | ||
| if (n.startsWith('yes')) return 'Yes'; | ||
| if (n.startsWith('no')) return 'No'; | ||
| if (n === 'integration planned') return 'Planned'; | ||
| if (n === 'test in progress') return 'Test in progress'; | ||
| if (n === 'partial integration') return 'Partial integration'; | ||
| if (n === 'some fields are ignored') return 'Some fields ignored'; | ||
| return raw || 'Unknown'; | ||
| } | ||
|
|
||
| export function getStatusIcon(raw: string): ReactElement { | ||
| const n = raw.toLowerCase().trim(); | ||
| if (n.startsWith('yes')) | ||
| return <CheckCircleIcon color='success' sx={{ fontSize: 20 }} />; | ||
| if (n.startsWith('no')) | ||
| return <CancelIcon color='error' sx={{ fontSize: 20 }} />; | ||
| if (n === 'integration planned') | ||
| return <InfoIcon color='info' sx={{ fontSize: 20 }} />; | ||
| if (n === 'test in progress') | ||
| return <AutorenewIcon color='warning' sx={{ fontSize: 20 }} />; | ||
| if (n === 'partial integration') | ||
| return <InfoIcon color='warning' sx={{ fontSize: 20 }} />; | ||
| if (n === 'some fields are ignored') | ||
| return <InfoIcon color='info' sx={{ fontSize: 20 }} />; | ||
| return <RemoveIcon sx={{ fontSize: 20, color: 'text.disabled' }} />; | ||
| } | ||
|
|
||
| export function isStatusSupported(raw: string): boolean { | ||
| return raw.toLowerCase().trim().startsWith('yes'); | ||
| } | ||
|
|
||
| export function computeCategoryProgress( | ||
| features: Feature[], | ||
| consumers: Consumer[], | ||
| ): number { | ||
| let supported = 0; | ||
| let total = 0; | ||
| for (const feature of features) { | ||
| for (const consumer of consumers) { | ||
| const raw = feature.support[consumer.id]?.rawStatus ?? ''; | ||
| if (raw) { | ||
| total++; | ||
| if (isStatusSupported(raw)) supported++; | ||
| } | ||
| } | ||
| } | ||
| return total > 0 ? Math.round((supported / total) * 100) : 0; | ||
| } | ||
|
|
||
| export function formatDate(dateStr: string): string { | ||
| if (!dateStr) return ''; | ||
| const d = new Date(dateStr); | ||
| if (isNaN(d.getTime())) return dateStr; | ||
| return d.toLocaleDateString('en-US', { | ||
| year: 'numeric', | ||
| month: 'short', | ||
| day: 'numeric', | ||
| }); | ||
|
Comment on lines
+73
to
+81
|
||
| } | ||
|
|
||
| export function tokenizeDetail( | ||
| text: string, | ||
| knownFieldsSet: Set<string>, | ||
| ): Token[] { | ||
| if (!text) return []; | ||
| const tokens: Array<MdLinkToken | UrlToken | FileToken | FieldToken> = []; | ||
|
|
||
| // 1. Markdown links [label](url) | ||
| const mdRe = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g; | ||
| let m: RegExpExecArray | null; | ||
| while ((m = mdRe.exec(text)) !== null) { | ||
| tokens.push({ | ||
| type: 'mdlink', | ||
| label: m[1], | ||
| url: m[2], | ||
| start: m.index, | ||
| end: m.index + m[0].length, | ||
| }); | ||
| } | ||
|
|
||
| // 2. Bare URLs | ||
| const urlRe = /https?:\/\/[^\s,)]+/g; | ||
| while ((m = urlRe.exec(text)) !== null) { | ||
| const overlaps = tokens.some( | ||
| (t) => m!.index >= t.start && m!.index < t.end, | ||
| ); | ||
| if (!overlaps) | ||
| tokens.push({ | ||
| type: 'url', | ||
| value: m[0], | ||
| start: m.index, | ||
| end: m.index + m[0].length, | ||
| }); | ||
| } | ||
|
|
||
| // 3. .txt file names | ||
| const fileRe = /\b[a-z_]+\.txt\b/g; | ||
| while ((m = fileRe.exec(text)) !== null) { | ||
| const overlaps = tokens.some( | ||
| (t) => m!.index >= t.start && m!.index < t.end, | ||
| ); | ||
| if (!overlaps) | ||
| tokens.push({ | ||
| type: 'file', | ||
| value: m[0], | ||
| start: m.index, | ||
| end: m.index + m[0].length, | ||
| }); | ||
| } | ||
|
|
||
| // 4. Known GTFS field names | ||
| const fieldRe = /\b[a-z][a-z0-9_]*[a-z0-9]\b|\b[a-z]{2,}\b/g; | ||
| while ((m = fieldRe.exec(text)) !== null) { | ||
| if (!knownFieldsSet.has(m[0])) continue; | ||
| const overlaps = tokens.some( | ||
| (t) => m!.index >= t.start && m!.index < t.end, | ||
| ); | ||
| if (!overlaps) | ||
| tokens.push({ | ||
| type: 'field', | ||
| value: m[0], | ||
| start: m.index, | ||
| end: m.index + m[0].length, | ||
| }); | ||
| } | ||
|
|
||
| tokens.sort((a, b) => a.start - b.start); | ||
|
|
||
| const segments: Token[] = []; | ||
| let cursor = 0; | ||
| for (const tok of tokens) { | ||
| if (tok.start > cursor) | ||
| segments.push({ type: 'text', value: text.slice(cursor, tok.start) }); | ||
| segments.push(tok); | ||
| cursor = tok.end; | ||
| } | ||
| if (cursor < text.length) | ||
| segments.push({ type: 'text', value: text.slice(cursor) }); | ||
| return segments; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.