Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7d9e556
feat(gtfs): add initial CRUD for fare_products
josh-willis-arcadis Jan 28, 2025
e5e43a5
feat: add crud for rest of fares v2 tables
josh-willis-arcadis Jan 29, 2025
5d38fbb
Revert "feat: add crud for rest of fares v2 tables"
josh-willis-arcadis Jan 30, 2025
a9bedf7
add fare_media, fare_products fare_transfer_rule to sidebar to view data
josh-willis-arcadis Jan 30, 2025
6c88639
feat(fare_leg_rule): add table to editor
josh-willis-arcadis Feb 5, 2025
30cf528
feat(fare_leg_rules): add fare_leg_rules.txt data to editor
josh-willis-arcadis Feb 5, 2025
d6f5538
fix put/post
miles-grant-ibigroup Feb 6, 2025
2075474
clean up labels
miles-grant-ibigroup Feb 6, 2025
ba3e4a6
add text to faremedia dropdown
josh-willis-arcadis Feb 10, 2025
c35bb86
remove extra whitespace
josh-willis-arcadis Feb 10, 2025
17d7f94
add GTFS_FARE_MEDIA type
josh-willis-arcadis Feb 10, 2025
7aa1ce3
use correct text/value for fare_media type
josh-willis-arcadis Feb 10, 2025
5de97f5
add currency dropdown to fare_product
josh-willis-arcadis Feb 10, 2025
404900e
Revert "add GTFS_FARE_MEDIA type"
josh-willis-arcadis Feb 13, 2025
95c4dc6
add fare product input type
josh-willis-arcadis Apr 14, 2025
d09b416
display fare_product_ids in dropdown
josh-willis-arcadis Apr 24, 2025
d4465ac
start fare media dropdown
josh-willis-arcadis Apr 30, 2025
54dea04
add support for areas.txt
josh-willis-arcadis Jul 11, 2025
1e11700
add stop_areas to config file
josh-willis-arcadis Jul 11, 2025
d6dd259
fix area graphql call
josh-willis-arcadis Jul 14, 2025
850c833
load stop_areas data
josh-willis-arcadis Jul 15, 2025
76081ba
fix type issue
josh-willis-arcadis Jul 15, 2025
c64a033
add gtfs_area input type
josh-willis-arcadis Jul 24, 2025
0042661
start table select component
josh-willis-arcadis Jul 28, 2025
1eefea5
initial support for stop_area_ids on stop entities
josh-willis-arcadis Aug 7, 2025
84583ce
edit and delete stop_area_ids with new component
josh-willis-arcadis Aug 14, 2025
3d20a9a
use correct separator symbol
josh-willis-arcadis Aug 14, 2025
92a398b
clean up label and remove stops usage
josh-willis-arcadis Aug 14, 2025
9cd1bd9
add networks.txt to config file
josh-willis-arcadis Aug 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 156 additions & 1 deletion gtfs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,12 @@
text: Not possible (2)
columnWidth: 12
helpContent: "The wheelchair_boarding field identifies whether wheelchair boardings are possible from the specified stop or station. The field can have the following values:"

- name: "stop_area_ids"
required: false
inputType: GTFS_STOP_AREA
bulkEditEnabled: true
columnWidth: 12
helpContent: "The area_id field defines the area that this stop belongs to. This is used to group stops together in a logical way, such as all stops in a particular neighborhood or district. If this stop is not part of an area, leave this field blank."

- id: route
name: routes.txt
Expand Down Expand Up @@ -1017,3 +1022,153 @@
required: false
- name: added_service
required: false

# Fares v2

