Skip to content

Commit

Permalink
Implement form functionality, design & refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
khoidt committed Oct 2, 2023
1 parent 3be1327 commit 4d5ced6
Show file tree
Hide file tree
Showing 3 changed files with 344 additions and 86 deletions.
28 changes: 28 additions & 0 deletions src/chronology/ui/DateConverterForm.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.date_converter
padding-top: 50px
padding-bottom: 20px

.section-row
align-items: stretch
border-bottom: 1px grey solid
padding-top: 10px

.even
.section-title

.odd
.section-title

.section-row:last-child
border-bottom: none

.section-title
writing-mode: vertical-rl
transform: rotate(180deg)
text-align: center
font-family: Arial, Helvetica, sans-serif
font-size: 80%
letter-spacing: 1px

.section-fields
padding-left: 15px
269 changes: 183 additions & 86 deletions src/chronology/ui/DateConverterForm.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,200 @@
import React, { useState } from 'react'
import {
Form,
FormGroup,
FormControl,
Row,
Col,
FormCheck,
FormLabel,
Popover,
} from 'react-bootstrap'
import DateConverter from 'chronology/domain/DateConverter'
import HelpTrigger from 'common/HelpTrigger'
import { sections, Field } from 'chronology/ui/DateConverterFormFieldData'
import './DateConverterForm.sass'
import { Markdown } from 'common/Markdown'

const fields = [
{ name: 'year', type: 'number', placeholder: 'Year', required: true },
{ name: 'bcYear', type: 'number', placeholder: 'BC Year' },
{ name: 'month', type: 'number', placeholder: 'Month', required: true },
{ name: 'day', type: 'number', placeholder: 'Day', required: true },
{ name: 'weekDay', type: 'number', placeholder: 'Week Day', required: true },
{ name: 'cjdn', type: 'number', placeholder: 'CJDN', required: true },
{
name: 'lunationNabonassar',
type: 'number',
placeholder: 'Lunation Nabonassar',
required: true,
},
{
name: 'seBabylonianYear',
type: 'number',
placeholder: 'SE Babylonian Year',
required: true,
},
{
name: 'seMacedonianYear',
type: 'number',
placeholder: 'SE Macedonian Year',
},
{ name: 'seArsacidYear', type: 'number', placeholder: 'SE Arsacid Year' },
{
name: 'mesopotamianMonth',
type: 'number',
placeholder: 'Mesopotamian Month',
required: true,
},
{ name: 'mesopotamianDay', type: 'number', placeholder: 'Mesopotamian Day' },
{
name: 'mesopotamianMonthLength',
type: 'number',
placeholder: 'Mesopotamian Month Length',
},
{ name: 'ruler', type: 'text', placeholder: 'Ruler' },
{ name: 'regnalYear', type: 'number', placeholder: 'Regnal Year' },
]
const description = `The project uses a date converter that is based on the converter developed by [Robert H. van Gent](https://webspace.science.uu.nl/~gent0113/babylon/babycal_converter.htm).
The form below presents a dedicated interface designed for users who need to convert dates between different ancient calendar systems. Users can choose from three different input scenarios for conversion:
- **Modern Date**: For conversion related to contemporary dates.
- **Seleucid (Babylonian) Date**: For dates in the Seleucid or Babylonian calendar.
- **Nabonassar Date**: For dates in the Nabonassar calendar system.
Each section of the form is dynamically updating based on the selected scenario. Fields that are relevant to the chosen scenario are highlighted for user convenience.`

