Skip to content
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

OR-40 and OR-WFHDC Form Models and React pages #1216

Draft
wants to merge 41 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e26f7ee
Generate base versions of form models
richmoni Jan 16, 2022
67801e9
Merge pull request #1 from or-40-development/make-or-form-models
richmoni Jan 16, 2022
621b2ef
Merge branch 'ustaxes:master' into master
richmoni Jan 23, 2022
0a9f865
Merge branch 'ustaxes:master' into master
richmoni Jan 29, 2022
d94e569
Implement base form models for Form OR-40-N and OR-40-P and add form …
richmoni Jan 29, 2022
efd55e7
Implement base form models for Schedule OR-ASC and OR-ASC-NP and add …
richmoni Jan 30, 2022
37e237c
Merge pull request #2 from or-40-development/or-40-np
richmoni Jan 30, 2022
8d7c86f
Start naming fields for OR-40
mrbluesky2 Jan 30, 2022
16cfb66
Named initial fields, defined some.
mrbluesky2 Jan 30, 2022
b89bfb3
Merge branch 'ustaxes:master' into master
richmoni Feb 8, 2022
d33f6b3
all the bool values are named
mrbluesky2 Feb 9, 2022
5c2fe7c
Fix ESLint warnings in 2021 OR form models
richmoni Feb 13, 2022
0207462
Merge pull request #3 from or-40-development/fix-eslint-warnings
richmoni Feb 13, 2022
1daae4a
radio button names updated
mrbluesky2 Feb 18, 2022
99eed39
formatting options for the radio group
mrbluesky2 Feb 18, 2022
6c61fcd
Logic for filling out radio group fields on the pdf
mrbluesky2 Feb 18, 2022
48ba455
Implement OR-40 form model fields for pages 2, 5, and 6
richmoni Feb 28, 2022
be48f07
Initial OR-WFHDC Form
AnthonyTrinh8 Mar 9, 2022
9deaab3
Readd Deleted Menu Components
AnthonyTrinh8 Mar 9, 2022
abb7a45
Merge branch 'ustaxes:master' into master
richmoni Apr 1, 2022
5309157
Merge branch 'ustaxes:master' into master
richmoni Apr 7, 2022
bde4527
Merge pull request #5 from or-40-development/react-form-fields
richmoni Apr 17, 2022
1c7beb4
Merge branch 'ustaxes:master' into master
richmoni Apr 30, 2022
b2f9e98
Add state questions React form, interface, and Redux action for getti…
richmoni Apr 30, 2022
c4c47d9
Merge branch 'ustaxes:master' into master
richmoni May 13, 2022
39cff7a
Add more OR state questions for pages 2, 5, and 6
richmoni May 14, 2022
e89c059
Merge branch 'ustaxes:master' into master
richmoni May 18, 2022
d08b94a
Add more OR state questions
richmoni May 18, 2022
093e2e2
Merge branch 'ustaxes:master' into master
richmoni May 20, 2022
87968ae
Merge branch 'master' into naming-form-model-fields
richmoni May 20, 2022
2d70305
Merge branch 'naming-form-model-fields' of https://github.com/or-40-d…
richmoni May 21, 2022
f538ba7
Finish implementing pages 2 & 5 of OR-40
richmoni May 21, 2022
c70aab5
Finish implementing page 4 of OR-40
AnthonyTrinh8 May 26, 2022
8aec09f
changed a tags to Link's to utilize the material-ui styling for dark …
mrbluesky2 May 27, 2022
a9083db
page 1 fields of the OR-40 form model are implemented
mrbluesky2 May 27, 2022
17fa1fa
Merge branch 'naming-form-model-fields' of https://github.com/or-40-d…
mrbluesky2 May 27, 2022
2abdda8
Merge branch 'ustaxes:master' into naming-form-model-fields
mrbluesky2 May 28, 2022
784b850
Remove unused imports from OR40 form model
richmoni May 28, 2022
2c6791c
Fix undefined state for StateQuestions
AnthonyTrinh8 May 28, 2022
51508f6
Merge branch 'naming-form-model-fields' of github.com:or-40-developme…
AnthonyTrinh8 May 28, 2022
403ce6a
Fix Menu Component. Change OR-40 Amount Fields to use Currency Pattern
AnthonyTrinh8 May 28, 2022
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
7 changes: 6 additions & 1 deletion src/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import Questions from './Questions'
import HelpAndFeedback from './HelpAndFeedback'
import UserSettings from './UserSettings'
import Urls from 'ustaxes/data/urls'
import StateQuestions from './StateQuestions'