- id: fareproduct
name: fare_products.txt
helpContent: Used to describe the range of fares available for purchase by riders or taken into account when computing the total fare for journeys with multiple legs, such as transfer costs.
fields:
- name: fare_product_id
required: true
inputType: GTFS_ID
- name: fare_product_name
required: false
inputType: TEXT
# FARE-TODO: Add rider_category_id?
- name: fare_media_id
required: false
inputType: GTFS_FARE_MEDIA
- name: amount
inputType: NUMBER
required: true
- name: currency
required: true
inputType: DROPDOWN
bulkEditEnabled: true
options:
- value: USD
text: US dollar (USD)
- value: AUD
text: Australian dollar (AUD)
- value: CAD
text: Canadian dollar (CAD)
- value: CHF
text: Swiss franc (CHF)
- value: CNH
text: Chinese renminbi (CNH)
- value: EUR
text: Euro (EUR)
- value: GBP
text: Pound sterling (GBP)
- value: JPY
text: Japanese yen (JPY)
- value: MXN
text: Mexican peso (MXN)
- value: NZD
text: New Zealand dollar (NZD)
- value: SEK
text: Swedish krona (SEK)
- id: faremedia
name: fare_media.txt
helpContent: To describe the different fare media that can be employed to use fare products. Fare media are physical or virtual holders used for the representation and/or validation of a fare product.
fields:
- name: fare_media_id
required: true
inputType: GTFS_ID
- name: fare_media_name
inputType: TEXT
- name: fare_media_type
required: true
inputType: DROPDOWN
options:
- value: '0'
text: None (0)
- value: '1'
text: Physical paper ticket (1)
- value: '2'
text: Physical transit card (2)
- value: '3'
text: cEMV (3)
- value: '4'
text: Mobile app (4)
- id: faretransferrule
name: fare_transfer_rules.txt
helpContent: Fare rules for transfers between legs of travel defined in fare_leg_rules.txt.
fields:
- name: from_leg_group_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference fare_leg_rules
- name: to_leg_group_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference fare_leg_rules
- name: transfer_count
inputType: NUMBER # FARE-TODO: NON-ZERO INT
- name: duration_limit
inputType: POSITIVE_INT
- name: duration_limit_type
inputType: DROPDOWN
# FARE-TODO: Some help content?
options:
- value: '0'
- value: '1'
- value: '2'
- value: '3'
- name: fare_transfer_type
inputType: DROPDOWN
# FARE-TODO: Some help content?
options:
- value: '0'
- value: '1'
- value: '2'
- name: fare_product_id
required: false
inputType: GTFS_FARE_PRODUCT
- id: farelegrule
name: fare_leg_rules.txt
# FARE-TODO: Some help content?
fields:
- name: leg_group_id
required: false
inputType: TEXT # FARE-TODO: should this be text or id?
- name: network_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference networks.network_id or routes.network_id
- name: from_area_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference areas.area_id
- name: to_area_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference areas.area_id
- name: from_timeframe_group_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference timeframes.timeframe_group_id
- name: to_timeframe_group_id
required: false
inputType: TEXT # FARE-TODO: Needs to reference timeframes.timeframe_group_id
- name: fare_product_id
required: true
inputType: TEXT # FARE-TODO: Needs to reference fare_products.fare_product_id
- name: rule_priority
required: false
inputType: POSITIVE_INT
- id: area
name: areas.txt
helpContent: Areas that can be used to define fare zones.
fields:
- name: area_id
required: true
inputType: GTFS_ID
- name: area_name
required: false
inputType: TEXT
- id: network
name: networks.txt
helpContent: Networks that can be used to group routes and fare legs.
fields:
- name: network_id
required: true
inputType: GTFS_ID
- name: network_name
required: false
inputType: TEXT
25 changes: 25 additions & 0 deletions lib/editor/actions/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,11 @@ export function fetchBaseGtfs ({
agency_id
agency_name
}
area (limit: -1) {
id
area_id
area_name
}
calendar (limit: -1) {
id
service_id
Expand All @@ -498,6 +503,26 @@ export function fetchBaseGtfs ({
id
fare_id
}
fare_product (limit: -1) {
id
fare_product_id
fare_media_id
}
fare_media (limit: -1) {
id
fare_media_id
}
fare_transfer_rule (limit: -1) {
id
from_leg_group_id
to_leg_group_id
fare_product_id
}
fare_leg_rule (limit: -1) {
fare_product_id
leg_group_id
id
}
routes (limit: -1) {
id
route_id
Expand Down
55 changes: 55 additions & 0 deletions lib/editor/components/EditorInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import type {Entity, Feed, GtfsSpecField, GtfsAgency, GtfsStop} from '../../type
import type {EditorTables} from '../../types/reducers'

import ColorField from './ColorField'
import FareProductSelect from './FareProductSelect'
import StopAreasSelector from './StopAreasSelector'
import RouteTypeSelect from './RouteTypeSelect'
import VirtualizedEntitySelect from './VirtualizedEntitySelect'
import ZoneSelect from './ZoneSelect'
Expand Down Expand Up @@ -68,6 +70,7 @@ export default class EditorInput extends React.Component<Props> {
onChange,
updateActiveGtfsEntity
} = this.props
console.log('processing field change::::', field.name, val)
onChange && onChange(val)
updateActiveGtfsEntity && activeEntity && updateActiveGtfsEntity({
component: activeComponent,
Expand Down Expand Up @@ -109,6 +112,7 @@ export default class EditorInput extends React.Component<Props> {
}
}

/* eslint-disable complexity */
render () {
const {
activeEntity,
Expand Down Expand Up @@ -386,6 +390,57 @@ export default class EditorInput extends React.Component<Props> {
</FormGroup>
)
}
case 'GTFS_FARE_PRODUCT':
const fareProducts = getTableById(tableData, 'fareproduct').map(fareProduct => ({
value: fareProduct.fare_product_id,
label: `${fareProduct.fare_product_id} (${fareProduct.fare_media_id})`
}))
return (
<FormGroup {...formProps}>
{basicLabel}
<FareProductSelect
addCreateOption
onChange={this._onSelectChange}
value={currentValue}
options={fareProducts} />
</FormGroup>
)
case 'GTFS_FARE_MEDIA':
// FARE-TODO: media entities are not loaded in the editor until each fare_media entity is opened in the fare media tab.
// ex. go to products tab, select fare media, they will be undefined.
const fareMedia = getTableById(tableData, 'faremedia').map(fareMedia => ({
value: fareMedia.fare_media_id,
label: `${fareMedia.fare_media_name} (${fareMedia.fare_media_id})`
}))
return (
<FormGroup {...formProps}>
{basicLabel}
<Select
clearable
onChange={this._onSelectChange}
value={currentValue}
options={fareMedia} />
</FormGroup>
)
case 'GTFS_STOP_AREA':
const areas = getTableById(tableData, 'area')
let stopAreaIds: Array<string> = []
if (activeComponent === 'stop' &&
activeEntity &&
activeEntity.stop_area_ids &&
typeof activeEntity.stop_area_ids === 'string') {
stopAreaIds = activeEntity.stop_area_ids.split('§').filter(Boolean)
}
return (
<FormGroup {...formProps}>
{basicLabel}
<StopAreasSelector
currentValue={stopAreaIds}
areas={areas}
processFieldChange={this._processFieldChange}
/>
</FormGroup>
)
default:
return null
}
Expand Down
33 changes: 31 additions & 2 deletions lib/editor/components/EntityListSecondaryActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import React, {Component} from 'react'
import { Nav, NavItem } from 'react-bootstrap'

import * as activeActions from '../actions/active'
import VirtualizedEntitySelect from './VirtualizedEntitySelect'

import type {Entity, Feed} from '../../types'

import VirtualizedEntitySelect from './VirtualizedEntitySelect'
import VirtualizedTableSelect from './VirtualizedTableSelect'

type Props = {
activeComponent: string,
activeEntity: Entity,
Expand Down Expand Up @@ -97,6 +98,13 @@ export default class EntityListSecondaryActions extends Component<Props> {
}
}

_onTableSelect = (option: any) => {
console.log('onTableSelect::::::', option)
if (this.props.activeComponent !== option.value) {
this.props.setActiveEntity(this.props.feedSource.id, option.value)
}
}

render () {
const {
activeComponent,
Expand Down Expand Up @@ -135,6 +143,27 @@ export default class EntityListSecondaryActions extends Component<Props> {
entities={entities}
onChange={this._onChangeEntity} />
)
case 'fareproduct':
case 'faremedia':
case 'faretransferrule':
case 'farelegrule':
case 'area':
console.log('entities::::::', entities)
const faresv2Tables = [
'faremedia',
'fareproduct',
'faretransferrule',
'farelegrule',
'area'
]
return (
<VirtualizedTableSelect
value={activeEntity && activeEntity.id}
optionRenderer={this._optionRenderer}
component={activeComponent}
entities={faresv2Tables}
onChange={this._onTableSelect} />
)
default:
return null
}
Expand Down
51 changes: 51 additions & 0 deletions lib/editor/components/FareProductSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @flow

import React, {Component} from 'react'
import Select from 'react-select'

import type {ZoneOption} from '../../types'

type Props = {
addCreateOption?: boolean,
onChange: ?ZoneOption => void,
options: Array<ZoneOption>,
placeholder: string,
value: ?ZoneOption
}

type State = {
value: any
}

export default class FareProductSelect extends Component<Props, State> {
static defaultProps = {
placeholder: 'Select fare product ID...'
}

state = {
value: null
}

_onChange = (option: ZoneOption) => {
const value = option ? option.value : null
this.setState({value})
}

render () {
const {onChange, placeholder, value, options} = this.props
if (value && typeof value === 'string' && !options.find(option => option.value === value)) {
console.warn(`${value} not found in fare product options. Adding to options.`)
options.push({label: value, value})
}
return (
<Select
clearable
data-test-id='fare-product-selector'
noResultsText={`No fare products found.`}
onChange={onChange || this._onChange}
options={options}
placeholder={placeholder}
value={value || this.state.value} />
)
}
}
Loading
Loading