diff --git a/app/controllers/bills_controller.rb b/app/controllers/bills_controller.rb index c1b3a7db..46d3356a 100644 --- a/app/controllers/bills_controller.rb +++ b/app/controllers/bills_controller.rb @@ -32,7 +32,7 @@ def new T.unsafe(self).render_bill_creator({ bills: (current_sway_locale&.bills || []).map { |b| b.to_builder.attributes! }, bill: Bill.new.attributes, - legislators: (current_sway_locale&.legislators&.where(active: true) || []).map do |l| + legislators: (current_sway_locale&.legislators || []).map do |l| l.to_builder.attributes! end, legislatorVotes: [], @@ -52,7 +52,7 @@ def edit organization.to_builder(with_positions: true).attributes! end end, - legislators: (current_sway_locale&.legislators&.where(active: true) || []).map do |l| + legislators: (current_sway_locale&.legislators || []).map do |l| l.to_builder.attributes! end, legislatorVotes: @bill.legislator_votes.map { |lv| lv.to_builder.attributes! }, diff --git a/app/frontend/components/admin/creator/BillCreatorAccordions.tsx b/app/frontend/components/admin/creator/BillCreatorAccordions.tsx index 76508df3..0ef8ace9 100644 --- a/app/frontend/components/admin/creator/BillCreatorAccordions.tsx +++ b/app/frontend/components/admin/creator/BillCreatorAccordions.tsx @@ -6,6 +6,7 @@ import { EEventKey } from "app/frontend/components/bill/creator/constants"; import { PropsWithChildren, useCallback } from "react"; import { Accordion } from "react-bootstrap"; import { sway } from "sway"; +import { notify } from "app/frontend/sway_utils"; interface IProps { setCreatorDirty: React.Dispatch>; @@ -13,10 +14,19 @@ interface IProps { const BillCreatorAccordions: React.FC = ({ setCreatorDirty }) => { const bill = usePage().props.bill as sway.IBill; + const isNoBillId = !(bill?.id && bill.id > 0); const event_key = new URLSearchParams(window.location.search).get("event_key"); const setEventKey = useCallback( (eventKey: EEventKey) => { + if (eventKey !== EEventKey.BILL && isNoBillId) { + notify({ + level: "error", + title: "Click Save on the Details and Summary tab before proceeding.", + }); + return; + } + const params = new URLSearchParams(window.location.search); if (eventKey === event_key) { params.delete("event_key", eventKey); @@ -25,7 +35,7 @@ const BillCreatorAccordions: React.FC = ({ setCreatorDirty }) => { } router.get(`${window.location.origin}${window.location.pathname}?${params.toString()}`); }, - [event_key], + [event_key, isNoBillId], ); return ( @@ -41,11 +51,7 @@ const BillCreatorAccordions: React.FC = ({ setCreatorDirty }) => { - 0)} - > + Legislator Votes @@ -55,11 +61,7 @@ const BillCreatorAccordions: React.FC = ({ setCreatorDirty }) => { - 0)} - > + Supporting/Opposing Arguments diff --git a/app/frontend/components/admin/creator/BillCreatorBill.tsx b/app/frontend/components/admin/creator/BillCreatorBill.tsx index accbf19a..a6441c47 100644 --- a/app/frontend/components/admin/creator/BillCreatorBill.tsx +++ b/app/frontend/components/admin/creator/BillCreatorBill.tsx @@ -70,6 +70,35 @@ const BillCreatorBill = ({ setCreatorDirty }: IProps) => { }; }); + if (!form.data.status) { + notify({ + level: "error", + title: "Please select the bill status before saving.", + }); + return; + } + if (!form.data.category) { + notify({ + level: "error", + title: "Please select the bill category before saving.", + }); + return; + } + if (!form.data.chamber) { + notify({ + level: "error", + title: "Please select the bill chamber before saving.", + }); + return; + } + if (!form.data.legislator_id) { + notify({ + level: "error", + title: "Please select the bill sponsor before saving.", + }); + return; + } + const caller = initialValues.id ? form.put : form.post; const route = initialValues.id ? `/bills/${initialValues.id}` : "/bills"; diff --git a/app/frontend/components/admin/creator/BillCreatorOrganizations.tsx b/app/frontend/components/admin/creator/BillCreatorOrganizations.tsx index 86ee8a39..47fa4427 100644 --- a/app/frontend/components/admin/creator/BillCreatorOrganizations.tsx +++ b/app/frontend/components/admin/creator/BillCreatorOrganizations.tsx @@ -11,6 +11,7 @@ import { TOrganizationOption, } from "app/frontend/components/admin/creator/types"; import FormContext from "app/frontend/components/contexts/FormContext"; +import SwaySpinner from "app/frontend/components/SwaySpinner"; import { useSearchParams } from "app/frontend/hooks/useSearchParams"; import { Support } from "app/frontend/sway_constants"; import { Fragment, useCallback, useEffect, useMemo } from "react"; @@ -20,9 +21,8 @@ import { MultiValue } from "react-select"; import Creatable from "react-select/creatable"; import { sway } from "sway"; import { useInertiaForm } from "use-inertia-form"; -import { notify, REACT_SELECT_STYLES, SWAY_STORAGE } from "../../../sway_utils"; +import { logDev, notify, REACT_SELECT_STYLES, SWAY_STORAGE } from "../../../sway_utils"; import BillCreatorOrganization from "./BillCreatorOrganization"; -import SwaySpinner from "app/frontend/components/SwaySpinner"; const toCreatorOption = (organization: sway.IOrganization, billId: number) => ({ @@ -37,6 +37,9 @@ const BillCreatorOrganizations: React.FC = () => { // @ts-expect-error - Property 'organizations' is missing in type 'Errors & ErrorBag' but required in type 'IOrganizationErrors'. const errors: IOrganizationErrors = usePage().props.errors; + // eslint-disable-next-line react-hooks/exhaustive-deps + const searchParams = useMemo(() => new URLSearchParams(window.location.search), [window.location.search]); + const bill = usePage().props.bill as sway.IBill & { organizations: sway.IOrganization[] }; const organizations = usePage().props.organizations as sway.IOrganization[]; const billOrganizations = bill.organizations as sway.IOrganization[]; @@ -60,18 +63,17 @@ const BillCreatorOrganizations: React.FC = () => { blurredFieldName, } = useTempStorage(SWAY_STORAGE.Local.BillOfTheWeek.Organizations, data); - const { - entries: { saved }, - remove, - } = useSearchParams(); + const { remove } = useSearchParams(); useEffect(() => { + const saved = searchParams.get("saved"); + logDev("SAVVVED", saved); if (saved) { notify({ level: "success", title: saved }); window.setTimeout(() => { remove("saved"); }, 2000); } - }, [saved, remove]); + }, [remove, searchParams]); const handleSelectOrganization = useCallback( (newValues: MultiValue) => { diff --git a/app/frontend/components/admin/creator/hooks/useNewBillInitialValues.ts b/app/frontend/components/admin/creator/hooks/useNewBillInitialValues.ts index 01c92895..3dbbfcf8 100644 --- a/app/frontend/components/admin/creator/hooks/useNewBillInitialValues.ts +++ b/app/frontend/components/admin/creator/hooks/useNewBillInitialValues.ts @@ -32,7 +32,7 @@ export const useNewBillInitialValues = (): IApiBillCreator => { summary: bill?.summary?.trim() ?? "", summary_preview: bill?.summary?.trim() ?? "", category: bill?.category ?? "", - status: bill?.status?.trim() ?? "", + status: bill?.status?.trim() ?? ("committee" as sway.TBillStatus), active: typeof bill?.active === "boolean" ? bill.active : true, introduced_date_time_utc: bill?.introducedDateTimeUtc ? new Date(bill?.introducedDateTimeUtc) : null, diff --git a/app/models/sway_locale.rb b/app/models/sway_locale.rb index aff6ddd8..fc25902e 100644 --- a/app/models/sway_locale.rb +++ b/app/models/sway_locale.rb @@ -73,9 +73,10 @@ def region? RegionUtil.from_region_name_to_region_code(city_name).present? end - sig { returns(T::Array[Legislator]) } - def legislators + sig { params(active: T.nilable(Boolean)).returns(ActiveRecord::Relation) } + def legislators(active = true) Legislator.joins(:district).where( + active: active, district: { sway_locale: self }