import { isMobileOnly as isMobile } from 'react-device-detect'
import HealthSavingsAccounts from './savingsAccounts/healthSavingsAccounts'
Expand Down Expand Up @@ -170,6 +171,10 @@ export const drawerSections: Section[] = [
)
]
},
{
title: 'State Questions',
items: [item('State Questions', Urls.stateQuestions, <StateQuestions />)]
},
{
title: 'Results',
items: [
Expand All @@ -196,7 +201,7 @@ const yearSpecificPages: Partial<{ [k in TaxYear]: Section[] }> = {
}

export const drawerSectionsForYear = (year: TaxYear): Section[] => [
...drawerSections.slice(0, -2),
...drawerSections.slice(0, -1),
...(yearSpecificPages[year] || []),
drawerSections[drawerSections.length - 1]
]
Expand Down
239 changes: 239 additions & 0 deletions src/components/StateQuestions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import { ReactElement, useEffect } from 'react'
import { Helmet } from 'react-helmet'
import { Grid, List, ListItem, Link } from '@material-ui/core'
import { useDispatch, useSelector, TaxesState } from 'ustaxes/redux'
import { StateQuestionTagName, StateResponses, State } from 'ustaxes/core/data'
import { Patterns } from 'ustaxes/components/Patterns'
import { getRequiredStateQuestions } from 'ustaxes/core/data/stateQuestions'
import {
LabeledCheckbox,
LabeledInput,
GenericLabeledDropdown,
boxLabel
} from './input'
import { answerStateQuestion } from 'ustaxes/redux/actions'
import { FormProvider, useForm } from 'react-hook-form'
import { usePager } from './pager'
import _ from 'lodash'
import { intentionallyFloat } from 'ustaxes/core/util'

const emptyQuestions: StateResponses = {
// oregon state questions
// page 2
OR_6A_TAXPAYER_SEVERELY_DISABLED: false,
OR_6B_SPOUSE_SEVERELY_DISABLED: false,
// page 4
OR_21_INTEREST_ON_INSTALLMENT_SALES: '',
OR_24_POLITICAL_CONTRIBUTION_CREDIT: '',
OR_25_TOTAL_STANDARD_CREDITS_FROM_OR_ASC: '',
OR_28_TOTAL_CARRYFORWARD_CREDITS_FROM_OR_ASC: '',
OR_30_TOTAL_CREDIT_RECAPTURES_FROM_OR_ASC: '',
// page 5
OR_32_OREGON_INCOME_TAX_WITHHELD: '',
OR_33_AMOUNT_APPLIED_FROM_PRIOR_YEAR_REFUND: '',
OR_34_ESTIMATED_TAX_PAYMENTS: '',
OR_36_53_KICKER_OREGON_SURPLUS_CREDIT: '',
OR_37_TOTAL_REFUNDABLE_CREDITS_FROM_OR_ASC: '',
OR_41_PENALTY_FOR_FILING_LATE: '',
OR_42_INTEREST_ON_UNDERPAYMENT_OF_EST_TAX: '',
OR_42a_EXCEPTION_NUMBER: '',
OR_42b_ANNUALIZED: false,
// page 6
OR_46_ESTIMATED_TAX: '',
OR_47_CHARITABLE_CHECKOFF_DONATIONS: '',
OR_48_POLITICAL_PARTY_3DOLLAR_CHECKOFF: '',
OR_48a_TAXPAYER_POLITICAL_PARTY_CODE: '',
OR_48b_SPOUSE_POLITICAL_PARTY_CODE: '',
OR_49_529_COLLEGE_SAVINGS_PLAN_DEPOSITS: '',
OR_53_DONATE_TO_STATE_SCHOOL_FUND: false
}

const OR10ExceptionNumbers = [
{
name: 'Exception 1: Two-thirds of your income is from farming or fishing.',
code: '1'
},
{
name: 'Exception 2: The tax on last year’s resident return was $0.',
code: '2'
},
{
name: 'Exception 3: You retired at age 62 or older or became disabled within the last two years and there was a reasonable cause for the underpayment.',
code: '3'
},
{
name: 'Exception 4: You underpaid due to unusual circumstances.',
code: '4'
},
{
name: 'Exception 5: You’re a shareholder of a corporation that recently made a Subchapter S election and you were a nonresident in 2021 or a part-year resident in 2020.',
code: '5'
}
]

const ORPoliticalParties = [
{ name: 'Constitution Party of Oregon', code: '500' },
{ name: 'Democratic Party of Oregon', code: '501' },
{ name: 'Independent Party of Oregon', code: '502' },
{ name: 'Libertarian Party of Oregon', code: '503' },
{ name: 'Oregon Republican Party', code: '504' },
{ name: 'Pacific Green Party of Oregon', code: '505' },
{ name: 'Progressive Party', code: '506' },
{ name: 'Working Families Party of Oregon', code: '507' }
]

const StateQuestions = (): ReactElement => {
const information = useSelector((state: TaxesState) => state.information)

const stateResidency: State | undefined =
information.stateResidencies[0]?.state

const stateAnswers: StateResponses = {
...emptyQuestions,
...information.stateQuestions
}

const methods = useForm<StateResponses>({ defaultValues: stateAnswers })

const {
handleSubmit,
getValues,
reset,
formState: { isDirty }
} = methods

const currentValues = getValues()

const { navButtons, onAdvance } = usePager()

const stateQuestions = getRequiredStateQuestions({
...information,
stateQuestions: {
...information.stateQuestions,
...currentValues
}
})

const currentAnswers: StateResponses = { ...emptyQuestions, ...currentValues }

// This form can be rerendered because the global state was modified by
// another control.
useEffect(() => {
if (!isDirty && !_.isEqual(currentAnswers, stateAnswers)) {
reset(stateAnswers)
}
}, [])

const dispatch = useDispatch()

const onSubmit = (stateResponses: StateResponses): void => {
// fix to remove unrequired answers:
const qtags = stateQuestions.map((q) => q.tag)
const unrequired = Object.keys(stateResponses).filter(
(rtag) =>
qtags.find((t) => t === (rtag as StateQuestionTagName)) === undefined
)

const newStateResponses = {
...stateResponses,
...Object.fromEntries(unrequired.map((k) => [k, undefined]))
}

dispatch(answerStateQuestion(newStateResponses))
onAdvance()
}

const htmlDecode = (input: string): string => {
const e = document.createElement('div')
e.innerHTML = input
const value = e.childNodes[0].nodeValue
return e.childNodes.length === 0 || value === null ? '' : value
}

const page = (
<form tabIndex={-1} onSubmit={intentionallyFloat(handleSubmit(onSubmit))}>
<Helmet>
<title>State Questions | Results | UsTaxes.org</title>
</Helmet>
<h2>State Questions</h2>
<div>
{(() => {
switch (stateResidency) {
case 'OR': {
return (
<Link
href="https://www.oregon.gov/dor/forms/FormsPubs/form-or-40-inst_101-040-1_2021.pdf"
target="_blank"
rel="noreferrer"
>
2021 Oregon Income Tax - Form OR-40 Instructions
</Link>
)
}
}
})()}
</div>
<Grid container spacing={2}>
<List>
{stateQuestions.map((q, i) => (
<ListItem key={i}>
<div>
{q.description !== '' && (
<div
dangerouslySetInnerHTML={{
__html: htmlDecode(q.description)
}}
/>
)}
{(() => {
let dropDownData: unknown[] = []

if (
q.tag === 'OR_48a_TAXPAYER_POLITICAL_PARTY_CODE' ||
q.tag === 'OR_48b_SPOUSE_POLITICAL_PARTY_CODE'
) {
dropDownData = ORPoliticalParties
} else if (q.tag === 'OR_42a_EXCEPTION_NUMBER') {
dropDownData = OR10ExceptionNumbers
}

if (q.valueTag === 'boolean') {
return <LabeledCheckbox name={q.tag} label={q.text} />
} else if (q.valueTag === 'combobox') {
return (
<GenericLabeledDropdown
dropDownData={
dropDownData as unknown as {
name: string
code: string
}[]
}
label={q.text}
valueMapping={(p) => p.code}
name={q.tag}
keyMapping={(p) => p.code}
textMapping={(p) => p.name}
/>
)
} else {
return (
<LabeledInput
label={boxLabel(q.tag.slice(3, 5), q.text)}
patternConfig={Patterns.currency}
name={q.tag}
/>
)
}
})()}
</div>
</ListItem>
))}
</List>
</Grid>
{navButtons}
</form>
)
return <FormProvider {...methods}>{page}</FormProvider>
}

export default StateQuestions
Loading