function DateForm(): JSX.Element {
const initialFormData = fields.reduce((acc, field) => {
acc[field.name] = ''
return acc
}, {})
const dateConverter = new DateConverter()
const [formData, setFormData] = useState(dateConverter.calendar)
const [scenario, setScenario] = useState('setToModernDate')

/* ToDo:
Implement.
1. Switch between scenarios.
2. When a scenario is selected, only the inputs that are relevant should remain active.
const handleChange = (event) => {
const { name, value } = event.target
const _formData = { ...formData, [name]: value }
const {
year,
month,
day,
ruler,
regnalYear,
seBabylonianYear,
mesopotamianMonth,
mesopotamianDay,
} = _formData
const setSeBabylonianDateArgs = [
seBabylonianYear,
mesopotamianMonth,
mesopotamianDay,
]
const setMesopotamianDateArgs = [
ruler,
regnalYear,
mesopotamianMonth,
mesopotamianDay,
]
if (scenario === 'setToModernDate') {
dateConverter.setToModernDate(
year as number,
month as number,
day as number
)
} else if (
scenario === 'setSeBabylonianDate' &&
!setSeBabylonianDateArgs.includes(undefined)
) {
dateConverter.setSeBabylonianDate(
seBabylonianYear as number,
mesopotamianMonth as number,
mesopotamianDay as number
)
} else if (
scenario === 'setMesopotamianDate' &&
!setMesopotamianDateArgs.includes(undefined)
) {
dateConverter.setMesopotamianDate(
ruler as string,
regnalYear as number,
mesopotamianMonth as number,
mesopotamianDay as number
)
}
setFormData({ ...dateConverter.calendar })
}

Scenarios:
* setToModernDate(year: number, month: number, day: number)
* setSeBabylonianDate(
seBabylonianYear: number,
mesopotamianMonth: number,
mesopotamianDay: number
)
* setMesopotamianDate(
ruler: string,
regnalYear: number,
mesopotamianMonth: number,
mesopotamianDay: number
)
const handleScenarioChange = (_scenario) => {
setScenario(_scenario)
}

*/
const fieldIsActive = (fieldName) => {
switch (scenario) {
case 'setToModernDate':
return ['year', 'month', 'day'].includes(fieldName)
case 'setSeBabylonianDate':
return [
'seBabylonianYear',
'mesopotamianMonth',
'mesopotamianDay',
].includes(fieldName)
case 'setMesopotamianDate':
return [
'ruler',
'regnalYear',
'mesopotamianMonth',
'mesopotamianDay',
].includes(fieldName)
default:
return false
}
}
const activeStyle = { backgroundColor: '#f9ffcf' }

const [formData, setFormData] = useState(initialFormData)
function getField(field: Field, index: number): JSX.Element {
return (
<Col xs={12} sm={12} md={6} lg={6} key={index}>
<FormGroup>
<FormLabel htmlFor={field.name}>{field.placeholder}</FormLabel>{' '}
<HelpTrigger
overlay={
<Popover id={field.name} title={field.name}>
<Popover.Content>{field.help}</Popover.Content>
</Popover>
}
/>
<FormControl
id={field.name}
name={field.name}
type={field.type}
value={formData[field.name] || ''}
onChange={handleChange}
placeholder={field.placeholder}
disabled={!fieldIsActive(field.name)}
required={field.required && fieldIsActive(field.name)}
style={fieldIsActive(field.name) ? activeStyle : {}}
/>
</FormGroup>
</Col>
)
}

function getSection(
title: string,
fields: Field[],
index: number
): JSX.Element {
return (
<Row className={`section-row ${index % 2 === 0 ? 'even' : 'odd'}`}>
<Col md={1} className="section-title">
<div>{title}</div>
</Col>
<Col md={11} className="section-fields">
<Row>
{fields.map((field, fieldIndex) => getField(field, fieldIndex))}
</Row>
</Col>
</Row>
)
}

const handleChange = (e) => {
const { name, value } = e.target
setFormData((prevState) => ({ ...prevState, [name]: value }))
const scenarioLabels = {
setToModernDate: 'Modern date',
setSeBabylonianDate: 'Seleucid (Babylonian) date',
setMesopotamianDate: 'Nabonassar date',
}

const handleSubmit = (e) => {
e.preventDefault()
console.log(formData)
function getControls(): JSX.Element {
return (
<aside className="main__sidebar">
<FormGroup>
<h4>Input</h4>
{Object.keys(scenarioLabels).map((_scenario) => (
<FormCheck
type="radio"
label={scenarioLabels[_scenario]}
key={_scenario}
checked={scenario === _scenario}
onChange={() => handleScenarioChange(_scenario)}
/>
))}
</FormGroup>
</aside>
)
}

return (
<form onSubmit={handleSubmit}>
{fields.map((field) => (
<input
key={field.name}
name={field.name}
type={field.type}
value={formData[field.name]}
onChange={handleChange}
placeholder={field.placeholder}
required={field.required}
/>
))}
<button type="submit">Submit</button>
</form>
<>
<Markdown text={description} />
<Row className="date_converter">
<Col md={8}>
<Form>
{sections.map(({ title, fields }, index) =>
getSection(title, fields, index)
)}
</Form>
</Col>
<Col md={4}>{getControls()}</Col>
</Row>
</>
)
}

Expand Down
Loading

0 comments on commit 4d5ced6

Please sign in to comment.