From e3cc84508833e55a3cd420f513c51cf34af2d33e Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 16 Mar 2025 16:26:49 -0500 Subject: [PATCH 01/40] Fix signup test --- routes/web.php | 31 ++++++++++++++----- .../Feature/Intake/ParticipantSignupTest.php | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/routes/web.php b/routes/web.php index b5d88426..2664dc5e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -32,24 +32,41 @@ Route::get('/users/create', [UsersController::class, 'create'])->name('users.create'); }); -Route::name('intake') +Route::name('intake.') ->prefix('intake') ->group(function () { Route::middleware(['role:intake'])->group(function () { - Route::get('/', [IntakeController::class, 'index'])->name('.index'); - Route::get('register', [ParticipantRegistrationController::class, 'create'])->name('.register'); + Route::get('/', [IntakeController::class, 'index'])->name('index'); + Route::get('register', [ParticipantRegistrationController::class, 'create'])->name('register'); Route::post('register', [ParticipantRegistrationController::class, 'store']); }); Route::middleware('role:participant')->group(function () { - Route::get('signup', [ParticipantSignupController::class, 'create'])->name('.signup'); + Route::get('signup', [ParticipantSignupController::class, 'create'])->name('signup'); Route::post('signup', [ParticipantSignupController::class, 'store']); }); - Route::middleware('role:participant')->group(function () { - Route::get('disclosure', [ParticipantDisclosureController::class, 'create'])->name('.disclosure'); - Route::post('disclosure', [ParticipantDisclosureController::class, 'store']); + Route::middleware('role:participant') + ->name('disclosure.') + ->prefix('disclosure') + ->group(function () { + Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('index'); + Route::post('/', [ParticipantDisclosureController::class, 'store']); + Route::get('/edit', [ParticipantDisclosureController::class, 'edit'])->name( + 'edit'); + Route::put('{id}', [ParticipantDisclosureController::class, 'update']); + }); + + Route::middleware('role:participant') + ->name('fatherhood-assessment.') + ->prefix('fatherhood-assessment.') + ->group(function () { + Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('index'); + Route::post('/', [ParticipantDisclosureController::class, 'store']); + Route::get('edit', [ParticipantDisclosureController::class, 'edit'])->name('edit'); + Route::put('{id}', [ParticipantDisclosureController::class, 'update']); }); + }); require __DIR__.'/auth.php'; diff --git a/tests/Feature/Intake/ParticipantSignupTest.php b/tests/Feature/Intake/ParticipantSignupTest.php index e00dda47..a881104f 100644 --- a/tests/Feature/Intake/ParticipantSignupTest.php +++ b/tests/Feature/Intake/ParticipantSignupTest.php @@ -67,7 +67,7 @@ public function test_participant_signup(): void $response = $this->actingAs($participantUser)->post(route('intake.signup'), $participantData); - $response->assertRedirectToRoute('intake.disclosure'); + $response->assertRedirectToRoute('intake.disclosure.index'); $participant = $participantUser->participant()->first(); From b0195ea0fe5ef5d615a87eb08cb90e09cf80efa1 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Tue, 18 Mar 2025 19:57:41 -0500 Subject: [PATCH 02/40] Participant Disclosure authorization --- .../ParticipantDisclosureController.php | 59 +++-- ...ticipantFatherhoodAssessmentController.php | 67 ++++++ .../Intake/ParticipantSignupController.php | 2 +- ...antDisclosureAuthorizationStoreRequest.php | 102 +++++++++ ...ntDisclosureAuthorizationUpdateRequest.php | 102 +++++++++ ...eParticipantFatherhoodAssesmentRequest.php | 28 +++ ...eParticipantFatherhoodAssesmentRequest.php | 28 +++ app/Models/Participant.php | 10 + .../ParticipantDisclosureAuthorization.php | 149 +++++++++++++ app/Services/ParticipantService.php | 20 ++ ...ticipantDisclosureAuthorizationFactory.php | 90 ++++++++ database/factories/ParticipantFactory.php | 32 +-- ...or_disclosure_confidential_information.php | 108 +++++++++ resources/js/Pages/Auth/Login.tsx | 2 +- routes/web.php | 20 +- .../ParticipantDisclosureControllerTest.php | 211 ++++++++++++++++++ .../Intake/ParticipantDisclosureTest.php | 180 +++++++++++++++ ...ParticipantDisclosureAuthorizationTest.php | 107 +++++++++ tests/Unit/Models/ParticipantTest.php | 60 +++++ 19 files changed, 1338 insertions(+), 39 deletions(-) create mode 100644 app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php create mode 100644 app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php create mode 100644 app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php create mode 100644 app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php create mode 100644 app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php create mode 100644 app/Models/ParticipantDisclosureAuthorization.php create mode 100644 database/factories/ParticipantDisclosureAuthorizationFactory.php create mode 100644 database/migrations/2025_03_02_173017_participant_consumer_authorization_for_disclosure_confidential_information.php create mode 100644 tests/Feature/Http/Controllers/Intake/ParticipantDisclosureControllerTest.php create mode 100644 tests/Feature/Intake/ParticipantDisclosureTest.php create mode 100644 tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php create mode 100644 tests/Unit/Models/ParticipantTest.php diff --git a/app/Http/Controllers/Intake/ParticipantDisclosureController.php b/app/Http/Controllers/Intake/ParticipantDisclosureController.php index 059d7587..8010f133 100644 --- a/app/Http/Controllers/Intake/ParticipantDisclosureController.php +++ b/app/Http/Controllers/Intake/ParticipantDisclosureController.php @@ -2,19 +2,12 @@ namespace App\Http\Controllers\Intake; -use App\Enums\Ethnicity; -use App\Enums\MaritalStatus; use App\Http\Controllers\Controller; -use App\Http\Requests\Intake\ParticipantSignupStoreRequest; -use App\Models\User; -use App\Services\ParticipantService; -use Illuminate\Auth\Events\Registered; +use App\Http\Requests\Intake\ParticipantDisclosureAuthorizationStoreRequest; +use App\Http\Requests\Intake\ParticipantDisclosureAuthorizationUpdateRequest; +use App\Models\ParticipantDisclosureAuthorization; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; -use Illuminate\Validation\Rules; use Inertia\Inertia; use Inertia\Response; @@ -31,16 +24,54 @@ public function create(Request $request): Response } /** - * Handle an incoming registration request. + * Handle an incoming disclosure agreement request. * * @throws \Illuminate\Validation\ValidationException */ - public function store(Request $request, ParticipantService $participantService) + public function store(ParticipantDisclosureAuthorizationStoreRequest $request) { - // todo: implement - return back(); + $validated = $request->validated(); + + // Get the current participant + $participant = $request->user()->participant; + + // Create or update the disclosure authorization + if ($participant->disclosureAuthorization) { + // Update existing authorization + $participant->disclosureAuthorization->update($validated); + $disclosureAuthorization = $participant->disclosureAuthorization; + } else { + // Create new authorization + $disclosureAuthorization = $participant->disclosureAuthorization()->create($validated); + } + + return redirect()->route('intake.fatherhood-assessment.index'); } + /** + * Show the form for editing the disclosure agreement. + */ + public function edit(Request $request): Response + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/Disclosure', [ + 'participant' => $participant, + 'disclosureAuthorization' => $participant->disclosureAuthorization, + ]); + } + + /** + * Update the disclosure agreement. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function update(ParticipantDisclosureAuthorizationUpdateRequest $request, ParticipantDisclosureAuthorization $disclosureAuthorization): RedirectResponse + { + $validated = $request->validated(); + $disclosureAuthorization->update($validated); + return redirect()->route('intake.fatherhood-assessment.index'); + } } \ No newline at end of file diff --git a/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php b/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php new file mode 100644 index 00000000..abe8074d --- /dev/null +++ b/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php @@ -0,0 +1,67 @@ +create($request->user(), $participantData); - return redirect(route('intake.disclosure', absolute: false)); + return redirect(route('intake.disclosure.index', absolute: false)); } diff --git a/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php b/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php new file mode 100644 index 00000000..a01302fc --- /dev/null +++ b/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php @@ -0,0 +1,102 @@ +|string> + */ + public function rules(): array + { + return [ + 'consumer_name' => 'required|string|max:255', + + // Authorized entities + 'is_dss_authorized' => 'required|boolean', + 'is_dys_authorized' => 'required|boolean', + 'is_mhd_authorized' => 'required|boolean', + 'is_dfas_authorized' => 'required|boolean', + 'is_mmac_authorized' => 'required|boolean', + 'is_fsd_authorized' => 'required|boolean', + 'is_cd_authorized' => 'required|boolean', + 'is_dls_authorized' => 'required|boolean', + 'is_other_authorized' => 'required|boolean', + 'other_authorized_entity' => 'required_if:is_other_authorized,1', + + // Subject information + 'subject_name' => 'required|string|max:255', + 'subject_phone' => 'required|string|max:20', + 'subject_dob' => 'required|date', + 'subject_ssn' => 'nullable|string|max:11', + 'subject_address' => 'required|string|max:255', + 'subject_email' => 'nullable|email|max:255', + + // Recipients + 'disclose_to_attorney' => 'required|boolean', + 'attorney_name' => 'nullable|required_if:disclose_to_attorney,1|string|max:255', + 'disclose_to_employer' => 'required|boolean', + 'employer_name' => 'nullable|required_if:disclose_to_employer,1|string|max:255', + 'disclose_to_legislator' => 'required|boolean', + 'legislator_name' => 'nullable|required_if:disclose_to_legislator,1|string|max:255', + 'disclose_to_governors_staff' => 'required|boolean', + 'other_recipient_details' => 'nullable|string|max:255', + + // Purpose of disclosure + 'purpose_eligibility_determination' => 'required|boolean', + 'purpose_legal_consultation' => 'required|boolean', + 'purpose_legal_proceedings' => 'required|boolean', + 'purpose_employment' => 'required|boolean', + 'purpose_complaint_investigation' => 'required|boolean', + 'purpose_treatment_planning' => 'required|boolean', + 'purpose_continuity_of_services' => 'required|boolean', + 'purpose_background_investigation' => 'required|boolean', + 'purpose_consumer_request' => 'required|boolean', + 'purpose_share_and_refer' => 'required|boolean', + 'purpose_other' => 'required|boolean', + 'other_purpose_details' => 'nullable|required_if:purpose_other,1|string|max:255', + + // Information to be disclosed + 'disclose_entire_file' => 'required|boolean', + 'disclose_licensure_information' => 'required|boolean', + 'disclose_medical_psychiatric_records' => 'required|boolean', + 'disclose_hotline_investigations' => 'required|boolean', + 'disclose_home_studies' => 'required|boolean', + 'disclose_eligibility_determinations' => 'required|boolean', + 'disclose_substance_abuse_treatment' => 'required|boolean', + 'disclose_client_employment_records' => 'required|boolean', + 'disclose_benefits_received' => 'required|boolean', + 'disclose_other_information' => 'required|boolean', + 'other_disclosure_details' => 'nullable|required_if:disclose_other_information,1|string|max:255', + + // Communication preferences + 'accept_text_messages' => 'nullable|boolean', + + // Signatures + 'consumer_signature' => 'required|string|max:255', + 'signature_date' => 'required|date', + 'witness_signature' => 'nullable|string|max:255', + 'witness_signature_date' => 'nullable|required_with:witness_signature|date', + 'guardian_signature' => 'nullable|string|max:255', + 'guardian_signature_date' => 'nullable|required_with:guardian_signature|date', + + // Survey delivery methods + 'survey_by_email' => 'required|boolean', + 'survey_by_mail' => 'required|boolean', + 'survey_by_online' => 'required|boolean', + + ]; + } +} diff --git a/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php b/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php new file mode 100644 index 00000000..8d2633b2 --- /dev/null +++ b/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php @@ -0,0 +1,102 @@ +|string> + */ + public function rules(): array + { + return [ + 'consumer_name' => 'required|string|max:255', + + // Authorized entities + 'is_dss_authorized' => 'required|boolean', + 'is_dys_authorized' => 'required|boolean', + 'is_mhd_authorized' => 'required|boolean', + 'is_dfas_authorized' => 'required|boolean', + 'is_mmac_authorized' => 'required|boolean', + 'is_fsd_authorized' => 'required|boolean', + 'is_cd_authorized' => 'required|boolean', + 'is_dls_authorized' => 'required|boolean', + 'is_other_authorized' => 'required|boolean', + 'other_authorized_entity' => 'required_if:is_other_authorized,1', + + // Subject information + 'subject_name' => 'required|string|max:255', + 'subject_phone' => 'required|string|max:20', + 'subject_dob' => 'required|date', + 'subject_ssn' => 'nullable|string|max:11', + 'subject_address' => 'required|string|max:255', + 'subject_email' => 'nullable|email|max:255', + + // Recipients + 'disclose_to_attorney' => 'required|boolean', + 'attorney_name' => 'nullable|required_if:disclose_to_attorney,1|string|max:255', + 'disclose_to_employer' => 'required|boolean', + 'employer_name' => 'nullable|required_if:disclose_to_employer,1|string|max:255', + 'disclose_to_legislator' => 'required|boolean', + 'legislator_name' => 'nullable|required_if:disclose_to_legislator,1|string|max:255', + 'disclose_to_governors_staff' => 'required|boolean', + 'other_recipient_details' => 'nullable|string|max:255', + + // Purpose of disclosure + 'purpose_eligibility_determination' => 'required|boolean', + 'purpose_legal_consultation' => 'required|boolean', + 'purpose_legal_proceedings' => 'required|boolean', + 'purpose_employment' => 'required|boolean', + 'purpose_complaint_investigation' => 'required|boolean', + 'purpose_treatment_planning' => 'required|boolean', + 'purpose_continuity_of_services' => 'required|boolean', + 'purpose_background_investigation' => 'required|boolean', + 'purpose_consumer_request' => 'required|boolean', + 'purpose_share_and_refer' => 'required|boolean', + 'purpose_other' => 'required|boolean', + 'other_purpose_details' => 'nullable|required_if:purpose_other,1|string|max:255', + + // Information to be disclosed + 'disclose_entire_file' => 'required|boolean', + 'disclose_licensure_information' => 'required|boolean', + 'disclose_medical_psychiatric_records' => 'required|boolean', + 'disclose_hotline_investigations' => 'required|boolean', + 'disclose_home_studies' => 'required|boolean', + 'disclose_eligibility_determinations' => 'required|boolean', + 'disclose_substance_abuse_treatment' => 'required|boolean', + 'disclose_client_employment_records' => 'required|boolean', + 'disclose_benefits_received' => 'required|boolean', + 'disclose_other_information' => 'required|boolean', + 'other_disclosure_details' => 'nullable|required_if:disclose_other_information,1|string|max:255', + + // Communication preferences + 'accept_text_messages' => 'nullable|boolean', + + // Signatures + 'consumer_signature' => 'required|string|max:255', + 'signature_date' => 'required|date', + 'witness_signature' => 'nullable|string|max:255', + 'witness_signature_date' => 'nullable|required_with:witness_signature|date', + 'guardian_signature' => 'nullable|string|max:255', + 'guardian_signature_date' => 'nullable|required_with:guardian_signature|date', + + // Survey delivery methods + 'survey_by_email' => 'required|boolean', + 'survey_by_mail' => 'required|boolean', + 'survey_by_online' => 'required|boolean', + + ]; + } +} diff --git a/app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php b/app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php new file mode 100644 index 00000000..b30ad429 --- /dev/null +++ b/app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php b/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php new file mode 100644 index 00000000..5767e1ae --- /dev/null +++ b/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Models/Participant.php b/app/Models/Participant.php index 782edf06..9e8747ad 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; /** * @property string $id @@ -99,4 +100,13 @@ public function children(): HasMany { return $this->hasMany(Child::class); } + + /** + * Get the disclosure authorizations for the participant. + */ + public function disclosureAuthorization(): HasMany + { + return $this->hasMany(ParticipantDisclosureAuthorization::class); + } + } diff --git a/app/Models/ParticipantDisclosureAuthorization.php b/app/Models/ParticipantDisclosureAuthorization.php new file mode 100644 index 00000000..21b1355e --- /dev/null +++ b/app/Models/ParticipantDisclosureAuthorization.php @@ -0,0 +1,149 @@ + + */ + protected $fillable = [ + 'participant_id', + 'consumer_name', + 'is_dss_authorized', + 'is_dys_authorized', + 'is_mhd_authorized', + 'is_dfas_authorized', + 'is_mmac_authorized', + 'is_fsd_authorized', + 'is_cd_authorized', + 'is_dls_authorized', + 'is_other_authorized', + 'other_authorized_entity', + 'subject_name', + 'subject_phone', + 'subject_dob', + 'subject_ssn', + 'subject_address', + 'subject_email', + 'disclose_to_attorney', + 'attorney_name', + 'disclose_to_employer', + 'employer_name', + 'disclose_to_legislator', + 'legislator_name', + 'disclose_to_governors_staff', + 'other_recipient_details', + 'purpose_eligibility_determination', + 'purpose_legal_consultation', + 'purpose_legal_proceedings', + 'purpose_employment', + 'purpose_complaint_investigation', + 'purpose_treatment_planning', + 'purpose_continuity_of_services', + 'purpose_background_investigation', + 'purpose_consumer_request', + 'purpose_share_and_refer', + 'purpose_other', + 'other_purpose_details', + 'disclose_entire_file', + 'disclose_licensure_information', + 'disclose_medical_psychiatric_records', + 'disclose_hotline_investigations', + 'disclose_home_studies', + 'disclose_eligibility_determinations', + 'disclose_substance_abuse_treatment', + 'disclose_client_employment_records', + 'disclose_benefits_received', + 'disclose_other_information', + 'other_disclosure_details', + 'accept_text_messages', + 'consumer_signature', + 'signature_date', + 'witness_signature', + 'witness_signature_date', + 'guardian_signature', + 'guardian_signature_date', + 'survey_by_email', + 'survey_by_mail', + 'survey_by_online', + 'date_completed', + 'date', + 'signature', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'subject_dob' => 'date', + 'signature_date' => 'date', + 'witness_signature_date' => 'date', + 'guardian_signature_date' => 'date', + 'date_completed' => 'date', + 'is_dss_authorized' => 'boolean', + 'is_dys_authorized' => 'boolean', + 'is_mhd_authorized' => 'boolean', + 'is_dfas_authorized' => 'boolean', + 'is_mmac_authorized' => 'boolean', + 'is_fsd_authorized' => 'boolean', + 'is_cd_authorized' => 'boolean', + 'is_dls_authorized' => 'boolean', + 'disclose_to_attorney' => 'boolean', + 'disclose_to_employer' => 'boolean', + 'disclose_to_legislator' => 'boolean', + 'disclose_to_governors_staff' => 'boolean', + 'purpose_eligibility_determination' => 'boolean', + 'purpose_legal_consultation' => 'boolean', + 'purpose_legal_proceedings' => 'boolean', + 'purpose_employment' => 'boolean', + 'purpose_complaint_investigation' => 'boolean', + 'purpose_treatment_planning' => 'boolean', + 'purpose_continuity_of_services' => 'boolean', + 'purpose_background_investigation' => 'boolean', + 'purpose_consumer_request' => 'boolean', + 'purpose_share_and_refer' => 'boolean', + 'purpose_other' => 'boolean', + 'disclose_entire_file' => 'boolean', + 'disclose_licensure_information' => 'boolean', + 'disclose_medical_psychiatric_records' => 'boolean', + 'disclose_hotline_investigations' => 'boolean', + 'disclose_home_studies' => 'boolean', + 'disclose_eligibility_determinations' => 'boolean', + 'disclose_substance_abuse_treatment' => 'boolean', + 'disclose_client_employment_records' => 'boolean', + 'disclose_benefits_received' => 'boolean', + 'disclose_other_information' => 'boolean', + 'accept_text_messages' => 'boolean', + 'survey_by_email' => 'boolean', + 'survey_by_mail' => 'boolean', + 'survey_by_online' => 'boolean', + ]; + + /** + * Get the participant that owns the authorization disclosure. + */ + public function participant(): BelongsTo + { + return $this->belongsTo(Participant::class); + } +} diff --git a/app/Services/ParticipantService.php b/app/Services/ParticipantService.php index 96586abb..c74f6405 100644 --- a/app/Services/ParticipantService.php +++ b/app/Services/ParticipantService.php @@ -25,4 +25,24 @@ public function addChildren(Participant $participant, array $childrenInfo): Coll return $participant->children()->createMany($childrenInfo); } + /** + * Create or update a participant's disclosure authorization. + * + * @param \App\Models\Participant $participant + * @param array $data + * @return \App\Models\ParticipantDisclosureAuthorization + */ + public function createOrUpdateDisclosureAuthorization($participant, array $data) + { + // Check if the participant already has a disclosure authorization + if ($participant->disclosureAuthorization) { + // Update existing authorization + $participant->disclosureAuthorization->update($data); + return $participant->disclosureAuthorization; + } + + // Create new authorization + return $participant->disclosureAuthorization()->create($data); + } + } \ No newline at end of file diff --git a/database/factories/ParticipantDisclosureAuthorizationFactory.php b/database/factories/ParticipantDisclosureAuthorizationFactory.php new file mode 100644 index 00000000..2eb13340 --- /dev/null +++ b/database/factories/ParticipantDisclosureAuthorizationFactory.php @@ -0,0 +1,90 @@ + + */ +class ParticipantDisclosureAuthorizationFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = ParticipantDisclosureAuthorization::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'participant_id' => Participant::factory(), + 'consumer_name' => $this->faker->name(), + 'is_dss_authorized' => $this->faker->boolean(), + 'is_dys_authorized' => $this->faker->boolean(), + 'is_mhd_authorized' => $this->faker->boolean(), + 'is_dfas_authorized' => $this->faker->boolean(), + 'is_mmac_authorized' => $this->faker->boolean(), + 'is_fsd_authorized' => $this->faker->boolean(), + 'is_cd_authorized' => $this->faker->boolean(), + 'is_dls_authorized' => $this->faker->boolean(), + 'other_authorized_entity' => $this->faker->company(), + 'subject_name' => $this->faker->name(), + 'subject_phone' => $this->faker->phoneNumber(), + 'subject_dob' => $this->faker->date(), + 'subject_ssn' => '123-45-6789', + 'subject_address' => $this->faker->address(), + 'subject_email' => $this->faker->email(), + 'disclose_to_attorney' => $this->faker->boolean(), + 'attorney_name' => $this->faker->name(), + 'disclose_to_employer' => $this->faker->boolean(), + 'employer_name' => $this->faker->company(), + 'disclose_to_legislator' => $this->faker->boolean(), + 'legislator_name' => $this->faker->name(), + 'disclose_to_governors_staff' => $this->faker->boolean(), + 'other_recipient_details' => $this->faker->sentence(), + 'purpose_eligibility_determination' => $this->faker->boolean(), + 'purpose_legal_consultation' => $this->faker->boolean(), + 'purpose_legal_proceedings' => $this->faker->boolean(), + 'purpose_employment' => $this->faker->boolean(), + 'purpose_complaint_investigation' => $this->faker->boolean(), + 'purpose_treatment_planning' => $this->faker->boolean(), + 'purpose_continuity_of_services' => $this->faker->boolean(), + 'purpose_background_investigation' => $this->faker->boolean(), + 'purpose_consumer_request' => $this->faker->boolean(), + 'purpose_share_and_refer' => $this->faker->boolean(), + 'purpose_other' => $this->faker->boolean(), + 'other_purpose_details' => $this->faker->sentence(), + 'disclose_entire_file' => $this->faker->boolean(), + 'disclose_licensure_information' => $this->faker->boolean(), + 'disclose_medical_psychiatric_records' => $this->faker->boolean(), + 'disclose_hotline_investigations' => $this->faker->boolean(), + 'disclose_home_studies' => $this->faker->boolean(), + 'disclose_eligibility_determinations' => $this->faker->boolean(), + 'disclose_substance_abuse_treatment' => $this->faker->boolean(), + 'disclose_client_employment_records' => $this->faker->boolean(), + 'disclose_benefits_received' => $this->faker->boolean(), + 'disclose_other_information' => $this->faker->boolean(), + 'other_disclosure_details' => $this->faker->sentence(), + 'accept_text_messages' => $this->faker->boolean(), + 'consumer_signature' => $this->faker->name(), + 'signature_date' => $this->faker->date(), + 'witness_signature' => $this->faker->name(), + 'witness_signature_date' => $this->faker->date(), + 'guardian_signature' => $this->faker->optional()->name(), + 'guardian_signature_date' => $this->faker->optional()->date(), + 'survey_by_email' => $this->faker->boolean(), + 'survey_by_mail' => $this->faker->boolean(), + 'survey_by_online' => $this->faker->boolean(), + 'date_completed' => $this->faker->date(), + ]; + } +} \ No newline at end of file diff --git a/database/factories/ParticipantFactory.php b/database/factories/ParticipantFactory.php index f312559c..a788f24c 100644 --- a/database/factories/ParticipantFactory.php +++ b/database/factories/ParticipantFactory.php @@ -4,9 +4,8 @@ use App\Enums\Ethnicity; use App\Enums\MaritalStatus; -use App\Models\Region; +use App\Models\Participant; use App\Models\User; -use App\Services\Formatter\PhoneFormatter; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -14,6 +13,13 @@ */ class ParticipantFactory extends Factory { + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = Participant::class; + /** * Define the model's default state. * @@ -21,22 +27,20 @@ class ParticipantFactory extends Factory */ public function definition(): array { + return [ 'user_id' => User::factory(), - 'region_id' => Region::factory(), - 'street' => $this->faker->streetAddress(), + 'cell_phone_number' => random_int(1000000000, 9999999999), + 'address_line_1' => $this->faker->streetAddress(), 'city' => $this->faker->city(), - 'state' => 'MO', - 'zip_code' => substr($this->faker->postcode(), 0, 5), + 'state' => $this->faker->stateAbbr(), + 'zipcode' => random_int(10000,99999), 'employer' => $this->faker->company(), - 'cell_phone_number' => PhoneFormatter::format($this->faker->phoneNumber()), - 'home_phone_number' => PhoneFormatter::format($this->faker->phoneNumber()), - 'work_phone_number' => PhoneFormatter::format($this->faker->phoneNumber()), - 'alt_contact_number' => PhoneFormatter::format($this->faker->phoneNumber()), - 'marital_status' => $this->faker->randomElement(MaritalStatus::cases()), - 'ethnicity' => $this->faker->randomElement(Ethnicity::cases()), - 'monthly_child_support' => $this->faker->randomFloat(2, 100, 1000), - 'intake_date' => $this->faker->date(), + 'probation_parole_case_worker_name' => $this->faker->name(), + 'probation_parole_case_worker_phone' => random_int(1000000000, 9999999999), + 'ethnicity' => $this->faker->randomElement(Ethnicity::values()), + 'marital_status' => $this->faker->randomElement(MaritalStatus::values()), + ]; } } diff --git a/database/migrations/2025_03_02_173017_participant_consumer_authorization_for_disclosure_confidential_information.php b/database/migrations/2025_03_02_173017_participant_consumer_authorization_for_disclosure_confidential_information.php new file mode 100644 index 00000000..ac1efc1e --- /dev/null +++ b/database/migrations/2025_03_02_173017_participant_consumer_authorization_for_disclosure_confidential_information.php @@ -0,0 +1,108 @@ +uuid('id')->primary(); + $table->foreignUuid('participant_id')->constrained(); + + // Consumer details + $table->string('consumer_name'); + + // Authorized entities (checkboxes) + $table->boolean('is_dss_authorized'); + $table->boolean('is_dys_authorized'); // Fixed typo from fys to dys based on form + $table->boolean('is_mhd_authorized'); + $table->boolean('is_dfas_authorized'); + $table->boolean('is_mmac_authorized'); + $table->boolean('is_fsd_authorized'); + $table->boolean('is_cd_authorized'); + $table->boolean('is_dls_authorized'); + $table->boolean('is_other_authorized'); + $table->string('other_authorized_entity')->nullable(); + + // Disclosure entity information + $table->string('subject_name'); + $table->string('subject_phone'); + $table->date('subject_dob'); + $table->string('subject_ssn')->nullable(); + $table->string('subject_address'); + $table->string('subject_email')->nullable(); + + // Recipients (who info will be disclosed to) + $table->boolean('disclose_to_attorney'); + $table->string('attorney_name')->nullable(); + $table->boolean('disclose_to_employer'); + $table->string('employer_name')->nullable(); + $table->boolean('disclose_to_legislator'); + $table->string('legislator_name')->nullable(); + $table->boolean('disclose_to_governors_staff'); + $table->string('other_recipient_details')->nullable(); + + // Purpose of disclosure checkboxes + $table->boolean('purpose_eligibility_determination'); + $table->boolean('purpose_legal_consultation'); + $table->boolean('purpose_legal_proceedings'); + $table->boolean('purpose_employment'); + $table->boolean('purpose_complaint_investigation'); + $table->boolean('purpose_treatment_planning'); + $table->boolean('purpose_continuity_of_services'); + $table->boolean('purpose_background_investigation'); + $table->boolean('purpose_consumer_request'); + $table->boolean('purpose_share_and_refer'); + $table->boolean('purpose_other'); + $table->string('other_purpose_details')->nullable(); // Changed to string for details + + // Information to be disclosed checkboxes + $table->boolean('disclose_entire_file'); + $table->boolean('disclose_licensure_information'); + $table->boolean('disclose_medical_psychiatric_records'); + $table->boolean('disclose_hotline_investigations'); + $table->boolean('disclose_home_studies'); + $table->boolean('disclose_eligibility_determinations'); + $table->boolean('disclose_substance_abuse_treatment'); + $table->boolean('disclose_client_employment_records'); + $table->boolean('disclose_benefits_received'); + $table->boolean('disclose_other_information'); + $table->string('other_disclosure_details')->nullable(); + + // Communication preferences + $table->boolean('accept_text_messages')->nullable(); + + // Signatures + $table->string('consumer_signature'); + $table->date('signature_date'); + $table->string('witness_signature')->nullable(); + $table->date('witness_signature_date')->nullable(); + $table->string('guardian_signature')->nullable(); + $table->date('guardian_signature_date')->nullable(); + + // Survey delivery preferences + $table->boolean('survey_by_email'); + $table->boolean('survey_by_mail'); + $table->boolean('survey_by_online'); + + // Marked completed on + $table->date('date_completed')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('participant_disclosure_authorization'); + } +}; diff --git a/resources/js/Pages/Auth/Login.tsx b/resources/js/Pages/Auth/Login.tsx index ea9e22fc..c431a976 100644 --- a/resources/js/Pages/Auth/Login.tsx +++ b/resources/js/Pages/Auth/Login.tsx @@ -25,7 +25,7 @@ export default function Login({ const submit: FormEventHandler = (e) => { e.preventDefault() - post(route('login')) + post('/login') } return ( diff --git a/routes/web.php b/routes/web.php index 2664dc5e..4a3b66cf 100644 --- a/routes/web.php +++ b/routes/web.php @@ -49,22 +49,24 @@ Route::middleware('role:participant') ->name('disclosure.') ->prefix('disclosure') + ->controller(ParticipantDisclosureController::class) ->group(function () { - Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('index'); - Route::post('/', [ParticipantDisclosureController::class, 'store']); - Route::get('/edit', [ParticipantDisclosureController::class, 'edit'])->name( - 'edit'); - Route::put('{id}', [ParticipantDisclosureController::class, 'update']); + Route::get('/', 'create')->name('index'); + Route::get('/', 'create')->name('create'); + Route::post('/', 'store')->name('store'); + Route::get('/edit', 'edit')->name('edit'); + Route::match(['put', 'patch'], '{disclosureAuthorization}', 'update')->name('update'); }); Route::middleware('role:participant') ->name('fatherhood-assessment.') ->prefix('fatherhood-assessment.') ->group(function () { - Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('index'); - Route::post('/', [ParticipantDisclosureController::class, 'store']); - Route::get('edit', [ParticipantDisclosureController::class, 'edit'])->name('edit'); - Route::put('{id}', [ParticipantDisclosureController::class, 'update']); + Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('index'); + Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('create'); + Route::post('/', [ParticipantDisclosureController::class, 'store'])->name('store'); + Route::get('edit', [ParticipantDisclosureController::class, 'edit'])->name('edit'); + Route::match(['put', 'patch'],'{id}', [ParticipantDisclosureController::class, 'update'])->name('update'); }); }); diff --git a/tests/Feature/Http/Controllers/Intake/ParticipantDisclosureControllerTest.php b/tests/Feature/Http/Controllers/Intake/ParticipantDisclosureControllerTest.php new file mode 100644 index 00000000..672085b3 --- /dev/null +++ b/tests/Feature/Http/Controllers/Intake/ParticipantDisclosureControllerTest.php @@ -0,0 +1,211 @@ +create(); + $participant = Participant::factory()->create(['user_id' => $user->id]); + + // Login as the user + $this->actingAs($user); + + // Prepare disclosure agreement data + $data = $this->getDisclosureAgreementData(); + + // Send POST request to store endpoint + $response = $this->post(route('intake.disclosure.store'), $data); + + // Assert redirect to dashboard with success message + $response->assertRedirect(route('intake.dashboard')); + $response->assertSessionHas('success', 'Disclosure agreement saved successfully.'); + + // Assert the disclosure agreement was created in the database + $this->assertDatabaseHas('participant_consumer_authorization_for_disclosure_confidential_information', [ + 'participant_id' => $participant->id, + 'consumer_name' => $data['consumer_name'], + 'subject_name' => $data['subject_name'], + ]); + } + + /** + * Test updating an existing disclosure agreement. + * + * @return void + */ + public function test_update_disclosure_agreement() + { + // Create a user with a participant + $user = User::factory()->create(); + $participant = Participant::factory()->create(['user_id' => $user->id]); + + // Create an existing disclosure authorization + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create([ + 'participant_id' => $participant->id, + 'consumer_name' => 'Original Name', + ]); + + // Login as the user + $this->actingAs($user); + + // Prepare updated disclosure agreement data + $data = $this->getDisclosureAgreementData([ + 'consumer_name' => 'Updated Consumer Name', + ]); + + // Send PUT request to update endpoint + $response = $this->put(route('intake.disclosure.update'), $data); + + // Assert redirect to dashboard with success message + $response->assertRedirect(route('intake.dashboard')); + $response->assertSessionHas('success', 'Disclosure agreement updated successfully.'); + + // Assert the disclosure agreement was updated in the database + $this->assertDatabaseHas('participant_consumer_authorization_for_disclosure_confidential_information', [ + 'participant_id' => $participant->id, + 'consumer_name' => 'Updated Consumer Name', + ]); + } + + /** + * Test that a user cannot access another user's disclosure agreement. + * + * @return void + */ + public function test_cannot_access_other_users_disclosure_agreement() + { + // Create two users with participants + $user1 = User::factory()->create(); + $participant1 = Participant::factory()->create(['user_id' => $user1->id]); + + $user2 = User::factory()->create(); + $participant2 = Participant::factory()->create(['user_id' => $user2->id]); + + // Create a disclosure authorization for user2 + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create([ + 'participant_id' => $participant2->id, + ]); + + // Login as user1 + $this->actingAs($user1); + + // Prepare disclosure data with user2's participant ID + $data = $this->getDisclosureAgreementData([ + 'participant_id' => $participant2->id, + ]); + + // Send POST request to store endpoint + $response = $this->post(route('intake.disclosure.store'), $data); + + // The controller should only use the authenticated user's participant + // So the disclosure should be created for user1's participant, not user2's + $this->assertDatabaseHas('participant_consumer_authorization_for_disclosure_confidential_information', [ + 'participant_id' => $participant1->id, + 'consumer_name' => $data['consumer_name'], + ]); + } + + /** + * Helper method to generate disclosure agreement test data. + * + * @param array $overrides + * @return array + */ + private function getDisclosureAgreementData(array $overrides = []) + { + $data = [ + 'consumer_name' => $this->faker->name, + + // Authorized entities + 'is_dss_authorized' => true, + 'is_dys_authorized' => false, + 'is_mhd_authorized' => true, + 'is_dfas_authorized' => false, + 'is_mmac_authorized' => true, + 'is_fsd_authorized' => false, + 'is_cd_authorized' => true, + 'is_dls_authorized' => false, + 'other_authorized_entity' => $this->faker->company, + + // Subject information + 'subject_name' => $this->faker->name, + 'subject_phone' => $this->faker->phoneNumber, + 'subject_dob' => $this->faker->date, + 'subject_ssn' => '123-45-6789', + 'subject_address' => $this->faker->address, + 'subject_email' => $this->faker->email, + + // Recipients + 'disclose_to_attorney' => true, + 'attorney_name' => $this->faker->name, + 'disclose_to_employer' => false, + 'employer_name' => null, + 'disclose_to_legislator' => true, + 'legislator_name' => $this->faker->name, + 'disclose_to_governors_staff' => false, + 'other_recipient_details' => $this->faker->sentence, + + // Purpose of disclosure + 'purpose_eligibility_determination' => true, + 'purpose_legal_consultation' => false, + 'purpose_legal_proceedings' => true, + 'purpose_employment' => false, + 'purpose_complaint_investigation' => true, + 'purpose_treatment_planning' => false, + 'purpose_continuity_of_services' => true, + 'purpose_background_investigation' => false, + 'purpose_consumer_request' => true, + 'purpose_share_and_refer' => false, + 'purpose_other' => true, + 'other_purpose_details' => $this->faker->sentence, + + // Information to be disclosed + 'disclose_entire_file' => true, + 'disclose_licensure_information' => false, + 'disclose_medical_psychiatric_records' => true, + 'disclose_hotline_investigations' => false, + 'disclose_home_studies' => true, + 'disclose_eligibility_determinations' => false, + 'disclose_substance_abuse_treatment' => true, + 'disclose_client_employment_records' => false, + 'disclose_benefits_received' => true, + 'disclose_other_information' => false, + 'other_disclosure_details' => null, + + // Communication preferences + 'accept_text_messages' => true, + + // Signatures + 'consumer_signature' => $this->faker->name, + 'signature_date' => $this->faker->date, + 'witness_signature' => $this->faker->name, + 'witness_signature_date' => $this->faker->date, + 'guardian_signature' => null, + 'guardian_signature_date' => null, + + // Survey delivery methods + 'survey_by_email' => true, + 'survey_by_mail' => false, + 'survey_by_online' => true, + ]; + + return array_merge($data, $overrides); + } +} \ No newline at end of file diff --git a/tests/Feature/Intake/ParticipantDisclosureTest.php b/tests/Feature/Intake/ParticipantDisclosureTest.php new file mode 100644 index 00000000..815ea944 --- /dev/null +++ b/tests/Feature/Intake/ParticipantDisclosureTest.php @@ -0,0 +1,180 @@ +seed(PermissionsSeeder::class); + } + + /** + * Test that the disclosure form can be accessed by a participant. + */ + public function test_disclosure_form_is_rendered(): void + { + $participantUser = User::factory()->create(); + $participantUser->assignRole('participant'); + Participant::factory()->create(['user_id' => $participantUser->id]); + + $response = $this->actingAs($participantUser)->get(route('intake.disclosure.index')); + + $response->assertStatus(200); + } + + /** + * Test that a participant can store a new disclosure agreement. + */ + public function test_participant_can_create_disclosure(): void + { + $participantUser = User::factory()->create(); + $participantUser->assignRole('participant'); + $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + + $disclosureData = $this->getDisclosureData(); + + $this->actingAs($participantUser)->post(route('intake.disclosure.store'), $disclosureData); + + $this->assertNotNull($participant->disclosureAuthorization); + } + + /** + * Test that a participant can update an existing disclosure agreement. + */ + public function test_participant_can_update_disclosure(): void + { + $participantUser = User::factory()->create(); + $participantUser->assignRole('participant'); + $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + + $disclosureData = $this->getDisclosureData(); + $disclosure = $participant->disclosureAuthorization()->create($disclosureData); + + $updatedData = [ + ...$disclosureData, + 'consumer_name' => 'Jane Doe', + ]; + + // Test the update endpoint + $updateRoute = route('intake.disclosure.update', ['disclosureAuthorization' => $disclosure->id]); + $updateResponse = $this->actingAs($participantUser)->put($updateRoute, $updatedData); + + $updateResponse->assertRedirectToRoute('intake.fatherhood-assessment.index'); + + $disclosure->refresh(); + $this->assertEquals('Jane Doe', $disclosure->consumer_name); + } + + + /** + * Test that authorization_agreed is required. + */ + public function test_authorization_agreed_is_required(): void + { + $participantUser = User::factory()->create()->assignRole('participant'); + Participant::factory()->create(['user_id' => $participantUser->id]); + + $disclosureData = [ + 'authorization_date' => now()->format('Y-m-d'), + 'authorization_signature' => 'John Doe', + 'authorization_agreed' => false, // Not agreed + ]; + + $response = $this->actingAs($participantUser)->post(route('intake.disclosure.store'), $disclosureData); + + $response->assertSessionHasErrors('authorization_agreed'); + } + + public function getDisclosureData(): array + { + return [ + 'consumer_name' => 'John Doe', + + // Authorized entities + 'is_dss_authorized' => true, + 'is_dys_authorized' => false, + 'is_mhd_authorized' => true, + 'is_dfas_authorized' => false, + 'is_mmac_authorized' => true, + 'is_fsd_authorized' => false, + 'is_cd_authorized' => true, + 'is_dls_authorized' => false, + 'is_other_authorized' => true, + 'other_authorized_entity' => 'Test Entity', + + // Subject information + 'subject_name' => 'John Doe', + 'subject_phone' => '555-123-4567', + 'subject_dob' => '1990-01-01', + 'subject_ssn' => '123-45-6789', + 'subject_address' => '123 Test St', + 'subject_email' => 'john@example.com', + + // Recipients + 'disclose_to_attorney' => true, + 'attorney_name' => 'Jane Smith', + 'disclose_to_employer' => false, + 'employer_name' => null, + 'disclose_to_legislator' => true, + 'legislator_name' => 'Sen. Johnson', + 'disclose_to_governors_staff' => false, + 'other_recipient_details' => 'Test Recipient', + + // Purpose of disclosure + 'purpose_eligibility_determination' => true, + 'purpose_legal_consultation' => false, + 'purpose_legal_proceedings' => true, + 'purpose_employment' => false, + 'purpose_complaint_investigation' => true, + 'purpose_treatment_planning' => false, + 'purpose_continuity_of_services' => true, + 'purpose_background_investigation' => false, + 'purpose_consumer_request' => true, + 'purpose_share_and_refer' => false, + 'purpose_other' => true, + 'other_purpose_details' => 'Test Purpose', + + // Information to disclose + 'disclose_entire_file' => true, + 'disclose_licensure_information' => false, + 'disclose_medical_psychiatric_records' => true, + 'disclose_hotline_investigations' => false, + 'disclose_home_studies' => true, + 'disclose_eligibility_determinations' => false, + 'disclose_substance_abuse_treatment' => true, + 'disclose_client_employment_records' => false, + 'disclose_benefits_received' => true, + 'disclose_other_information' => false, + 'other_disclosure_details' => null, + + // Communication preferences + 'accept_text_messages' => true, + + // Signatures + 'consumer_signature' => 'John Doe', + 'signature_date' => now()->format('Y-m-d'), + 'witness_signature' => 'Jane Witness', + 'witness_signature_date' => now()->format('Y-m-d'), + 'guardian_signature' => null, + 'guardian_signature_date' => null, + + // Survey preferences + 'survey_by_email' => true, + 'survey_by_mail' => false, + 'survey_by_online' => true, + + 'date_completed' => now()->format('Y-m-d'), + ]; + + } +} \ No newline at end of file diff --git a/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php b/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php new file mode 100644 index 00000000..7530ff55 --- /dev/null +++ b/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php @@ -0,0 +1,107 @@ +create(); + + // Create a disclosure authorization for the participant + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create([ + 'participant_id' => $participant->id + ]); + + // Test the relationship from disclosure to participant + $this->assertInstanceOf(Participant::class, $disclosureAuth->participant); + $this->assertEquals($participant->id, $disclosureAuth->participant->id); + + // Test the relationship from participant to disclosure + $this->assertTrue($participant->disclosureAuthorization->is($disclosureAuth)); + } + + /** + * Test that the model uses UUIDs. + * + * @return void + */ + public function test_uses_uuids() + { + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create(); + + // UUID should be a string, not an integer + $this->assertIsString($disclosureAuth->id); + + // UUID should be 36 characters long (standard UUID format) + $this->assertEquals(36, strlen($disclosureAuth->id)); + } + + /** + * Test that the model casts date fields correctly. + * + * @return void + */ + public function test_date_casting() + { + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create(); + + // Test that date fields are cast to Carbon instances + $this->assertInstanceOf(\Carbon\Carbon::class, $disclosureAuth->subject_dob); + $this->assertInstanceOf(\Carbon\Carbon::class, $disclosureAuth->signature_date); + $this->assertInstanceOf(\Carbon\Carbon::class, $disclosureAuth->witness_signature_date); + + // If guardian_signature_date is not null, it should be a Carbon instance + if ($disclosureAuth->guardian_signature_date) { + $this->assertInstanceOf(\Carbon\Carbon::class, $disclosureAuth->guardian_signature_date); + } + + // If date_completed is not null, it should be a Carbon instance + if ($disclosureAuth->date_completed) { + $this->assertInstanceOf(\Carbon\Carbon::class, $disclosureAuth->date_completed); + } + } + + /** + * Test that boolean fields are cast correctly. + * + * @return void + */ + public function test_boolean_casting() + { + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create([ + 'is_dss_authorized' => 1, + 'disclose_to_attorney' => 1, + 'purpose_eligibility_determination' => 1, + 'disclose_entire_file' => 1, + 'accept_text_messages' => 1, + ]); + + // Test that boolean fields are cast to boolean + $this->assertIsBool($disclosureAuth->is_dss_authorized); + $this->assertIsBool($disclosureAuth->disclose_to_attorney); + $this->assertIsBool($disclosureAuth->purpose_eligibility_determination); + $this->assertIsBool($disclosureAuth->disclose_entire_file); + $this->assertIsBool($disclosureAuth->accept_text_messages); + + // Test that the values are true + $this->assertTrue($disclosureAuth->is_dss_authorized); + $this->assertTrue($disclosureAuth->disclose_to_attorney); + $this->assertTrue($disclosureAuth->purpose_eligibility_determination); + $this->assertTrue($disclosureAuth->disclose_entire_file); + $this->assertTrue($disclosureAuth->accept_text_messages); + } +} \ No newline at end of file diff --git a/tests/Unit/Models/ParticipantTest.php b/tests/Unit/Models/ParticipantTest.php new file mode 100644 index 00000000..9d568d43 --- /dev/null +++ b/tests/Unit/Models/ParticipantTest.php @@ -0,0 +1,60 @@ +create(); + + // Create a participant for the user + $participant = Participant::factory()->create([ + 'user_id' => $user->id + ]); + + // Test the relationship from participant to user + $this->assertInstanceOf(User::class, $participant->user); + $this->assertEquals($user->id, $participant->user->id); + } + + /** + * Test the relationship between Participant and ParticipantDisclosureAuthorization. + * + * @return void + */ + public function test_disclosure_authorization_relationship() + { + // Create a participant + $participant = Participant::factory()->create(); + + // Initially, the participant should not have a disclosure authorization + $this->assertNull($participant->disclosureAuthorization); + + // Create a disclosure authorization for the participant + $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create([ + 'participant_id' => $participant->id + ]); + + // Refresh the participant model + $participant->refresh(); + + // Now the participant should have a disclosure authorization + $this->assertInstanceOf(ParticipantDisclosureAuthorization::class, $participant->disclosureAuthorization); + $this->assertEquals($disclosureAuth->id, $participant->disclosureAuthorization->id); + } +} \ No newline at end of file From 4403dcb6766eaa729dadb96f9b03cf7100c6beda Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 11:06:47 -0500 Subject: [PATCH 03/40] Add Participant, Child, and Region Resources --- app/Http/Resources/ChildResource.php | 32 ++++++++++++++++ app/Http/Resources/ParticipantResource.php | 44 ++++++++++++++++++++++ app/Http/Resources/RegionResource.php | 27 +++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 app/Http/Resources/ChildResource.php create mode 100644 app/Http/Resources/ParticipantResource.php create mode 100644 app/Http/Resources/RegionResource.php diff --git a/app/Http/Resources/ChildResource.php b/app/Http/Resources/ChildResource.php new file mode 100644 index 00000000..e6c1ed5e --- /dev/null +++ b/app/Http/Resources/ChildResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'participantId' => $this->participant_id, + 'firstName' => $this->first_name, + 'lastName' => $this->last_name, + 'dateOfBirth' => $this->date_of_birth, + 'contact' => $this->contact, + 'childSupport' => $this->child_support, + 'createdAt' => $this->created_at, + 'updatedAt' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/ParticipantResource.php b/app/Http/Resources/ParticipantResource.php new file mode 100644 index 00000000..3e12309d --- /dev/null +++ b/app/Http/Resources/ParticipantResource.php @@ -0,0 +1,44 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'user_id' => $this->user_id, + 'user' => $this->whenLoaded('user', fn() => UserResource::make($this->user)), + 'region_id' => $this->region_id, + 'region' => $this->whenLoaded('region', RegionResource::make($this->region)), + 'children' => $this->whenLoaded('children', ChildResource::collection($this->children)), + 'address_line_1' => $this->address_line_1, + 'address_line_2' => $this->address_line_2, + 'city' => $this->city, + 'state' => $this->state, + 'zipcode' => $this->zipcode, + 'employer' => $this->employer, + 'cell_phone_number' => $this->cell_phone_number, + 'home_phone_number' => $this->home_phone_number, + 'work_phone_number' => $this->work_phone_number, + 'at_contact_number' => $this->at_contact_number, + 'marital_status' => $this->marital_status?->value, + 'ethnicity' => $this->ethnicity?->value, + 'monthly_child_support' => $this->monthly_child_support, + 'intake_date' => $this->intake_date, + ]; + } +} diff --git a/app/Http/Resources/RegionResource.php b/app/Http/Resources/RegionResource.php new file mode 100644 index 00000000..7d45f695 --- /dev/null +++ b/app/Http/Resources/RegionResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'description' => $this->description, + 'createdAt' => $this->created_at, + 'updatedAt' => $this->updated_at, + ]; + } +} From 27074f10e95c367f6013aafdbba94d625e2ecbff Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 11:54:22 -0500 Subject: [PATCH 04/40] Update Participant Disclosure Routes, Controller, Tests --- .../ParticipantDisclosureController.php | 69 +++++++++++++----- app/Models/Participant.php | 2 +- app/Services/ParticipantService.php | 19 ----- .../Intake/Disclosure/DisclosureCreate.tsx | 19 +++++ .../Intake/Disclosure/DisclosureDelete.tsx | 19 +++++ .../DisclosureEdit.tsx} | 4 +- .../Intake/Disclosure/DisclosureIndex.tsx | 19 +++++ .../Intake/Disclosure/DisclosureShow.tsx | 19 +++++ resources/js/types/participant.ts | 9 +++ routes/web.php | 12 +--- .../Intake/ParticipantDisclosureTest.php | 70 ++++++++++++++----- ...ParticipantDisclosureAuthorizationTest.php | 2 +- tests/Unit/Models/ParticipantTest.php | 26 ------- 13 files changed, 195 insertions(+), 94 deletions(-) create mode 100644 resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx create mode 100644 resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx rename resources/js/Pages/Intake/{Disclosure.tsx => Disclosure/DisclosureEdit.tsx} (81%) create mode 100644 resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx create mode 100644 resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx create mode 100644 resources/js/types/participant.ts diff --git a/app/Http/Controllers/Intake/ParticipantDisclosureController.php b/app/Http/Controllers/Intake/ParticipantDisclosureController.php index 8010f133..bb5a3311 100644 --- a/app/Http/Controllers/Intake/ParticipantDisclosureController.php +++ b/app/Http/Controllers/Intake/ParticipantDisclosureController.php @@ -5,6 +5,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Intake\ParticipantDisclosureAuthorizationStoreRequest; use App\Http\Requests\Intake\ParticipantDisclosureAuthorizationUpdateRequest; +use App\Http\Resources\ParticipantResource; use App\Models\ParticipantDisclosureAuthorization; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -13,13 +14,25 @@ class ParticipantDisclosureController extends Controller { + + public function index(Request $request): Response + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/Disclosure/DisclosureIndex',[ + 'participant' => ParticipantResource::make($participant), + 'disclosureAuthorizations' => $participant?->disclosureAuthorizations?->toArray(), + ]); + } + + /** * Display the registration view. */ public function create(Request $request): Response { - return Inertia::render('Intake/Disclosure',[ - 'participant' => $request->user()?->participant ?? [], + return Inertia::render('Intake/Disclosure/DisclosureCreate',[ + 'participant' => ParticipantResource::make($request->user()->participant), ]); } @@ -32,32 +45,38 @@ public function store(ParticipantDisclosureAuthorizationStoreRequest $request) { $validated = $request->validated(); - // Get the current participant $participant = $request->user()->participant; - - // Create or update the disclosure authorization - if ($participant->disclosureAuthorization) { - // Update existing authorization - $participant->disclosureAuthorization->update($validated); - $disclosureAuthorization = $participant->disclosureAuthorization; - } else { - // Create new authorization - $disclosureAuthorization = $participant->disclosureAuthorization()->create($validated); - } - - return redirect()->route('intake.fatherhood-assessment.index'); + + $participant->disclosureAuthorizations()->create($validated); + + return redirect()->back(303)->with('message', 'Disclosure Authorization Created Successfully'); } + + /** * Show the form for editing the disclosure agreement. */ - public function edit(Request $request): Response + public function show(Request $request, ParticipantDisclosureAuthorization $disclosureAuthorization): Response + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/Disclosure/DisclosureShow', [ + 'participant' => ParticipantResource::make($participant), + 'disclosureAuthorization' => $disclosureAuthorization, + ]); + } + + /** + * Show the form for editing the disclosure agreement. + */ + public function edit(Request $request, ParticipantDisclosureAuthorization $disclosureAuthorization): Response { $participant = $request->user()->participant; return Inertia::render('Intake/Disclosure', [ - 'participant' => $participant, - 'disclosureAuthorization' => $participant->disclosureAuthorization, + 'participant' => ParticipantResource::make($participant), + 'disclosureAuthorization' => $disclosureAuthorization, ]); } @@ -72,6 +91,18 @@ public function update(ParticipantDisclosureAuthorizationUpdateRequest $request, $disclosureAuthorization->update($validated); - return redirect()->route('intake.fatherhood-assessment.index'); + return redirect()->back(303)->with('message', 'Disclosure Authorization Updated Successfully'); + } + + + public function destroy(Request $request, ParticipantDisclosureAuthorization $disclosureAuthorization): RedirectResponse + { + if($disclosureAuthorization->participant_id !== $request->user()->participant->id) { + abort(403); + } + + $disclosureAuthorization->delete(); + + return redirect()->back(303)->with('message', 'Disclosure Authorization Deleted Successfully'); } } \ No newline at end of file diff --git a/app/Models/Participant.php b/app/Models/Participant.php index 9e8747ad..cacbfe4f 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -104,7 +104,7 @@ public function children(): HasMany /** * Get the disclosure authorizations for the participant. */ - public function disclosureAuthorization(): HasMany + public function disclosureAuthorizations(): HasMany { return $this->hasMany(ParticipantDisclosureAuthorization::class); } diff --git a/app/Services/ParticipantService.php b/app/Services/ParticipantService.php index c74f6405..2de6ccb7 100644 --- a/app/Services/ParticipantService.php +++ b/app/Services/ParticipantService.php @@ -25,24 +25,5 @@ public function addChildren(Participant $participant, array $childrenInfo): Coll return $participant->children()->createMany($childrenInfo); } - /** - * Create or update a participant's disclosure authorization. - * - * @param \App\Models\Participant $participant - * @param array $data - * @return \App\Models\ParticipantDisclosureAuthorization - */ - public function createOrUpdateDisclosureAuthorization($participant, array $data) - { - // Check if the participant already has a disclosure authorization - if ($participant->disclosureAuthorization) { - // Update existing authorization - $participant->disclosureAuthorization->update($data); - return $participant->disclosureAuthorization; - } - - // Create new authorization - return $participant->disclosureAuthorization()->create($data); - } } \ No newline at end of file diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx b/resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx new file mode 100644 index 00000000..4b25250f --- /dev/null +++ b/resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' + +interface DisclosureProps extends PageProps {} + +export const DisclosureCreate: React.FC = ({ auth }) => { + return ( + + +
+
Disclosure
+
+
+ ) +} + +export default DisclosureCreate diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx b/resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx new file mode 100644 index 00000000..4b776a4e --- /dev/null +++ b/resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' + +interface DisclosureProps extends PageProps {} + +export const DisclosureDelete: React.FC = ({ auth }) => { + return ( + + +
+
Disclosure
+
+
+ ) +} + +export default DisclosureDelete diff --git a/resources/js/Pages/Intake/Disclosure.tsx b/resources/js/Pages/Intake/Disclosure/DisclosureEdit.tsx similarity index 81% rename from resources/js/Pages/Intake/Disclosure.tsx rename to resources/js/Pages/Intake/Disclosure/DisclosureEdit.tsx index 7555c842..03ab88db 100644 --- a/resources/js/Pages/Intake/Disclosure.tsx +++ b/resources/js/Pages/Intake/Disclosure/DisclosureEdit.tsx @@ -5,7 +5,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' interface DisclosureProps extends PageProps {} -export const Disclosure: React.FC = ({ auth }) => { +export const DisclosureEdit: React.FC = ({ auth }) => { return ( @@ -16,4 +16,4 @@ export const Disclosure: React.FC = ({ auth }) => { ) } -export default Disclosure +export default DisclosureEdit diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx b/resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx new file mode 100644 index 00000000..3c5e4dcc --- /dev/null +++ b/resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' + +interface DisclosureProps extends PageProps {} + +export const DisclosureIndex: React.FC = ({ auth }) => { + return ( + + +
+
Disclosure
+
+
+ ) +} + +export default DisclosureIndex diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx b/resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx new file mode 100644 index 00000000..2bd4e34f --- /dev/null +++ b/resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' + +interface DisclosureProps extends PageProps {} + +export const DisclosureShow: React.FC = ({ auth }) => { + return ( + + +
+
Disclosure
+
+
+ ) +} + +export default DisclosureShow diff --git a/resources/js/types/participant.ts b/resources/js/types/participant.ts new file mode 100644 index 00000000..faf2907a --- /dev/null +++ b/resources/js/types/participant.ts @@ -0,0 +1,9 @@ +import type { User } from '@/types/index' + +export type Participant = { + id: string + user: User + + created_at: string + updated_at: string +} diff --git a/routes/web.php b/routes/web.php index 4a3b66cf..e0848340 100644 --- a/routes/web.php +++ b/routes/web.php @@ -47,16 +47,8 @@ }); Route::middleware('role:participant') - ->name('disclosure.') - ->prefix('disclosure') - ->controller(ParticipantDisclosureController::class) - ->group(function () { - Route::get('/', 'create')->name('index'); - Route::get('/', 'create')->name('create'); - Route::post('/', 'store')->name('store'); - Route::get('/edit', 'edit')->name('edit'); - Route::match(['put', 'patch'], '{disclosureAuthorization}', 'update')->name('update'); - }); + ->resource('disclosure', ParticipantDisclosureController::class) + ->parameter('disclosure', 'disclosureAuthorization'); Route::middleware('role:participant') ->name('fatherhood-assessment.') diff --git a/tests/Feature/Intake/ParticipantDisclosureTest.php b/tests/Feature/Intake/ParticipantDisclosureTest.php index 815ea944..92d4455f 100644 --- a/tests/Feature/Intake/ParticipantDisclosureTest.php +++ b/tests/Feature/Intake/ParticipantDisclosureTest.php @@ -6,6 +6,7 @@ use App\Models\User; use Database\Seeders\PermissionsSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; +use Inertia\Testing\AssertableInertia; use Tests\TestCase; class ParticipantDisclosureTest extends TestCase @@ -21,15 +22,49 @@ public function setUp(): void /** * Test that the disclosure form can be accessed by a participant. */ - public function test_disclosure_form_is_rendered(): void + public function test_disclosure_index_is_rendered(): void { $participantUser = User::factory()->create(); $participantUser->assignRole('participant'); - Participant::factory()->create(['user_id' => $participantUser->id]); + $participant = Participant::factory()->create(['user_id' => $participantUser->id]); $response = $this->actingAs($participantUser)->get(route('intake.disclosure.index')); $response->assertStatus(200); + + + $disclosureData = $this->getDisclosureData(); + $participant->disclosureAuthorizations()->create($disclosureData); + + $participantUser->refresh(); + $response = $this->actingAs($participantUser)->get(route('intake.disclosure.index')); + + $response->assertInertia(fn(AssertableInertia $page) => $page->count('disclosureAuthorizations', 1)); + + } + public function test_disclosure_create_form_is_rendered(): void + { + $participantUser = User::factory()->create(); + $participantUser->assignRole('participant'); + $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + + $response = $this->actingAs($participantUser)->get(route('intake.disclosure.create')); + + $response->assertStatus(200); + } + + public function test_disclosure_edit_is_rendered(): void + { + $participantUser = User::factory()->create(); + $participantUser->assignRole('participant'); + $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + + $disclosureData = $this->getDisclosureData(); + $disclosure = $participant->disclosureAuthorizations()->create($disclosureData); + + $response = $this->actingAs($participantUser)->get(route('intake.disclosure.update', $disclosure->id)); + + $response->assertStatus(200); } /** @@ -45,7 +80,7 @@ public function test_participant_can_create_disclosure(): void $this->actingAs($participantUser)->post(route('intake.disclosure.store'), $disclosureData); - $this->assertNotNull($participant->disclosureAuthorization); + $this->assertNotNull($participant->disclosureAuthorizations); } /** @@ -58,7 +93,7 @@ public function test_participant_can_update_disclosure(): void $participant = Participant::factory()->create(['user_id' => $participantUser->id]); $disclosureData = $this->getDisclosureData(); - $disclosure = $participant->disclosureAuthorization()->create($disclosureData); + $disclosure = $participant->disclosureAuthorizations()->create($disclosureData); $updatedData = [ ...$disclosureData, @@ -69,7 +104,7 @@ public function test_participant_can_update_disclosure(): void $updateRoute = route('intake.disclosure.update', ['disclosureAuthorization' => $disclosure->id]); $updateResponse = $this->actingAs($participantUser)->put($updateRoute, $updatedData); - $updateResponse->assertRedirectToRoute('intake.fatherhood-assessment.index'); + $updateResponse->assertRedirect(); $disclosure->refresh(); $this->assertEquals('Jane Doe', $disclosure->consumer_name); @@ -77,24 +112,27 @@ public function test_participant_can_update_disclosure(): void /** - * Test that authorization_agreed is required. + * Test that a participant can update an existing disclosure agreement. */ - public function test_authorization_agreed_is_required(): void + public function test_participant_can_delete_disclosure(): void { - $participantUser = User::factory()->create()->assignRole('participant'); - Participant::factory()->create(['user_id' => $participantUser->id]); + $participantUser = User::factory()->create(); + $participantUser->assignRole('participant'); + $participant = Participant::factory()->create(['user_id' => $participantUser->id]); - $disclosureData = [ - 'authorization_date' => now()->format('Y-m-d'), - 'authorization_signature' => 'John Doe', - 'authorization_agreed' => false, // Not agreed - ]; + $disclosureData = $this->getDisclosureData(); + $disclosure = $participant->disclosureAuthorizations()->create($disclosureData); + + // Test the update endpoint + $updateRoute = route('intake.disclosure.destroy', ['disclosureAuthorization' => $disclosure->id]); + $updateResponse = $this->actingAs($participantUser)->delete($updateRoute); - $response = $this->actingAs($participantUser)->post(route('intake.disclosure.store'), $disclosureData); + $participant->refresh(); - $response->assertSessionHasErrors('authorization_agreed'); + $this->assertCount(0, $participant->disclosureAuthorizations); } + public function getDisclosureData(): array { return [ diff --git a/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php b/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php index 7530ff55..fd32b4ff 100644 --- a/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php +++ b/tests/Unit/Models/ParticipantDisclosureAuthorizationTest.php @@ -31,7 +31,7 @@ public function test_participant_relationship() $this->assertEquals($participant->id, $disclosureAuth->participant->id); // Test the relationship from participant to disclosure - $this->assertTrue($participant->disclosureAuthorization->is($disclosureAuth)); + $this->assertTrue($participant->disclosureAuthorizations->is($disclosureAuth)); } /** diff --git a/tests/Unit/Models/ParticipantTest.php b/tests/Unit/Models/ParticipantTest.php index 9d568d43..7c63562e 100644 --- a/tests/Unit/Models/ParticipantTest.php +++ b/tests/Unit/Models/ParticipantTest.php @@ -31,30 +31,4 @@ public function test_user_relationship() $this->assertInstanceOf(User::class, $participant->user); $this->assertEquals($user->id, $participant->user->id); } - - /** - * Test the relationship between Participant and ParticipantDisclosureAuthorization. - * - * @return void - */ - public function test_disclosure_authorization_relationship() - { - // Create a participant - $participant = Participant::factory()->create(); - - // Initially, the participant should not have a disclosure authorization - $this->assertNull($participant->disclosureAuthorization); - - // Create a disclosure authorization for the participant - $disclosureAuth = ParticipantDisclosureAuthorization::factory()->create([ - 'participant_id' => $participant->id - ]); - - // Refresh the participant model - $participant->refresh(); - - // Now the participant should have a disclosure authorization - $this->assertInstanceOf(ParticipantDisclosureAuthorization::class, $participant->disclosureAuthorization); - $this->assertEquals($disclosureAuth->id, $participant->disclosureAuthorization->id); - } } \ No newline at end of file From 6472403accc03c5eeb1ae91ca33791bdcab62fdf Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 12:19:04 -0500 Subject: [PATCH 05/40] Rename Front End Pages for Participant Disclosure --- .../ParticipantDisclosureController.php | 8 +++--- .../js/Pages/Intake/Assesment/Create.tsx | 27 +++++++++++++++++++ .../{DisclosureShow.tsx => Create.tsx} | 4 +-- .../Intake/Disclosure/DisclosureCreate.tsx | 19 ------------- .../{DisclosureEdit.tsx => Edit.tsx} | 4 +-- .../{DisclosureIndex.tsx => Index.tsx} | 4 +-- .../{DisclosureDelete.tsx => Show.tsx} | 4 +-- .../Intake/ParticipantDisclosureTest.php | 2 +- .../Intake/ParticipantRegisterTest.php | 3 --- tests/Unit/Models/ParticipantTest.php | 1 - 10 files changed, 40 insertions(+), 36 deletions(-) create mode 100644 resources/js/Pages/Intake/Assesment/Create.tsx rename resources/js/Pages/Intake/Disclosure/{DisclosureShow.tsx => Create.tsx} (81%) delete mode 100644 resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx rename resources/js/Pages/Intake/Disclosure/{DisclosureEdit.tsx => Edit.tsx} (81%) rename resources/js/Pages/Intake/Disclosure/{DisclosureIndex.tsx => Index.tsx} (80%) rename resources/js/Pages/Intake/Disclosure/{DisclosureDelete.tsx => Show.tsx} (80%) diff --git a/app/Http/Controllers/Intake/ParticipantDisclosureController.php b/app/Http/Controllers/Intake/ParticipantDisclosureController.php index bb5a3311..7f69f991 100644 --- a/app/Http/Controllers/Intake/ParticipantDisclosureController.php +++ b/app/Http/Controllers/Intake/ParticipantDisclosureController.php @@ -19,7 +19,7 @@ public function index(Request $request): Response { $participant = $request->user()->participant; - return Inertia::render('Intake/Disclosure/DisclosureIndex',[ + return Inertia::render('Intake/Disclosure/Index',[ 'participant' => ParticipantResource::make($participant), 'disclosureAuthorizations' => $participant?->disclosureAuthorizations?->toArray(), ]); @@ -31,7 +31,7 @@ public function index(Request $request): Response */ public function create(Request $request): Response { - return Inertia::render('Intake/Disclosure/DisclosureCreate',[ + return Inertia::render('Intake/Disclosure/Create',[ 'participant' => ParticipantResource::make($request->user()->participant), ]); } @@ -61,7 +61,7 @@ public function show(Request $request, ParticipantDisclosureAuthorization $disc { $participant = $request->user()->participant; - return Inertia::render('Intake/Disclosure/DisclosureShow', [ + return Inertia::render('Intake/Disclosure/Show', [ 'participant' => ParticipantResource::make($participant), 'disclosureAuthorization' => $disclosureAuthorization, ]); @@ -74,7 +74,7 @@ public function edit(Request $request, ParticipantDisclosureAuthorization $discl { $participant = $request->user()->participant; - return Inertia::render('Intake/Disclosure', [ + return Inertia::render('Intake/Disclosure/Edit', [ 'participant' => ParticipantResource::make($participant), 'disclosureAuthorization' => $disclosureAuthorization, ]); diff --git a/resources/js/Pages/Intake/Assesment/Create.tsx b/resources/js/Pages/Intake/Assesment/Create.tsx new file mode 100644 index 00000000..1d9bc10a --- /dev/null +++ b/resources/js/Pages/Intake/Assesment/Create.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Create: React.FC = ({ + auth, + participant, +}) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Create diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx b/resources/js/Pages/Intake/Disclosure/Create.tsx similarity index 81% rename from resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx rename to resources/js/Pages/Intake/Disclosure/Create.tsx index 2bd4e34f..d272a88a 100644 --- a/resources/js/Pages/Intake/Disclosure/DisclosureShow.tsx +++ b/resources/js/Pages/Intake/Disclosure/Create.tsx @@ -5,7 +5,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' interface DisclosureProps extends PageProps {} -export const DisclosureShow: React.FC = ({ auth }) => { +export const Create: React.FC = ({ auth }) => { return ( @@ -16,4 +16,4 @@ export const DisclosureShow: React.FC = ({ auth }) => { ) } -export default DisclosureShow +export default Create diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx b/resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx deleted file mode 100644 index 4b25250f..00000000 --- a/resources/js/Pages/Intake/Disclosure/DisclosureCreate.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import { Head } from '@inertiajs/react' -import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' - -interface DisclosureProps extends PageProps {} - -export const DisclosureCreate: React.FC = ({ auth }) => { - return ( - - -
-
Disclosure
-
-
- ) -} - -export default DisclosureCreate diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureEdit.tsx b/resources/js/Pages/Intake/Disclosure/Edit.tsx similarity index 81% rename from resources/js/Pages/Intake/Disclosure/DisclosureEdit.tsx rename to resources/js/Pages/Intake/Disclosure/Edit.tsx index 03ab88db..cadfde5a 100644 --- a/resources/js/Pages/Intake/Disclosure/DisclosureEdit.tsx +++ b/resources/js/Pages/Intake/Disclosure/Edit.tsx @@ -5,7 +5,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' interface DisclosureProps extends PageProps {} -export const DisclosureEdit: React.FC = ({ auth }) => { +export const Edit: React.FC = ({ auth }) => { return ( @@ -16,4 +16,4 @@ export const DisclosureEdit: React.FC = ({ auth }) => { ) } -export default DisclosureEdit +export default Edit diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx b/resources/js/Pages/Intake/Disclosure/Index.tsx similarity index 80% rename from resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx rename to resources/js/Pages/Intake/Disclosure/Index.tsx index 3c5e4dcc..c7beeeb2 100644 --- a/resources/js/Pages/Intake/Disclosure/DisclosureIndex.tsx +++ b/resources/js/Pages/Intake/Disclosure/Index.tsx @@ -5,7 +5,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' interface DisclosureProps extends PageProps {} -export const DisclosureIndex: React.FC = ({ auth }) => { +export const Index: React.FC = ({ auth }) => { return ( @@ -16,4 +16,4 @@ export const DisclosureIndex: React.FC = ({ auth }) => { ) } -export default DisclosureIndex +export default Index diff --git a/resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx b/resources/js/Pages/Intake/Disclosure/Show.tsx similarity index 80% rename from resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx rename to resources/js/Pages/Intake/Disclosure/Show.tsx index 4b776a4e..4f90a608 100644 --- a/resources/js/Pages/Intake/Disclosure/DisclosureDelete.tsx +++ b/resources/js/Pages/Intake/Disclosure/Show.tsx @@ -5,7 +5,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' interface DisclosureProps extends PageProps {} -export const DisclosureDelete: React.FC = ({ auth }) => { +export const Show: React.FC = ({ auth }) => { return ( @@ -16,4 +16,4 @@ export const DisclosureDelete: React.FC = ({ auth }) => { ) } -export default DisclosureDelete +export default Show diff --git a/tests/Feature/Intake/ParticipantDisclosureTest.php b/tests/Feature/Intake/ParticipantDisclosureTest.php index 92d4455f..17b0d708 100644 --- a/tests/Feature/Intake/ParticipantDisclosureTest.php +++ b/tests/Feature/Intake/ParticipantDisclosureTest.php @@ -20,7 +20,7 @@ public function setUp(): void } /** - * Test that the disclosure form can be accessed by a participant. + * Test that the disclosure index is rendered properly */ public function test_disclosure_index_is_rendered(): void { diff --git a/tests/Feature/Intake/ParticipantRegisterTest.php b/tests/Feature/Intake/ParticipantRegisterTest.php index fc24add7..6f779438 100644 --- a/tests/Feature/Intake/ParticipantRegisterTest.php +++ b/tests/Feature/Intake/ParticipantRegisterTest.php @@ -19,9 +19,6 @@ public function setUp(): void $this->seed(PermissionsSeeder::class); } - /** - * A basic feature test example. - */ public function test_intake_form_can_only_be_accessed_by_intake_role(): void { diff --git a/tests/Unit/Models/ParticipantTest.php b/tests/Unit/Models/ParticipantTest.php index 7c63562e..4c5727b5 100644 --- a/tests/Unit/Models/ParticipantTest.php +++ b/tests/Unit/Models/ParticipantTest.php @@ -3,7 +3,6 @@ namespace Tests\Unit\Models; use App\Models\Participant; -use App\Models\ParticipantDisclosureAuthorization; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; From 44d78816cbc2dfdfa8c0533935c6798079abf5f3 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 13:04:12 -0500 Subject: [PATCH 06/40] Remove unused variable assignments --- tests/Feature/Intake/ParticipantDisclosureTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Feature/Intake/ParticipantDisclosureTest.php b/tests/Feature/Intake/ParticipantDisclosureTest.php index 17b0d708..f7fd49f9 100644 --- a/tests/Feature/Intake/ParticipantDisclosureTest.php +++ b/tests/Feature/Intake/ParticipantDisclosureTest.php @@ -46,7 +46,7 @@ public function test_disclosure_create_form_is_rendered(): void { $participantUser = User::factory()->create(); $participantUser->assignRole('participant'); - $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + Participant::factory()->create(['user_id' => $participantUser->id]); $response = $this->actingAs($participantUser)->get(route('intake.disclosure.create')); @@ -125,7 +125,7 @@ public function test_participant_can_delete_disclosure(): void // Test the update endpoint $updateRoute = route('intake.disclosure.destroy', ['disclosureAuthorization' => $disclosure->id]); - $updateResponse = $this->actingAs($participantUser)->delete($updateRoute); + $this->actingAs($participantUser)->delete($updateRoute); $participant->refresh(); From c8769c59a13f3edd6790e9f2cb6a11dabd5b8297 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 13:50:58 -0500 Subject: [PATCH 07/40] update participant factory to add permission --- database/factories/ParticipantFactory.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/database/factories/ParticipantFactory.php b/database/factories/ParticipantFactory.php index a788f24c..f4a78b70 100644 --- a/database/factories/ParticipantFactory.php +++ b/database/factories/ParticipantFactory.php @@ -43,4 +43,11 @@ public function definition(): array ]; } + + public function configure(): static + { + return $this->afterCreating(function (Participant $participant) { + $participant->user->assignRole('participant'); + }); + } } From 3ccc43a1625019e80ff739467a97a11a38879882 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 13:52:08 -0500 Subject: [PATCH 08/40] implement participant fatherhood assessment form --- .../ParticipantDisclosureController.php | 2 +- ...ticipantFatherhoodAssessmentController.php | 64 ++++++--- ...eParticipantFatherhoodAssesmentRequest.php | 28 ---- ...ParticipantFatherhoodAssessmentRequest.php | 50 +++++++ ...eParticipantFatherhoodAssesmentRequest.php | 28 ---- ...ParticipantFatherhoodAssessmentRequest.php | 50 +++++++ app/Models/Participant.php | 11 +- .../ParticipantFatherhoodAssessment.php | 66 ++++++++++ app/Rules/ValidSocialSecurityNumber.php | 66 ++++++++++ ...ParticipantFatherhoodAssessmentFactory.php | 49 +++++++ ...articipant_fatherhood_assesments_table.php | 55 ++++++++ .../Create.tsx | 0 .../Intake/FatherhoodAssessment/Edit.tsx | 24 ++++ .../Intake/FatherhoodAssessment/Index.tsx | 24 ++++ .../Intake/FatherhoodAssessment/Show.tsx | 24 ++++ routes/web.php | 14 +- .../ParticipantFatherhoodAssessmentTest.php | 123 ++++++++++++++++++ 17 files changed, 593 insertions(+), 85 deletions(-) delete mode 100644 app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php create mode 100644 app/Http/Requests/Intake/StoreParticipantFatherhoodAssessmentRequest.php delete mode 100644 app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php create mode 100644 app/Http/Requests/Intake/UpdateParticipantFatherhoodAssessmentRequest.php create mode 100644 app/Models/ParticipantFatherhoodAssessment.php create mode 100644 app/Rules/ValidSocialSecurityNumber.php create mode 100644 database/factories/ParticipantFatherhoodAssessmentFactory.php create mode 100644 database/migrations/2025_03_02_184237_create_participant_fatherhood_assesments_table.php rename resources/js/Pages/Intake/{Assesment => FatherhoodAssessment}/Create.tsx (100%) create mode 100644 resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx create mode 100644 resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx create mode 100644 resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx create mode 100644 tests/Feature/Intake/ParticipantFatherhoodAssessmentTest.php diff --git a/app/Http/Controllers/Intake/ParticipantDisclosureController.php b/app/Http/Controllers/Intake/ParticipantDisclosureController.php index 7f69f991..4fd4dc8d 100644 --- a/app/Http/Controllers/Intake/ParticipantDisclosureController.php +++ b/app/Http/Controllers/Intake/ParticipantDisclosureController.php @@ -27,7 +27,7 @@ public function index(Request $request): Response /** - * Display the registration view. + * Display the create view. */ public function create(Request $request): Response { diff --git a/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php b/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php index abe8074d..965c48cd 100644 --- a/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php +++ b/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php @@ -3,65 +3,95 @@ namespace App\Http\Controllers\Intake; use App\Http\Controllers\Controller; -use App\Http\Requests\StoreParticipantFatherhoodAssesmentRequest; -use App\Http\Requests\UpdateParticipantFatherhoodAssesmentRequest; -use App\Models\ParticipantFatherhoodAssesment; +use App\Http\Requests\Intake\StoreParticipantFatherhoodAssessmentRequest; +use App\Http\Requests\Intake\UpdateParticipantFatherhoodAssessmentRequest; +use App\Http\Resources\ParticipantResource; +use App\Models\ParticipantFatherhoodAssessment; +use Inertia\Inertia; +use Illuminate\Http\Request; +use Inertia\Response; class ParticipantFatherhoodAssessmentController extends Controller { /** * Display a listing of the resource. */ - public function index() + public function index(Request $request) { - // + $participant = $request->user()->participant; + + return Inertia::render('Intake/FatherhoodAssessment/Index',[ + 'participant' => ParticipantResource::make($participant), + 'fatherhoodAssessments' => $participant?->fatherhoodAssessments?->toArray(), + ]); } /** * Show the form for creating a new resource. */ - public function create() + public function create(Request $request): Response { - // + return Inertia::render('Intake/FatherhoodAssessment/Create',[ + 'participant' => ParticipantResource::make($request->user()->participant), + ]); } /** * Store a newly created resource in storage. */ - public function store(StoreParticipantFatherhoodAssesmentRequest $request) + public function store(StoreParticipantFatherhoodAssessmentRequest $request) { - // + $validated = $request->validated(); + $participant = $request->user()->participant; + + $participant->fatherhoodAssessments()->create($validated); + return redirect()->back(303)->with('message', 'Created Successfully'); } /** * Display the specified resource. */ - public function show(ParticipantFatherhoodAssesment $participantFatherhoodAssesment) + public function show(Request $request, ParticipantFatherhoodAssessment $fatherhoodAssessment) { - // + $participant = $request->user()->participant; + + return Inertia::render('Intake/FatherhoodAssessment/Show', [ + 'participant' => ParticipantResource::make($participant), + 'fatherhoodAssessment' => $fatherhoodAssessment->toArray(), + ]); } /** * Show the form for editing the specified resource. */ - public function edit(ParticipantFatherhoodAssesment $participantFatherhoodAssesment) + public function edit(Request $request, ParticipantFatherhoodAssessment $fatherhoodAssessment) { - // + $participant = $request->user()->participant; + + return Inertia::render('Intake/FatherhoodAssessment/Edit', [ + 'participant' => ParticipantResource::make($participant), + 'fatherhoodAssessment' => $fatherhoodAssessment->toArray(), + ]); } /** * Update the specified resource in storage. */ - public function update(UpdateParticipantFatherhoodAssesmentRequest $request, ParticipantFatherhoodAssesment $participantFatherhoodAssesment) + public function update(UpdateParticipantFatherhoodAssessmentRequest $request, ParticipantFatherhoodAssessment $fatherhoodAssessment) { - // + $validated = $request->validated(); + $fatherhoodAssessment->update($validated); + + return redirect()->back(303)->with('message', 'Updated Successfully'); } /** * Remove the specified resource from storage. */ - public function destroy(ParticipantFatherhoodAssesment $participantFatherhoodAssesment) + public function destroy(ParticipantFatherhoodAssessment $fatherhoodAssessment) { - // + $fatherhoodAssessment->delete(); + + return redirect()->back(303)->with('message', 'Deleted Successfully'); } } diff --git a/app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php b/app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php deleted file mode 100644 index b30ad429..00000000 --- a/app/Http/Requests/Intake/StoreParticipantFatherhoodAssesmentRequest.php +++ /dev/null @@ -1,28 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - // - ]; - } -} diff --git a/app/Http/Requests/Intake/StoreParticipantFatherhoodAssessmentRequest.php b/app/Http/Requests/Intake/StoreParticipantFatherhoodAssessmentRequest.php new file mode 100644 index 00000000..166c0ea0 --- /dev/null +++ b/app/Http/Requests/Intake/StoreParticipantFatherhoodAssessmentRequest.php @@ -0,0 +1,50 @@ +|string> + */ + public function rules(): array + { + return [ + 'vendor_name' => 'sometimes|string|max:191', + 'participant_name' => 'required|string|max:191', + 'date_of_birth' => 'required|date|before:today', + 'social_security_number' => ['required','string','max:191', new ValidSocialSecurityNumber()], + 'is_missouri_resident' => 'required|boolean', + 'child_is_under_18' => 'required|boolean', + 'is_financially_eligible' => 'required|boolean', + 'drivers_license_provided' => 'required|boolean', + 'utility_bill_provided' => 'required|boolean', + 'pay_stub_provided' => 'required|boolean', + 'written_employer_statement_provided' => 'required|boolean', + 'social_security_benefits_provided' => 'required|boolean', + 'self_attestation_provided' => 'required|boolean', + 'unemployment_compensation_provided' => 'required|boolean', + 'other_provided' => 'required|boolean', + 'other_provided_name' => 'nullable|string|max:191|required_if:other_provided,true', + 'gross_monthly_household_income' => 'required|numeric|min:0', + 'number_of_family_members' => 'required|integer|min:1', + 'percentage_of_fpl' => 'required|numeric|min:0|max:99.99', + 'approved_for_services' => 'nullable|boolean', + 'state_agency_review_date' => 'nullable|date', + + ]; + } +} diff --git a/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php b/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php deleted file mode 100644 index 5767e1ae..00000000 --- a/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssesmentRequest.php +++ /dev/null @@ -1,28 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - // - ]; - } -} diff --git a/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssessmentRequest.php b/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssessmentRequest.php new file mode 100644 index 00000000..8e15fc25 --- /dev/null +++ b/app/Http/Requests/Intake/UpdateParticipantFatherhoodAssessmentRequest.php @@ -0,0 +1,50 @@ +|string> + */ + public function rules(): array + { + return [ + 'vendor_name' => 'sometimes|string|max:191', + 'participant_name' => 'required|string|max:191', + 'date_of_birth' => 'required|date|before:today', + 'social_security_number' => ['required','string','max:191', new ValidSocialSecurityNumber()], + 'is_missouri_resident' => 'required|boolean', + 'child_is_under_18' => 'required|boolean', + 'is_financially_eligible' => 'required|boolean', + 'drivers_license_provided' => 'required|boolean', + 'utility_bill_provided' => 'required|boolean', + 'pay_stub_provided' => 'required|boolean', + 'written_employer_statement_provided' => 'required|boolean', + 'social_security_benefits_provided' => 'required|boolean', + 'self_attestation_provided' => 'required|boolean', + 'unemployment_compensation_provided' => 'required|boolean', + 'other_provided' => 'required|boolean', + 'other_provided_name' => 'nullable|string|max:191|required_if:other_provided,true', + 'gross_monthly_household_income' => 'required|numeric|min:0', + 'number_of_family_members' => 'required|integer|min:1', + 'percentage_of_fpl' => 'required|numeric|min:0|max:99.99', + 'approved_for_services' => 'nullable|boolean', + 'state_agency_review_date' => 'nullable|date', + + ]; + } +} diff --git a/app/Models/Participant.php b/app/Models/Participant.php index cacbfe4f..e53ae51c 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -11,7 +11,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\HasOne; /** * @property string $id @@ -108,5 +107,13 @@ public function disclosureAuthorizations(): HasMany { return $this->hasMany(ParticipantDisclosureAuthorization::class); } - + + /** + * Get the Fatherhood Assessments for the participant. + */ + public function fatherhoodAssessments(): HasMany + { + return $this->hasMany(ParticipantFatherhoodAssessment::class); + } + } diff --git a/app/Models/ParticipantFatherhoodAssessment.php b/app/Models/ParticipantFatherhoodAssessment.php new file mode 100644 index 00000000..6a884c0a --- /dev/null +++ b/app/Models/ParticipantFatherhoodAssessment.php @@ -0,0 +1,66 @@ + 'boolean', + 'child_is_under_18' => 'boolean', + 'is_financially_eligible' => 'boolean', + 'drivers_license_provided' => 'boolean', + 'utility_bill_provided' => 'boolean', + 'pay_stub_provided' => 'boolean', + 'written_employer_statement_provided' => 'boolean', + 'social_security_number' => 'encrypted', + 'social_security_benefits_provided' => 'boolean', + 'self_attestation_provided' => 'boolean', + 'unemployment_compensation_provided' => 'boolean', + 'other_provided' => 'boolean', + 'approved_for_services' => 'boolean', + 'date_of_birth' => 'date', + 'state_agency_review_date' => 'date', + 'date_completed' => 'date', + ]; + + public function participant(): BelongsTo + { + return $this->belongsTo(Participant::class); + } +} diff --git a/app/Rules/ValidSocialSecurityNumber.php b/app/Rules/ValidSocialSecurityNumber.php new file mode 100644 index 00000000..c4e887d0 --- /dev/null +++ b/app/Rules/ValidSocialSecurityNumber.php @@ -0,0 +1,66 @@ +message()); + return; + } + + // Split into the three parts + $area = substr($ssn, 0, 3); // First 3 digits + $group = substr($ssn, 3, 2); // Middle 2 digits + $serial = substr($ssn, 5, 4); // Last 4 digits + + // Basic SSN validation rules + // 1. Cannot start with 000 + if ($area === '000') { + $fail($this->message()); + return; + } + + // 2. Cannot start with 666 + if ($area === '666') { + $fail($this->message()); + return; + } + + // 3. Cannot start with 900-999 (reserved for ITINs and other special cases) + if ($area >= '900' && $area <= '999') { + $fail($this->message()); + return; + } + + // 4. Middle group cannot be 00 + if ($group === '00') { + $fail($this->message()); + return; + } + + // 5. Serial number cannot be 0000 + if ($serial === '0000') { + $fail($this->message()); + return; + } + + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message(): string + { + return 'The :attribute must be a valid Social Security Number.'; + } +} \ No newline at end of file diff --git a/database/factories/ParticipantFatherhoodAssessmentFactory.php b/database/factories/ParticipantFatherhoodAssessmentFactory.php new file mode 100644 index 00000000..70816118 --- /dev/null +++ b/database/factories/ParticipantFatherhoodAssessmentFactory.php @@ -0,0 +1,49 @@ + + */ +class ParticipantFatherhoodAssessmentFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $otherProvided = $this->faker->boolean(); + + return [ + 'id' => $this->faker->uuid(), + 'participant_id' => Participant::factory(), + 'vendor_name' => 'Good Dads', + 'participant_name' => $this->faker->name(), + 'date_of_birth' => $this->faker->dateTimeBetween('-50 years', '-18 years')->format('Y-m-d'), + 'social_security_number' => $this->faker->unique()->numerify('###-##-####'), + 'is_missouri_resident' => $this->faker->boolean(), + 'child_is_under_18' => $this->faker->boolean(), + 'is_financially_eligible' => $this->faker->boolean(), + 'drivers_license_provided' => $this->faker->boolean(), + 'utility_bill_provided' => $this->faker->boolean(), + 'pay_stub_provided' => $this->faker->boolean(), + 'written_employer_statement_provided' => $this->faker->boolean(), + 'social_security_benefits_provided' => $this->faker->boolean(), + 'self_attestation_provided' => $this->faker->boolean(), + 'unemployment_compensation_provided' => $this->faker->boolean(), + 'other_provided' => $otherProvided, + 'other_provided_name' => $otherProvided ? $this->faker->name() : null, + 'gross_monthly_household_income' => $this->faker->randomFloat(2, 0, 10000), + 'number_of_family_members' => $this->faker->numberBetween(1, 10), + 'percentage_of_fpl' => $this->faker->randomFloat(2, 0, 400), + 'approved_for_services' => $this->faker->boolean(), + 'state_agency_review_date' => $this->faker->optional()->date(), + 'date_completed' => $this->faker->optional()->date(), + ]; + } +} diff --git a/database/migrations/2025_03_02_184237_create_participant_fatherhood_assesments_table.php b/database/migrations/2025_03_02_184237_create_participant_fatherhood_assesments_table.php new file mode 100644 index 00000000..2f862f53 --- /dev/null +++ b/database/migrations/2025_03_02_184237_create_participant_fatherhood_assesments_table.php @@ -0,0 +1,55 @@ +uuid('id')->primary(); + $table->foreignUuid('participant_id')->constrained(); + $table->string('vendor_name')->default('Good Dads'); + $table->string('participant_name'); + $table->date('date_of_birth'); + $table->text('social_security_number'); + $table->boolean('is_missouri_resident'); + $table->boolean('child_is_under_18'); + $table->boolean('is_financially_eligible'); + $table->boolean('drivers_license_provided'); + $table->boolean('utility_bill_provided'); + $table->boolean('pay_stub_provided'); + $table->boolean('written_employer_statement_provided'); + $table->boolean('social_security_benefits_provided'); + $table->boolean('self_attestation_provided'); + $table->boolean('unemployment_compensation_provided'); + $table->boolean('other_provided'); + $table->string('other_provided_name')->nullable(); + $table->decimal('gross_monthly_household_income', 10, 2); + $table->integer('number_of_family_members'); + $table->decimal('percentage_of_fpl', 5, 2); + + // Waiting confirmation from Good Dads if this is needed + $table->boolean('approved_for_services')->nullable(); + $table->date('state_agency_review_date')->nullable(); + + // Marked completed on + $table->date('date_completed')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('participant_fatherhood_assessments'); + } +}; diff --git a/resources/js/Pages/Intake/Assesment/Create.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx similarity index 100% rename from resources/js/Pages/Intake/Assesment/Create.tsx rename to resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx new file mode 100644 index 00000000..7e209d41 --- /dev/null +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Edit: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Edit diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx new file mode 100644 index 00000000..d957666b --- /dev/null +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Index: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Index diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx new file mode 100644 index 00000000..f5858cc8 --- /dev/null +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Show: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Show diff --git a/routes/web.php b/routes/web.php index e0848340..10d687a9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,10 +2,12 @@ use App\Http\Controllers\Intake\IntakeController; use App\Http\Controllers\Intake\ParticipantDisclosureController; +use App\Http\Controllers\Intake\ParticipantFatherhoodAssessmentController; use App\Http\Controllers\Intake\ParticipantRegistrationController; use App\Http\Controllers\Intake\ParticipantSignupController; use App\Http\Controllers\LegalController; use App\Http\Controllers\ProfileController; +use App\Models\ParticipantFatherhoodAssesment; use Illuminate\Support\Facades\Route; use Inertia\Inertia; use App\Http\Controllers\UsersController; @@ -51,15 +53,9 @@ ->parameter('disclosure', 'disclosureAuthorization'); Route::middleware('role:participant') - ->name('fatherhood-assessment.') - ->prefix('fatherhood-assessment.') - ->group(function () { - Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('index'); - Route::get('/', [ParticipantDisclosureController::class, 'create'])->name('create'); - Route::post('/', [ParticipantDisclosureController::class, 'store'])->name('store'); - Route::get('edit', [ParticipantDisclosureController::class, 'edit'])->name('edit'); - Route::match(['put', 'patch'],'{id}', [ParticipantDisclosureController::class, 'update'])->name('update'); - }); + ->resource('fatherhood-assessment', ParticipantFatherhoodAssessmentController::class) + ->parameter('fatherhood-assessment', 'fatherhoodAssessment'); + }); diff --git a/tests/Feature/Intake/ParticipantFatherhoodAssessmentTest.php b/tests/Feature/Intake/ParticipantFatherhoodAssessmentTest.php new file mode 100644 index 00000000..15d8ed81 --- /dev/null +++ b/tests/Feature/Intake/ParticipantFatherhoodAssessmentTest.php @@ -0,0 +1,123 @@ +seed(PermissionsSeeder::class); + } + + + /** + * A basic feature test example. + */ + public function test_participant_fatherhood_assessment_index_renders(): void + { + $fatherhoodAssessment = ParticipantFatherhoodAssessment::factory()->create(); + + $participantUser = $fatherhoodAssessment->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-assessment.index')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->count('fatherhoodAssessments', 1)); + } + + public function test_participant_fatherhood_assessment_create_form_renders(): void + { + $fatherhoodAssessment = ParticipantFatherhoodAssessment::factory()->create(); + + $participantUser = $fatherhoodAssessment->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-assessment.create')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/FatherhoodAssessment/Create')); + } + + public function test_participant_fatherhood_assessment_edit_form_renders(): void + { + $fatherhoodAssessment = ParticipantFatherhoodAssessment::factory()->create(); + + $participantUser = $fatherhoodAssessment->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-assessment.edit', ['fatherhoodAssessment' => $fatherhoodAssessment->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/FatherhoodAssessment/Edit')); + } + + public function test_participant_fatherhood_assessment_show_form_renders(): void + { + $fatherhoodAssessment = ParticipantFatherhoodAssessment::factory()->create(); + + $participantUser = $fatherhoodAssessment->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-assessment.show', ['fatherhoodAssessment' => $fatherhoodAssessment->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/FatherhoodAssessment/Show')); + } + + + public function test_participant_fatherhood_assessment_store_creates_record(): void + { + $participant = Participant::factory()->create(); + + $participantUser = $participant->user; + + $data = ParticipantFatherhoodAssessment::factory()->make()->attributesToArray(); + + $response = $this->actingAs($participantUser)->post(route('intake.fatherhood-assessment.store'), $data); + + $response->assertRedirect(); + $participant->refresh(); + + $this->assertCount(1, $participant->fatherhoodAssessments()->get()); + } + + public function test_participant_fatherhood_assessment_update_record(): void + { + $fatherhoodAssessment = ParticipantFatherhoodAssessment::factory()->create(); + + $participantUser = $fatherhoodAssessment->participant->user; + + $data = $fatherhoodAssessment->attributesToArray(); + $data['participant_name'] = 'new name'; + + $response = $this->actingAs($participantUser)->patch(route('intake.fatherhood-assessment.update', ['fatherhoodAssessment' => $fatherhoodAssessment->id]), $data); + $response->assertRedirect(); + + $fatherhoodAssessment->refresh(); + $this->assertEquals('new name', $fatherhoodAssessment->participant_name); + } + + public function test_participant_fatherhood_assessment_deletes_record(): void + { + $fatherhoodAssessment = ParticipantFatherhoodAssessment::factory()->create(); + $participant = $fatherhoodAssessment->participant; + $participantUser = $participant->user; + + $response = $this->actingAs($participantUser)->delete(route('intake.fatherhood-assessment.destroy', ['fatherhoodAssessment' => $fatherhoodAssessment->id])); + $response->assertRedirect(); + + $participant->refresh(); + $this->assertCount(0, $participant->fatherhoodAssessments()->get()); + + } +} From e79b4542653d01bca59e64cac1ea19e079e221a4 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 14:53:08 -0500 Subject: [PATCH 09/40] implement participant fatherhood survey form --- .../ParticipantFatherhoodSurveyController.php | 98 ++++++++++++++ ...toreParticipantFatherhoodSurveyRequest.php | 65 +++++++++ ...dateParticipantFatherhoodSurveyRequest.php | 65 +++++++++ app/Models/Participant.php | 8 ++ app/Models/ParticipantFatherhoodSurvey.php | 83 ++++++++++++ .../ParticipantFatherhoodSurveyFactory.php | 69 ++++++++++ ...e_participant_fatherhood_surveys_table.php | 67 ++++++++++ .../ParticipantFatherhoodSurveySeeder.php | 17 +++ .../Pages/Intake/FatherhoodSurvey/Create.tsx | 27 ++++ .../js/Pages/Intake/FatherhoodSurvey/Edit.tsx | 24 ++++ .../Pages/Intake/FatherhoodSurvey/Index.tsx | 24 ++++ .../js/Pages/Intake/FatherhoodSurvey/Show.tsx | 24 ++++ routes/web.php | 5 + .../ParticipantFatherhoodSurveyTest.php | 124 ++++++++++++++++++ 14 files changed, 700 insertions(+) create mode 100644 app/Http/Controllers/Intake/ParticipantFatherhoodSurveyController.php create mode 100644 app/Http/Requests/Intake/StoreParticipantFatherhoodSurveyRequest.php create mode 100644 app/Http/Requests/Intake/UpdateParticipantFatherhoodSurveyRequest.php create mode 100644 app/Models/ParticipantFatherhoodSurvey.php create mode 100644 database/factories/ParticipantFatherhoodSurveyFactory.php create mode 100644 database/migrations/2025_03_23_185614_create_participant_fatherhood_surveys_table.php create mode 100644 database/seeders/ParticipantFatherhoodSurveySeeder.php create mode 100644 resources/js/Pages/Intake/FatherhoodSurvey/Create.tsx create mode 100644 resources/js/Pages/Intake/FatherhoodSurvey/Edit.tsx create mode 100644 resources/js/Pages/Intake/FatherhoodSurvey/Index.tsx create mode 100644 resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx create mode 100644 tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php diff --git a/app/Http/Controllers/Intake/ParticipantFatherhoodSurveyController.php b/app/Http/Controllers/Intake/ParticipantFatherhoodSurveyController.php new file mode 100644 index 00000000..abfaefb8 --- /dev/null +++ b/app/Http/Controllers/Intake/ParticipantFatherhoodSurveyController.php @@ -0,0 +1,98 @@ +user()->participant; + + return Inertia::render('Intake/FatherhoodSurvey/Index',[ + 'participant' => ParticipantResource::make($participant), + 'fatherhoodSurveys' => $participant?->fatherhoodSurveys?->toArray(), + ]); + } + + /** + * Show the form for creating a new resource. + */ + public function create(Request $request): Response + { + return Inertia::render('Intake/FatherhoodSurvey/Create',[ + 'participant' => ParticipantResource::make($request->user()->participant), + ]); + } + + /** + * Store a newly created resource in storage. + */ + public function store(StoreParticipantFatherhoodSurveyRequest $request) + { + $validated = $request->validated(); + $participant = $request->user()->participant; + + $participant->FatherhoodSurveys()->create($validated); + return redirect()->back(303)->with('message', 'Created Successfully'); + } + + /** + * Display the specified resource. + */ + public function show(Request $request, ParticipantFatherhoodSurvey $fatherhoodSurvey) + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/FatherhoodSurvey/Show', [ + 'participant' => ParticipantResource::make($participant), + 'fatherhoodSurvey' => $fatherhoodSurvey->toArray(), + ]); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Request $request, ParticipantFatherhoodSurvey $fatherhoodSurvey) + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/FatherhoodSurvey/Edit', [ + 'participant' => ParticipantResource::make($participant), + 'fatherhoodSurvey' => $fatherhoodSurvey->toArray(), + ]); + } + + /** + * Update the specified resource in storage. + */ + public function update(UpdateParticipantFatherhoodSurveyRequest $request, ParticipantFatherhoodSurvey $fatherhoodSurvey) + { + $validated = $request->validated(); + $fatherhoodSurvey->update($validated); + + return redirect()->back(303)->with('message', 'Updated Successfully'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(ParticipantFatherhoodSurvey $fatherhoodSurvey) + { + $fatherhoodSurvey->delete(); + + return redirect()->back(303)->with('message', 'Deleted Successfully'); + } + +} diff --git a/app/Http/Requests/Intake/StoreParticipantFatherhoodSurveyRequest.php b/app/Http/Requests/Intake/StoreParticipantFatherhoodSurveyRequest.php new file mode 100644 index 00000000..e90340af --- /dev/null +++ b/app/Http/Requests/Intake/StoreParticipantFatherhoodSurveyRequest.php @@ -0,0 +1,65 @@ +|string> + */ + public function rules(): array + { + return [ + + 'date_of_birth' => ['nullable', 'date'], + 'fatherhood_program' => ['required', 'string', 'max:191'], + + // Reason fields (boolean) + 'reason_become_responsible_father' => ['required', 'boolean'], + 'reason_referred' => ['required', 'boolean'], + 'reason_court_ordered' => ['required', 'boolean'], + 'reason_address_child_support_concerns' => ['required', 'boolean'], + 'reason_other' => ['required', 'boolean'], + 'reason_other_description' => ['nullable', 'string', 'max:191', 'required_if:reason_other,true'], + + // Referred by fields (boolean) + 'referred_by_word_of_mouth' => ['required', 'boolean'], + 'referred_by_past_participant' => ['required', 'boolean'], + 'referred_by_family_support_division' => ['required', 'boolean'], + 'referred_by_prosecuting_attorney' => ['required', 'boolean'], + 'referred_by_marketing' => ['required', 'boolean'], + 'referred_by_organization_itself' => ['required', 'boolean'], + 'referred_by_other' => ['required', 'boolean'], + 'referred_by_other_source' => ['nullable', 'string', 'max:191', 'required_if:referred_by_other,true'], + + // Expectations fields (boolean) + 'employment_opportunities_expected' => ['required', 'boolean'], + 'assistance_with_alcohol_abuse_expected' => ['required', 'boolean'], + 'increased_emphasis_on_parenting_skills_expected' => ['required', 'boolean'], + 'access_to_mentors_resources_outside_program_expected' => ['required', 'boolean'], + 'resume_building_skills_expected' => ['required', 'boolean'], + 'free_legal_services_expected' => ['required', 'boolean'], + 'assistance_with_criminal_history_expected' => ['required', 'boolean'], + 'assistance_with_credit_repair_expected' => ['required', 'boolean'], + 'assistance_with_overcoming_homelessness_expected' => ['required', 'boolean'], + 'assistance_with_visitation_custody_expected' => ['required', 'boolean'], + 'increased_understanding_of_child_support_issues_expected' => ['required', 'boolean'], + 'maintaining_hope_for_the_future_expected' => ['required', 'boolean'], + 'help_obtaining_information_about_health_wellness_expected' => ['required', 'boolean'], + 'other_expected' => ['required', 'boolean'], + 'other_expectations_description' => ['nullable', 'string', 'max:191', 'required_if:other_expected,true'], + ]; + } +} diff --git a/app/Http/Requests/Intake/UpdateParticipantFatherhoodSurveyRequest.php b/app/Http/Requests/Intake/UpdateParticipantFatherhoodSurveyRequest.php new file mode 100644 index 00000000..2f85d63c --- /dev/null +++ b/app/Http/Requests/Intake/UpdateParticipantFatherhoodSurveyRequest.php @@ -0,0 +1,65 @@ +|string> + */ + public function rules(): array + { + return [ + + 'date_of_birth' => ['nullable', 'date'], + 'fatherhood_program' => ['required', 'string', 'max:191'], + + // Reason fields (boolean) + 'reason_become_responsible_father' => ['required', 'boolean'], + 'reason_referred' => ['required', 'boolean'], + 'reason_court_ordered' => ['required', 'boolean'], + 'reason_address_child_support_concerns' => ['required', 'boolean'], + 'reason_other' => ['required', 'boolean'], + 'reason_other_description' => ['nullable', 'string', 'max:191', 'required_if:reason_other,true'], + + // Referred by fields (boolean) + 'referred_by_word_of_mouth' => ['required', 'boolean'], + 'referred_by_past_participant' => ['required', 'boolean'], + 'referred_by_family_support_division' => ['required', 'boolean'], + 'referred_by_prosecuting_attorney' => ['required', 'boolean'], + 'referred_by_marketing' => ['required', 'boolean'], + 'referred_by_organization_itself' => ['required', 'boolean'], + 'referred_by_other' => ['required', 'boolean'], + 'referred_by_other_source' => ['nullable', 'string', 'max:191', 'required_if:referred_by_other,true'], + + // Expectations fields (boolean) + 'employment_opportunities_expected' => ['required', 'boolean'], + 'assistance_with_alcohol_abuse_expected' => ['required', 'boolean'], + 'increased_emphasis_on_parenting_skills_expected' => ['required', 'boolean'], + 'access_to_mentors_resources_outside_program_expected' => ['required', 'boolean'], + 'resume_building_skills_expected' => ['required', 'boolean'], + 'free_legal_services_expected' => ['required', 'boolean'], + 'assistance_with_criminal_history_expected' => ['required', 'boolean'], + 'assistance_with_credit_repair_expected' => ['required', 'boolean'], + 'assistance_with_overcoming_homelessness_expected' => ['required', 'boolean'], + 'assistance_with_visitation_custody_expected' => ['required', 'boolean'], + 'increased_understanding_of_child_support_issues_expected' => ['required', 'boolean'], + 'maintaining_hope_for_the_future_expected' => ['required', 'boolean'], + 'help_obtaining_information_about_health_wellness_expected' => ['required', 'boolean'], + 'other_expected' => ['required', 'boolean'], + 'other_expectations_description' => ['nullable', 'string', 'max:191', 'required_if:other_expected,true'], + ]; + } +} diff --git a/app/Models/Participant.php b/app/Models/Participant.php index e53ae51c..34b53cf4 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -116,4 +116,12 @@ public function fatherhoodAssessments(): HasMany return $this->hasMany(ParticipantFatherhoodAssessment::class); } + /** + * Get the Fatherhood Surveys for the participant. + */ + public function fatherhoodSurveys(): HasMany + { + return $this->hasMany(ParticipantFatherhoodSurvey::class); + } + } diff --git a/app/Models/ParticipantFatherhoodSurvey.php b/app/Models/ParticipantFatherhoodSurvey.php new file mode 100644 index 00000000..4b2e9b5e --- /dev/null +++ b/app/Models/ParticipantFatherhoodSurvey.php @@ -0,0 +1,83 @@ + 'date', + 'reason_become_responsible_father' => 'boolean', + 'reason_referred' => 'boolean', + 'reason_court_ordered' => 'boolean', + 'reason_address_child_support_concerns' => 'boolean', + 'reason_other' => 'boolean', + 'referred_by_word_of_mouth' => 'boolean', + 'referred_by_past_participant' => 'boolean', + 'referred_by_family_support_division' => 'boolean', + 'referred_by_prosecuting_attorney' => 'boolean', + 'referred_by_marketing' => 'boolean', + 'referred_by_organization_itself' => 'boolean', + 'referred_by_other' => 'boolean', + 'employment_opportunities_expected' => 'boolean', + 'assistance_with_alcohol_abuse_expected' => 'boolean', + 'increased_emphasis_on_parenting_skills_expected' => 'boolean', + 'access_to_mentors_resources_outside_program_expected' => 'boolean', + 'resume_building_skills_expected' => 'boolean', + 'free_legal_services_expected' => 'boolean', + 'assistance_with_criminal_history_expected' => 'boolean', + 'assistance_with_credit_repair_expected' => 'boolean', + 'assistance_with_overcoming_homelessness_expected' => 'boolean', + 'assistance_with_visitation_custody_expected' => 'boolean', + 'increased_understanding_of_child_support_issues_expected' => 'boolean', + 'maintaining_hope_for_the_future_expected' => 'boolean', + 'help_obtaining_information_about_health_wellness_expected' => 'boolean', + 'other_expected' => 'boolean', + ]; + + public function participant(): BelongsTo + { + return $this->belongsTo(Participant::class); + } +} diff --git a/database/factories/ParticipantFatherhoodSurveyFactory.php b/database/factories/ParticipantFatherhoodSurveyFactory.php new file mode 100644 index 00000000..fd699670 --- /dev/null +++ b/database/factories/ParticipantFatherhoodSurveyFactory.php @@ -0,0 +1,69 @@ + + */ +class ParticipantFatherhoodSurveyFactory extends Factory +{ + protected $model = \App\Models\ParticipantFatherhoodSurvey::class; + + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $reasonOther = $this->faker->boolean; + $referredByOther = $this->faker->boolean; + $otherExpected = $this->faker->boolean; + + return [ + 'participant_id' => Participant::factory(), + 'date_of_birth' => $this->faker->dateTimeBetween('-50 years', '-18 years')->format('Y-m-d'), + 'fatherhood_program' => 'Good Dads', + + // Reasons + 'reason_become_responsible_father' => $this->faker->boolean, + 'reason_referred' => $this->faker->boolean, + 'reason_court_ordered' => $this->faker->boolean, + 'reason_address_child_support_concerns' => $this->faker->boolean, + 'reason_other' => $reasonOther, + 'reason_other_description' => $reasonOther ? $this->faker->sentence : null, + + // Referred by + 'referred_by_word_of_mouth' => $this->faker->boolean, + 'referred_by_past_participant' => $this->faker->boolean, + 'referred_by_family_support_division' => $this->faker->boolean, + 'referred_by_prosecuting_attorney' => $this->faker->boolean, + 'referred_by_marketing' => $this->faker->boolean, + 'referred_by_organization_itself' => $this->faker->boolean, + 'referred_by_other' => $referredByOther, + 'referred_by_other_source' => $referredByOther ? $this->faker->company : null, + + // Expectations + 'employment_opportunities_expected' => $this->faker->boolean, + 'assistance_with_alcohol_abuse_expected' => $this->faker->boolean, + 'increased_emphasis_on_parenting_skills_expected' => $this->faker->boolean, + 'access_to_mentors_resources_outside_program_expected' => $this->faker->boolean, + 'resume_building_skills_expected' => $this->faker->boolean, + 'free_legal_services_expected' => $this->faker->boolean, + 'assistance_with_criminal_history_expected' => $this->faker->boolean, + 'assistance_with_credit_repair_expected' => $this->faker->boolean, + 'assistance_with_overcoming_homelessness_expected' => $this->faker->boolean, + 'assistance_with_visitation_custody_expected' => $this->faker->boolean, + 'increased_understanding_of_child_support_issues_expected' => $this->faker->boolean, + 'maintaining_hope_for_the_future_expected' => $this->faker->boolean, + 'help_obtaining_information_about_health_wellness_expected' => $this->faker->boolean, + 'other_expected' => $otherExpected, + 'other_expectations_description' => $otherExpected ? $this->faker->sentence : null, + 'date_completed' => null, + ]; + } +} diff --git a/database/migrations/2025_03_23_185614_create_participant_fatherhood_surveys_table.php b/database/migrations/2025_03_23_185614_create_participant_fatherhood_surveys_table.php new file mode 100644 index 00000000..f378b390 --- /dev/null +++ b/database/migrations/2025_03_23_185614_create_participant_fatherhood_surveys_table.php @@ -0,0 +1,67 @@ +uuid('id')->primary(); + $table->foreignUuid('participant_id')->constrained(); + + $table->date('date_of_birth')->nullable(); + $table->string('fatherhood_program')->default('Good Dads'); + $table->boolean('reason_become_responsible_father'); + $table->boolean('reason_referred'); + $table->boolean('reason_court_ordered'); + $table->boolean('reason_address_child_support_concerns'); + $table->boolean('reason_other'); + $table->string('reason_other_description')->nullable(); + + $table->boolean('referred_by_word_of_mouth'); + $table->boolean('referred_by_past_participant'); + $table->boolean('referred_by_family_support_division'); + $table->boolean('referred_by_prosecuting_attorney'); + $table->boolean('referred_by_marketing'); + // Verify if this organization is good dads + $table->boolean('referred_by_organization_itself'); + $table->boolean('referred_by_other'); + $table->string('referred_by_other_source')->nullable(); + + $table->boolean('employment_opportunities_expected'); + $table->boolean('assistance_with_alcohol_abuse_expected'); + $table->boolean('increased_emphasis_on_parenting_skills_expected'); + $table->boolean('access_to_mentors_resources_outside_program_expected'); + $table->boolean('resume_building_skills_expected'); + $table->boolean('free_legal_services_expected'); + $table->boolean('assistance_with_criminal_history_expected'); + $table->boolean('assistance_with_credit_repair_expected'); + $table->boolean('assistance_with_overcoming_homelessness_expected'); + $table->boolean('assistance_with_visitation_custody_expected'); + $table->boolean('increased_understanding_of_child_support_issues_expected'); + $table->boolean('maintaining_hope_for_the_future_expected'); + $table->boolean('help_obtaining_information_about_health_wellness_expected'); + $table->boolean('other_expected'); + $table->string('other_expectations_description')->nullable(); + + // Marked completed on + $table->date('date_completed')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('participant_fatherhood_surveys'); + } +}; diff --git a/database/seeders/ParticipantFatherhoodSurveySeeder.php b/database/seeders/ParticipantFatherhoodSurveySeeder.php new file mode 100644 index 00000000..7d64728c --- /dev/null +++ b/database/seeders/ParticipantFatherhoodSurveySeeder.php @@ -0,0 +1,17 @@ + = ({ + auth, + participant, +}) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Create diff --git a/resources/js/Pages/Intake/FatherhoodSurvey/Edit.tsx b/resources/js/Pages/Intake/FatherhoodSurvey/Edit.tsx new file mode 100644 index 00000000..7e209d41 --- /dev/null +++ b/resources/js/Pages/Intake/FatherhoodSurvey/Edit.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Edit: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Edit diff --git a/resources/js/Pages/Intake/FatherhoodSurvey/Index.tsx b/resources/js/Pages/Intake/FatherhoodSurvey/Index.tsx new file mode 100644 index 00000000..d957666b --- /dev/null +++ b/resources/js/Pages/Intake/FatherhoodSurvey/Index.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Index: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Index diff --git a/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx b/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx new file mode 100644 index 00000000..f5858cc8 --- /dev/null +++ b/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Show: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Show diff --git a/routes/web.php b/routes/web.php index 10d687a9..b2288a0f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Intake\IntakeController; use App\Http\Controllers\Intake\ParticipantDisclosureController; use App\Http\Controllers\Intake\ParticipantFatherhoodAssessmentController; +use App\Http\Controllers\Intake\ParticipantFatherhoodSurveyController; use App\Http\Controllers\Intake\ParticipantRegistrationController; use App\Http\Controllers\Intake\ParticipantSignupController; use App\Http\Controllers\LegalController; @@ -56,6 +57,10 @@ ->resource('fatherhood-assessment', ParticipantFatherhoodAssessmentController::class) ->parameter('fatherhood-assessment', 'fatherhoodAssessment'); + Route::middleware('role:participant') + ->resource('fatherhood-survey', ParticipantFatherhoodSurveyController::class) + ->parameter('fatherhood-survey', 'fatherhoodSurvey'); + }); diff --git a/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php b/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php new file mode 100644 index 00000000..ca3524dd --- /dev/null +++ b/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php @@ -0,0 +1,124 @@ +seed(PermissionsSeeder::class); + } + + + /** + * A basic feature test example. + */ + public function test_participant_index_renders(): void + { + $fatherhoodSurvey = ParticipantFatherhoodSurvey::factory()->create(); + + $participantUser = $fatherhoodSurvey->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-survey.index')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->dd() && $page->count('fatherhoodSurveys', 1)); + } + + public function test_participant_create_form_renders(): void + { + $fatherhoodSurvey = ParticipantFatherhoodSurvey::factory()->create(); + + $participantUser = $fatherhoodSurvey->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-survey.create')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/FatherhoodSurvey/Create')); + } + + public function test_participant_edit_form_renders(): void + { + $fatherhoodSurvey = ParticipantFatherhoodSurvey::factory()->create(); + + $participantUser = $fatherhoodSurvey->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-survey.edit', ['fatherhoodSurvey' => $fatherhoodSurvey->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/FatherhoodSurvey/Edit')); + } + + public function test_participant_show_form_renders(): void + { + $fatherhoodSurvey = ParticipantFatherhoodSurvey::factory()->create(); + + $participantUser = $fatherhoodSurvey->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.fatherhood-survey.show', ['fatherhoodSurvey' => $fatherhoodSurvey->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/FatherhoodSurvey/Show')); + } + + + public function test_participant_store_creates_record(): void + { + $participant = Participant::factory()->create(); + + $participantUser = $participant->user; + + $data = ParticipantFatherhoodSurvey::factory()->make()->attributesToArray(); + + $response = $this->actingAs($participantUser)->post(route('intake.fatherhood-survey.store'), $data); + + $response->assertRedirect(); + $participant->refresh(); + + $this->assertCount(1, $participant->fatherhoodSurveys()->get()); + } + + public function test_participant_update_record(): void + { + $fatherhoodSurvey = ParticipantFatherhoodSurvey::factory()->create(); + + $participantUser = $fatherhoodSurvey->participant->user; + + $data = $fatherhoodSurvey->attributesToArray(); + $data['date_of_birth'] = null; + + $response = $this->actingAs($participantUser)->patch(route('intake.fatherhood-survey.update', ['fatherhoodSurvey' => $fatherhoodSurvey->id]), $data); + $response->assertRedirect(); + + $fatherhoodSurvey->refresh(); + $this->assertEquals(null, $fatherhoodSurvey->date_of_birth); + } + + public function test_participant_deletes_record(): void + { + $fatherhoodSurvey = ParticipantFatherhoodSurvey::factory()->create(); + $participant = $fatherhoodSurvey->participant; + $participantUser = $participant->user; + + $response = $this->actingAs($participantUser)->delete(route('intake.fatherhood-survey.destroy', ['fatherhoodSurvey' => $fatherhoodSurvey->id])); + $response->assertRedirect(); + + $participant->refresh(); + $this->assertCount(0, $participant->fatherhoodSurveys()->get()); + + } +} From e05dfa48900bf16cf26aaf7f147b2405a2faa9bc Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 14:54:09 -0500 Subject: [PATCH 10/40] remove unused imports on fatherhood survey test --- tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php b/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php index ca3524dd..e1782a5d 100644 --- a/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php +++ b/tests/Feature/Intake/ParticipantFatherhoodSurveyTest.php @@ -3,11 +3,9 @@ namespace Tests\Feature\Intake; use App\Models\Participant; -use App\Models\ParticipantFatherhoodAssessment; use App\Models\ParticipantFatherhoodSurvey; use Database\Seeders\PermissionsSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; use Inertia\Testing\AssertableInertia; use Tests\TestCase; From 1e41d71ee77e093d7d196715ef5fe2f0e3b6fd34 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 15:27:40 -0500 Subject: [PATCH 11/40] implement participant service plan forms --- .../ParticipantServicePlanController.php | 97 ++++++++++++++ .../StoreParticipantServicePlanRequest.php | 57 ++++++++ .../UpdateParticipantServicePlanRequest.php | 57 ++++++++ app/Models/Participant.php | 8 ++ app/Models/ParticipantServicePlan.php | 61 +++++++++ .../ParticipantServicePlanFactory.php | 53 ++++++++ ...create_participant_service_plans_table.php | 62 +++++++++ .../js/Pages/Intake/ServicePlan/Create.tsx | 27 ++++ .../js/Pages/Intake/ServicePlan/Edit.tsx | 24 ++++ .../js/Pages/Intake/ServicePlan/Index.tsx | 24 ++++ .../js/Pages/Intake/ServicePlan/Show.tsx | 24 ++++ routes/web.php | 5 + .../Intake/ParticipantServicePlanTest.php | 122 ++++++++++++++++++ 13 files changed, 621 insertions(+) create mode 100644 app/Http/Controllers/Intake/ParticipantServicePlanController.php create mode 100644 app/Http/Requests/Intake/StoreParticipantServicePlanRequest.php create mode 100644 app/Http/Requests/Intake/UpdateParticipantServicePlanRequest.php create mode 100644 app/Models/ParticipantServicePlan.php create mode 100644 database/factories/ParticipantServicePlanFactory.php create mode 100644 database/migrations/2025_03_23_195537_create_participant_service_plans_table.php create mode 100644 resources/js/Pages/Intake/ServicePlan/Create.tsx create mode 100644 resources/js/Pages/Intake/ServicePlan/Edit.tsx create mode 100644 resources/js/Pages/Intake/ServicePlan/Index.tsx create mode 100644 resources/js/Pages/Intake/ServicePlan/Show.tsx create mode 100644 tests/Feature/Intake/ParticipantServicePlanTest.php diff --git a/app/Http/Controllers/Intake/ParticipantServicePlanController.php b/app/Http/Controllers/Intake/ParticipantServicePlanController.php new file mode 100644 index 00000000..e1d92d35 --- /dev/null +++ b/app/Http/Controllers/Intake/ParticipantServicePlanController.php @@ -0,0 +1,97 @@ +user()->participant; + + return Inertia::render('Intake/ServicePlan/Index',[ + 'participant' => ParticipantResource::make($participant), + 'servicePlans' => $participant?->servicePlans?->toArray(), + ]); + } + + /** + * Show the form for creating a new resource. + */ + public function create(Request $request): Response + { + return Inertia::render('Intake/ServicePlan/Create',[ + 'participant' => ParticipantResource::make($request->user()->participant), + ]); + } + + /** + * Store a newly created resource in storage. + */ + public function store(StoreParticipantservicePlanRequest $request) + { + $validated = $request->validated(); + $participant = $request->user()->participant; + + $participant->servicePlans()->create($validated); + return redirect()->back(303)->with('message', 'Created Successfully'); + } + + /** + * Display the specified resource. + */ + public function show(Request $request, ParticipantservicePlan $servicePlan) + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/ServicePlan/Show', [ + 'participant' => ParticipantResource::make($participant), + 'servicePlan' => $servicePlan->toArray(), + ]); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Request $request, ParticipantServicePlan $servicePlan) + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/ServicePlan/Edit', [ + 'participant' => ParticipantResource::make($participant), + 'servicePlan' => $servicePlan->toArray(), + ]); + } + + /** + * Update the specified resource in storage. + */ + public function update(UpdateParticipantServicePlanRequest $request, ParticipantServicePlan $servicePlan) + { + $validated = $request->validated(); + $servicePlan->update($validated); + + return redirect()->back(303)->with('message', 'Updated Successfully'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(ParticipantServicePlan $servicePlan) + { + $servicePlan->delete(); + + return redirect()->back(303)->with('message', 'Deleted Successfully'); + } +} diff --git a/app/Http/Requests/Intake/StoreParticipantServicePlanRequest.php b/app/Http/Requests/Intake/StoreParticipantServicePlanRequest.php new file mode 100644 index 00000000..d12ff7ee --- /dev/null +++ b/app/Http/Requests/Intake/StoreParticipantServicePlanRequest.php @@ -0,0 +1,57 @@ +|string> + */ + public function rules(): array + { + return [ + 'participant_name' => 'required|string|max:191', + 'client_number' => 'required|string|max:191', + + 'parenting_skill_development_is_service_area' => 'required|boolean', + 'effective_co_parenting_is_service_area' => 'required|boolean', + 'employment_and_education_is_service_area' => 'required|boolean', + 'child_support_is_service_area' => 'required|boolean', + 'domestic_violence_is_service_area' => 'required|boolean', + 'service_identified_by_participant' => 'nullable|string|max:191', + + 'goal' => 'required|string', + 'custody_visitation_strategy' => 'required|nullable|string', + 'custody_visitation_person_responsible' => 'required|nullable|string|max:191', + 'custody_visitation_timeline' => 'required|nullable|string|max:191', + 'custody_visitation_measure_of_success' => 'required|nullable|string', + 'education_employment_strategy' => 'required|nullable|string', + 'education_employment_person_responsible' => 'required|nullable|string|max:191', + 'education_employment_timeline' => 'required|nullable|string|max:191', + 'education_employment_measure_of_success' => 'required|nullable|string', + 'housing_transportation_strategy' => 'required|nullable|string', + 'housing_transportation_person_responsible' => 'required|nullable|string|max:191', + 'housing_transportation_timeline' => 'required|nullable|string|max:191', + 'housing_transportation_measure_of_success' => 'required|nullable|string', + + 'participant_signature' => 'sometimes|string|max:191', + 'participant_signature_date' => 'sometimes|date', + 'case_manager_signature' => 'sometimes|string|max:191', + 'case_manager_signature_date' => 'sometimes|date', + + 'date_completed' => 'nullable|date', + ]; + } +} diff --git a/app/Http/Requests/Intake/UpdateParticipantServicePlanRequest.php b/app/Http/Requests/Intake/UpdateParticipantServicePlanRequest.php new file mode 100644 index 00000000..b299bc6c --- /dev/null +++ b/app/Http/Requests/Intake/UpdateParticipantServicePlanRequest.php @@ -0,0 +1,57 @@ +|string> + */ + public function rules(): array + { + return [ + 'participant_name' => 'required|string|max:191', + 'client_number' => 'required|string|max:191', + + 'parenting_skill_development_is_service_area' => 'required|boolean', + 'effective_co_parenting_is_service_area' => 'required|boolean', + 'employment_and_education_is_service_area' => 'required|boolean', + 'child_support_is_service_area' => 'required|boolean', + 'domestic_violence_is_service_area' => 'required|boolean', + 'service_identified_by_participant' => 'nullable|string|max:191', + + 'goal' => 'required|string', + 'custody_visitation_strategy' => 'required|nullable|string', + 'custody_visitation_person_responsible' => 'required|nullable|string|max:191', + 'custody_visitation_timeline' => 'required|nullable|string|max:191', + 'custody_visitation_measure_of_success' => 'required|nullable|string', + 'education_employment_strategy' => 'required|nullable|string', + 'education_employment_person_responsible' => 'required|nullable|string|max:191', + 'education_employment_timeline' => 'required|nullable|string|max:191', + 'education_employment_measure_of_success' => 'required|nullable|string', + 'housing_transportation_strategy' => 'required|nullable|string', + 'housing_transportation_person_responsible' => 'required|nullable|string|max:191', + 'housing_transportation_timeline' => 'required|nullable|string|max:191', + 'housing_transportation_measure_of_success' => 'required|nullable|string', + + 'participant_signature' => 'sometimes|string|max:191', + 'participant_signature_date' => 'sometimes|date', + 'case_manager_signature' => 'sometimes|string|max:191', + 'case_manager_signature_date' => 'sometimes|date', + + 'date_completed' => 'nullable|date', + ]; + } +} diff --git a/app/Models/Participant.php b/app/Models/Participant.php index 34b53cf4..5c8c6e58 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -124,4 +124,12 @@ public function fatherhoodSurveys(): HasMany return $this->hasMany(ParticipantFatherhoodSurvey::class); } + /** + * Get the Service Plans for the participant. + */ + public function servicePlans(): HasMany + { + return $this->hasMany(ParticipantServicePlan::class); + } + } diff --git a/app/Models/ParticipantServicePlan.php b/app/Models/ParticipantServicePlan.php new file mode 100644 index 00000000..2382e9c8 --- /dev/null +++ b/app/Models/ParticipantServicePlan.php @@ -0,0 +1,61 @@ + 'date', + 'parenting_skill_development_is_service_area' => 'boolean', + 'effective_co_parenting_is_service_area' => 'boolean', + 'employment_and_education_is_service_area' => 'boolean', + 'child_support_is_service_area' => 'boolean', + 'domestic_violence_is_service_area' => 'boolean', + 'participant_signature_date' => 'date', + 'case_manager_signature_date' => 'date', + 'date_completed' => 'date', + ]; + + public function participant(): BelongsTo + { + return $this->belongsTo(Participant::class, 'participant_id'); + } + +} diff --git a/database/factories/ParticipantServicePlanFactory.php b/database/factories/ParticipantServicePlanFactory.php new file mode 100644 index 00000000..c8917f48 --- /dev/null +++ b/database/factories/ParticipantServicePlanFactory.php @@ -0,0 +1,53 @@ + + */ +class ParticipantServicePlanFactory extends Factory +{ + protected $model = \App\Models\ParticipantServicePlan::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'participant_id' => Participant::factory(), // Assumes a Participant model/factory exists + 'participant_name' => $this->faker->name(), + 'client_number' => $this->faker->unique()->numerify('CL-#####'), + 'review_date' => $this->faker->dateTimeBetween('-1 month', '+1 month'), + 'parenting_skill_development_is_service_area' => $this->faker->boolean(), + 'effective_co_parenting_is_service_area' => $this->faker->boolean(), + 'employment_and_education_is_service_area' => $this->faker->boolean(), + 'child_support_is_service_area' => $this->faker->boolean(), + 'domestic_violence_is_service_area' => $this->faker->boolean(), + 'service_identified_by_participant' => $this->faker->optional()->sentence(), + 'goal' => $this->faker->paragraph(), + 'custody_visitation_strategy' => $this->faker->paragraph(), + 'custody_visitation_person_responsible' => $this->faker->name(), + 'custody_visitation_timeline' => $this->faker->randomElement(['1 month', '3 months', '6 months']), + 'custody_visitation_measure_of_success' => $this->faker->sentence(), + 'education_employment_strategy' => $this->faker->paragraph(), + 'education_employment_person_responsible' => $this->faker->name(), + 'education_employment_timeline' => $this->faker->randomElement(['1 month', '3 months', '6 months']), + 'education_employment_measure_of_success' => $this->faker->sentence(), + 'housing_transportation_strategy' => $this->faker->paragraph(), + 'housing_transportation_person_responsible' => $this->faker->name(), + 'housing_transportation_timeline' => $this->faker->randomElement(['1 month', '3 months', '6 months']), + 'housing_transportation_measure_of_success' => $this->faker->sentence(), + 'participant_signature' => $this->faker->name(), + 'participant_signature_date' => $this->faker->dateTimeBetween('-1 month', 'now'), + 'case_manager_signature' => $this->faker->name(), + 'case_manager_signature_date' => $this->faker->dateTimeBetween('-1 month', 'now'), + 'date_completed' => $this->faker->optional(0.3)->dateTimeBetween('-1 month', 'now'), // 30% chance of being completed + ]; + } +} diff --git a/database/migrations/2025_03_23_195537_create_participant_service_plans_table.php b/database/migrations/2025_03_23_195537_create_participant_service_plans_table.php new file mode 100644 index 00000000..6cc32d1a --- /dev/null +++ b/database/migrations/2025_03_23_195537_create_participant_service_plans_table.php @@ -0,0 +1,62 @@ +uuid('id')->primary(); + $table->foreignUuid('participant_id')->constrained(); + + $table->string('participant_name'); + $table->string('client_number'); + $table->date('review_date')->nullable(); + $table->boolean('parenting_skill_development_is_service_area'); + $table->boolean('effective_co_parenting_is_service_area'); + $table->boolean('employment_and_education_is_service_area'); + $table->boolean('child_support_is_service_area'); + $table->boolean('domestic_violence_is_service_area'); + $table->string('service_identified_by_participant')->nullable(); + + $table->text('goal'); + $table->text('custody_visitation_strategy'); + $table->string('custody_visitation_person_responsible'); + $table->string('custody_visitation_timeline'); + $table->text('custody_visitation_measure_of_success'); + $table->text('education_employment_strategy'); + $table->string('education_employment_person_responsible'); + $table->string('education_employment_timeline'); + $table->text('education_employment_measure_of_success'); + $table->text('housing_transportation_strategy'); + $table->string('housing_transportation_person_responsible'); + $table->string('housing_transportation_timeline'); + $table->text('housing_transportation_measure_of_success'); + + // Potentially need "Child Support Action Goal" fields, confirming with client + $table->string('participant_signature'); + $table->date('participant_signature_date')->nullable(); + $table->string('case_manager_signature'); + $table->date('case_manager_signature_date')->nullable(); + + // Marked completed on + $table->date('date_completed')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('participant_service_plans'); + } +}; diff --git a/resources/js/Pages/Intake/ServicePlan/Create.tsx b/resources/js/Pages/Intake/ServicePlan/Create.tsx new file mode 100644 index 00000000..1d9bc10a --- /dev/null +++ b/resources/js/Pages/Intake/ServicePlan/Create.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Create: React.FC = ({ + auth, + participant, +}) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Create diff --git a/resources/js/Pages/Intake/ServicePlan/Edit.tsx b/resources/js/Pages/Intake/ServicePlan/Edit.tsx new file mode 100644 index 00000000..7e209d41 --- /dev/null +++ b/resources/js/Pages/Intake/ServicePlan/Edit.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Edit: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Edit diff --git a/resources/js/Pages/Intake/ServicePlan/Index.tsx b/resources/js/Pages/Intake/ServicePlan/Index.tsx new file mode 100644 index 00000000..d957666b --- /dev/null +++ b/resources/js/Pages/Intake/ServicePlan/Index.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Index: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Index diff --git a/resources/js/Pages/Intake/ServicePlan/Show.tsx b/resources/js/Pages/Intake/ServicePlan/Show.tsx new file mode 100644 index 00000000..f5858cc8 --- /dev/null +++ b/resources/js/Pages/Intake/ServicePlan/Show.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Show: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Show diff --git a/routes/web.php b/routes/web.php index b2288a0f..b126d122 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,6 +5,7 @@ use App\Http\Controllers\Intake\ParticipantFatherhoodAssessmentController; use App\Http\Controllers\Intake\ParticipantFatherhoodSurveyController; use App\Http\Controllers\Intake\ParticipantRegistrationController; +use App\Http\Controllers\Intake\ParticipantServicePlanController; use App\Http\Controllers\Intake\ParticipantSignupController; use App\Http\Controllers\LegalController; use App\Http\Controllers\ProfileController; @@ -61,6 +62,10 @@ ->resource('fatherhood-survey', ParticipantFatherhoodSurveyController::class) ->parameter('fatherhood-survey', 'fatherhoodSurvey'); + Route::middleware('role:participant') + ->resource('service-plan', ParticipantServicePlanController::class) + ->parameter('service-plan', 'servicePlan'); + }); diff --git a/tests/Feature/Intake/ParticipantServicePlanTest.php b/tests/Feature/Intake/ParticipantServicePlanTest.php new file mode 100644 index 00000000..036e2ced --- /dev/null +++ b/tests/Feature/Intake/ParticipantServicePlanTest.php @@ -0,0 +1,122 @@ +seed(PermissionsSeeder::class); + } + + + /** + * A basic feature test example. + */ + public function test_participant_index_renders(): void + { + $servicePlan = ParticipantServicePlan::factory()->create(); + + $participantUser = $servicePlan->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.service-plan.index')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->dd() && $page->count('servicePlans', 1)); + } + + public function test_participant_create_form_renders(): void + { + $servicePlan = ParticipantServicePlan::factory()->create(); + + $participantUser = $servicePlan->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.service-plan.create')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/ServicePlan/Create')); + } + + public function test_participant_edit_form_renders(): void + { + $servicePlan = ParticipantServicePlan::factory()->create(); + + $participantUser = $servicePlan->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.service-plan.edit', ['servicePlan' => $servicePlan->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/ServicePlan/Edit')); + } + + public function test_participant_show_form_renders(): void + { + $servicePlan = ParticipantServicePlan::factory()->create(); + + $participantUser = $servicePlan->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.service-plan.show', ['servicePlan' => $servicePlan->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/ServicePlan/Show')); + } + + + public function test_participant_store_creates_record(): void + { + $participant = Participant::factory()->create(); + + $participantUser = $participant->user; + + $data = ParticipantServicePlan::factory()->make()->attributesToArray(); + + $response = $this->actingAs($participantUser)->post(route('intake.service-plan.store'), $data); + + $response->assertRedirect(); + $participant->refresh(); + + $this->assertCount(1, $participant->servicePlans()->get()); + } + + public function test_participant_update_record(): void + { + $servicePlan = ParticipantServicePlan::factory()->create(); + + $participantUser = $servicePlan->participant->user; + + $data = $servicePlan->attributesToArray(); + $data['goal'] = 'This is a new goal to test'; + + $response = $this->actingAs($participantUser)->patch(route('intake.service-plan.update', ['servicePlan' => $servicePlan->id]), $data); + $response->assertRedirect(); + + $servicePlan->refresh(); + $this->assertEquals('This is a new goal to test', $servicePlan->goal); + } + + public function test_participant_deletes_record(): void + { + $servicePlan = ParticipantServicePlan::factory()->create(); + $participant = $servicePlan->participant; + $participantUser = $participant->user; + + $response = $this->actingAs($participantUser)->delete(route('intake.service-plan.destroy', ['servicePlan' => $servicePlan->id])); + $response->assertRedirect(); + + $participant->refresh(); + $this->assertCount(0, $participant->servicePlans()->get()); + + } +} From a7fc4634e23556914fd708e9ac0b42bec9898fe8 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 15:54:44 -0500 Subject: [PATCH 12/40] implement participant media release forms --- .../ParticipantMediaReleaseController.php | 97 ++++++++++++++ .../StoreParticipantMediaReleaseRequest.php | 32 +++++ .../UpdateParticipantMediaReleaseRequest.php | 32 +++++ app/Models/Participant.php | 7 + app/Models/ParticipantMediaRelease.php | 33 +++++ .../ParticipantMediaReleaseFactory.php | 32 +++++ ...reate_participant_media_releases_table.php | 38 ++++++ .../js/Pages/Intake/MediaRelease/Create.tsx | 27 ++++ .../js/Pages/Intake/MediaRelease/Edit.tsx | 24 ++++ .../js/Pages/Intake/MediaRelease/Index.tsx | 24 ++++ .../js/Pages/Intake/MediaRelease/Show.tsx | 24 ++++ routes/web.php | 6 + .../Intake/ParticipantMediaReleaseTest.php | 122 ++++++++++++++++++ 13 files changed, 498 insertions(+) create mode 100644 app/Http/Controllers/Intake/ParticipantMediaReleaseController.php create mode 100644 app/Http/Requests/Intake/StoreParticipantMediaReleaseRequest.php create mode 100644 app/Http/Requests/Intake/UpdateParticipantMediaReleaseRequest.php create mode 100644 app/Models/ParticipantMediaRelease.php create mode 100644 database/factories/ParticipantMediaReleaseFactory.php create mode 100644 database/migrations/2025_03_23_202925_create_participant_media_releases_table.php create mode 100644 resources/js/Pages/Intake/MediaRelease/Create.tsx create mode 100644 resources/js/Pages/Intake/MediaRelease/Edit.tsx create mode 100644 resources/js/Pages/Intake/MediaRelease/Index.tsx create mode 100644 resources/js/Pages/Intake/MediaRelease/Show.tsx create mode 100644 tests/Feature/Intake/ParticipantMediaReleaseTest.php diff --git a/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php b/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php new file mode 100644 index 00000000..982968f0 --- /dev/null +++ b/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php @@ -0,0 +1,97 @@ +user()->participant; + + return Inertia::render('Intake/MediaRelease/Index',[ + 'participant' => ParticipantResource::make($participant), + 'mediaReleases' => $participant?->mediaReleases?->toArray(), + ]); + } + + /** + * Show the form for creating a new resource. + */ + public function create(Request $request): Response + { + return Inertia::render('Intake/MediaRelease/Create',[ + 'participant' => ParticipantResource::make($request->user()->participant), + ]); + } + + /** + * Store a newly created resource in storage. + */ + public function store(StoreParticipantmediaReleaseRequest $request) + { + $validated = $request->validated(); + $participant = $request->user()->participant; + + $participant->mediaReleases()->create($validated); + return redirect()->back(303)->with('message', 'Created Successfully'); + } + + /** + * Display the specified resource. + */ + public function show(Request $request, ParticipantmediaRelease $mediaRelease) + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/MediaRelease/Show', [ + 'participant' => ParticipantResource::make($participant), + 'mediaRelease' => $mediaRelease->toArray(), + ]); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Request $request, ParticipantMediaRelease $mediaRelease) + { + $participant = $request->user()->participant; + + return Inertia::render('Intake/MediaRelease/Edit', [ + 'participant' => ParticipantResource::make($participant), + 'mediaRelease' => $mediaRelease->toArray(), + ]); + } + + /** + * Update the specified resource in storage. + */ + public function update(UpdateParticipantMediaReleaseRequest $request, ParticipantMediaRelease $mediaRelease) + { + $validated = $request->validated(); + $mediaRelease->update($validated); + + return redirect()->back(303)->with('message', 'Updated Successfully'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(ParticipantMediaRelease $mediaRelease) + { + $mediaRelease->delete(); + + return redirect()->back(303)->with('message', 'Deleted Successfully'); + } +} diff --git a/app/Http/Requests/Intake/StoreParticipantMediaReleaseRequest.php b/app/Http/Requests/Intake/StoreParticipantMediaReleaseRequest.php new file mode 100644 index 00000000..b43f3f36 --- /dev/null +++ b/app/Http/Requests/Intake/StoreParticipantMediaReleaseRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'printed_name' => 'required|string|max:191', + 'signature' => 'required|string|max:191', + 'signature_date' => 'nullable|date', + 'phone_number' => 'required|string|max:191', + 'email' => 'required|string|email|max:191', + ]; + } +} diff --git a/app/Http/Requests/Intake/UpdateParticipantMediaReleaseRequest.php b/app/Http/Requests/Intake/UpdateParticipantMediaReleaseRequest.php new file mode 100644 index 00000000..e941b0e9 --- /dev/null +++ b/app/Http/Requests/Intake/UpdateParticipantMediaReleaseRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'printed_name' => 'required|string|max:191', + 'signature' => 'required|string|max:191', + 'signature_date' => 'nullable|date', + 'phone_number' => 'required|string|max:191', + 'email' => 'required|string|email|max:191', + ]; + } +} diff --git a/app/Models/Participant.php b/app/Models/Participant.php index 5c8c6e58..82edac30 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -132,4 +132,11 @@ public function servicePlans(): HasMany return $this->hasMany(ParticipantServicePlan::class); } + /** + * Get the Media Releases for the participant. + */ + public function mediaReleases(): HasMany + { + return $this->hasMany(ParticipantMediaRelease::class); + } } diff --git a/app/Models/ParticipantMediaRelease.php b/app/Models/ParticipantMediaRelease.php new file mode 100644 index 00000000..d1dbdedd --- /dev/null +++ b/app/Models/ParticipantMediaRelease.php @@ -0,0 +1,33 @@ + 'date', + 'date_completed' => 'date', + ]; + + public function participant(): BelongsTo + { + return $this->belongsTo(Participant::class); + } +} diff --git a/database/factories/ParticipantMediaReleaseFactory.php b/database/factories/ParticipantMediaReleaseFactory.php new file mode 100644 index 00000000..9e22e494 --- /dev/null +++ b/database/factories/ParticipantMediaReleaseFactory.php @@ -0,0 +1,32 @@ + + */ +class ParticipantMediaReleaseFactory extends Factory +{ + protected $model = \App\Models\ParticipantMediaRelease::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'participant_id' => Participant::factory(), + 'printed_name' => $this->faker->name(), + 'signature' => $this->faker->name(), + 'signature_date' => $this->faker->optional()->date(), + 'phone_number' => $this->faker->phoneNumber(), + 'email' => $this->faker->unique()->safeEmail(), + 'date_completed' => null, + ]; + } +} diff --git a/database/migrations/2025_03_23_202925_create_participant_media_releases_table.php b/database/migrations/2025_03_23_202925_create_participant_media_releases_table.php new file mode 100644 index 00000000..558918ed --- /dev/null +++ b/database/migrations/2025_03_23_202925_create_participant_media_releases_table.php @@ -0,0 +1,38 @@ +uuid('id')->primary(); + $table->foreignUuid('participant_id')->constrained(); + + $table->string('printed_name'); + $table->string('signature'); + $table->date('signature_date')->nullable(); + $table->string('phone_number'); + $table->string('email'); + + // Marked completed on + $table->date('date_completed')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('participant_media_releases'); + } +}; diff --git a/resources/js/Pages/Intake/MediaRelease/Create.tsx b/resources/js/Pages/Intake/MediaRelease/Create.tsx new file mode 100644 index 00000000..1d9bc10a --- /dev/null +++ b/resources/js/Pages/Intake/MediaRelease/Create.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Create: React.FC = ({ + auth, + participant, +}) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Create diff --git a/resources/js/Pages/Intake/MediaRelease/Edit.tsx b/resources/js/Pages/Intake/MediaRelease/Edit.tsx new file mode 100644 index 00000000..7e209d41 --- /dev/null +++ b/resources/js/Pages/Intake/MediaRelease/Edit.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Edit: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Edit diff --git a/resources/js/Pages/Intake/MediaRelease/Index.tsx b/resources/js/Pages/Intake/MediaRelease/Index.tsx new file mode 100644 index 00000000..d957666b --- /dev/null +++ b/resources/js/Pages/Intake/MediaRelease/Index.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Index: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Index diff --git a/resources/js/Pages/Intake/MediaRelease/Show.tsx b/resources/js/Pages/Intake/MediaRelease/Show.tsx new file mode 100644 index 00000000..f5858cc8 --- /dev/null +++ b/resources/js/Pages/Intake/MediaRelease/Show.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import { type PageProps } from '@/types' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' + +interface AssessmentPageProps extends PageProps { + participant: Participant +} + +export const Show: React.FC = ({ auth, participant }) => { + return ( + + +
+
+ Fatherhood Assessment for {participant.user.first_name} +
+
+
+ ) +} + +export default Show diff --git a/routes/web.php b/routes/web.php index b126d122..b7c676bd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -4,6 +4,7 @@ use App\Http\Controllers\Intake\ParticipantDisclosureController; use App\Http\Controllers\Intake\ParticipantFatherhoodAssessmentController; use App\Http\Controllers\Intake\ParticipantFatherhoodSurveyController; +use App\Http\Controllers\Intake\ParticipantMediaReleaseController; use App\Http\Controllers\Intake\ParticipantRegistrationController; use App\Http\Controllers\Intake\ParticipantServicePlanController; use App\Http\Controllers\Intake\ParticipantSignupController; @@ -66,6 +67,11 @@ ->resource('service-plan', ParticipantServicePlanController::class) ->parameter('service-plan', 'servicePlan'); + Route::middleware('role:participant') + ->resource('media-release', ParticipantMediaReleaseController::class) + ->parameter('media-release', 'mediaRelease'); + + }); diff --git a/tests/Feature/Intake/ParticipantMediaReleaseTest.php b/tests/Feature/Intake/ParticipantMediaReleaseTest.php new file mode 100644 index 00000000..5d4ece55 --- /dev/null +++ b/tests/Feature/Intake/ParticipantMediaReleaseTest.php @@ -0,0 +1,122 @@ +seed(PermissionsSeeder::class); + } + + + /** + * A basic feature test example. + */ + public function test_participant_index_renders(): void + { + $mediaRelease = ParticipantMediaRelease::factory()->create(); + + $participantUser = $mediaRelease->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.media-release.index')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->dd() && $page->count('mediaReleases', 1)); + } + + public function test_participant_create_form_renders(): void + { + $mediaRelease = ParticipantMediaRelease::factory()->create(); + + $participantUser = $mediaRelease->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.media-release.create')); + $response->assertStatus(200); + + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/MediaRelease/Create')); + } + + public function test_participant_edit_form_renders(): void + { + $mediaRelease = ParticipantMediaRelease::factory()->create(); + + $participantUser = $mediaRelease->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.media-release.edit', ['mediaRelease' => $mediaRelease->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/MediaRelease/Edit')); + } + + public function test_participant_show_form_renders(): void + { + $mediaRelease = ParticipantMediaRelease::factory()->create(); + + $participantUser = $mediaRelease->participant->user; + + $response = $this->actingAs($participantUser)->get(route('intake.media-release.show', ['mediaRelease' => $mediaRelease->id])); + $response->assertStatus(200); + + $response->assertInertia(fn(AssertableInertia $page) => $page->component('Intake/MediaRelease/Show')); + } + + + public function test_participant_store_creates_record(): void + { + $participant = Participant::factory()->create(); + + $participantUser = $participant->user; + + $data = ParticipantMediaRelease::factory()->make()->attributesToArray(); + + $response = $this->actingAs($participantUser)->post(route('intake.media-release.store'), $data); + + $response->assertRedirect(); + $participant->refresh(); + + $this->assertCount(1, $participant->mediaReleases()->get()); + } + + public function test_participant_update_record(): void + { + $mediaRelease = ParticipantMediaRelease::factory()->create(); + + $participantUser = $mediaRelease->participant->user; + + $data = $mediaRelease->attributesToArray(); + $data['email'] = 'this-is-a-new-email-to-test@some-random-domain.gov'; + + $response = $this->actingAs($participantUser)->patch(route('intake.media-release.update', ['mediaRelease' => $mediaRelease->id]), $data); + $response->assertRedirect(); + + $mediaRelease->refresh(); + $this->assertEquals('this-is-a-new-email-to-test@some-random-domain.gov', $mediaRelease->email); + } + + public function test_participant_deletes_record(): void + { + $mediaRelease = ParticipantMediaRelease::factory()->create(); + $participant = $mediaRelease->participant; + $participantUser = $participant->user; + + $response = $this->actingAs($participantUser)->delete(route('intake.media-release.destroy', ['mediaRelease' => $mediaRelease->id])); + $response->assertRedirect(); + + $participant->refresh(); + $this->assertCount(0, $participant->mediaReleases()->get()); + } + +} From 774f4ee5da78d6f220470434fb543f2a95121b7a Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 16:22:40 -0500 Subject: [PATCH 13/40] Update Participant Types and create MediaRelease Resource --- .../ParticipantMediaReleaseController.php | 7 ++-- .../ParticipantMediaReleaseResource.php | 33 +++++++++++++++++ app/Http/Resources/ParticipantResource.php | 3 +- .../js/Components/Intake/MediaReleaseForm.tsx | 35 +++++++++++++++++++ resources/js/types/app.ts | 6 ++++ .../js/types/intake-media-release-form.ts | 11 ++++++ resources/js/types/participant.ts | 34 +++++++++++++++++- 7 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 app/Http/Resources/ParticipantMediaReleaseResource.php create mode 100644 resources/js/Components/Intake/MediaReleaseForm.tsx create mode 100644 resources/js/types/app.ts create mode 100644 resources/js/types/intake-media-release-form.ts diff --git a/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php b/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php index 982968f0..08697e53 100644 --- a/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php +++ b/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php @@ -5,6 +5,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Intake\StoreParticipantMediaReleaseRequest; use App\Http\Requests\Intake\UpdateParticipantMediaReleaseRequest; +use App\Http\Resources\ParticipantMediaReleaseResource; use App\Http\Resources\ParticipantResource; use App\Models\ParticipantMediaRelease; use Illuminate\Http\Request; @@ -22,7 +23,7 @@ public function index(Request $request) return Inertia::render('Intake/MediaRelease/Index',[ 'participant' => ParticipantResource::make($participant), - 'mediaReleases' => $participant?->mediaReleases?->toArray(), + 'mediaReleases' => ParticipantMediaReleaseResource::collection($participant?->mediaReleases?->toArray()), ]); } @@ -57,7 +58,7 @@ public function show(Request $request, ParticipantmediaRelease $mediaRelease) return Inertia::render('Intake/MediaRelease/Show', [ 'participant' => ParticipantResource::make($participant), - 'mediaRelease' => $mediaRelease->toArray(), + 'mediaRelease' => ParticipantMediaReleaseResource::make($mediaRelease), ]); } @@ -70,7 +71,7 @@ public function edit(Request $request, ParticipantMediaRelease $mediaRelease) return Inertia::render('Intake/MediaRelease/Edit', [ 'participant' => ParticipantResource::make($participant), - 'mediaRelease' => $mediaRelease->toArray(), + 'mediaRelease' => ParticipantMediaReleaseResource::make($mediaRelease), ]); } diff --git a/app/Http/Resources/ParticipantMediaReleaseResource.php b/app/Http/Resources/ParticipantMediaReleaseResource.php new file mode 100644 index 00000000..ffa91201 --- /dev/null +++ b/app/Http/Resources/ParticipantMediaReleaseResource.php @@ -0,0 +1,33 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'participant_id' => $this->participant_id, + 'printed_name' => $this->printed_name, + 'signature' => $this->signature, + 'signature_date' => $this->signature_date?->toDateString(), + 'phone_number' => $this->phone_number, + 'email' => $this->email, + 'created_at' => $this->created_at->toDateTimeString(), + 'updated_at' => $this->updated_at->toDateTimeString(), + ]; + } +} diff --git a/app/Http/Resources/ParticipantResource.php b/app/Http/Resources/ParticipantResource.php index 3e12309d..48dc37a6 100644 --- a/app/Http/Resources/ParticipantResource.php +++ b/app/Http/Resources/ParticipantResource.php @@ -21,7 +21,8 @@ public function toArray(Request $request): array return [ 'id' => $this->id, 'user_id' => $this->user_id, - 'user' => $this->whenLoaded('user', fn() => UserResource::make($this->user)), + 'user' => UserResource::make($this->user), + 'name' => $this->user->first_name . ' ' . $this->user->last_name, 'region_id' => $this->region_id, 'region' => $this->whenLoaded('region', RegionResource::make($this->region)), 'children' => $this->whenLoaded('children', ChildResource::collection($this->children)), diff --git a/resources/js/Components/Intake/MediaReleaseForm.tsx b/resources/js/Components/Intake/MediaReleaseForm.tsx new file mode 100644 index 00000000..f79a5ec4 --- /dev/null +++ b/resources/js/Components/Intake/MediaReleaseForm.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import type { Participant } from '@/types/participant' +import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' +import { useForm } from '@inertiajs/react' + +interface MediaReleaseFormProps { + participant: Participant + mediaReleaseForm: IntakeMediaReleaseForm +} + +interface MediaReleaseFormDefinition extends Record { + printed_name: string + signature: string + signature_date: string | null + phone_number: string + email: string +} + +export const MediaReleaseForm: React.FC = ({ + participant, + mediaReleaseForm, +}) => { + const form = useForm({ + printed_name: mediaReleaseForm?.printed_name ?? participant?.name ?? '', + signature: mediaReleaseForm?.signature ?? '', + signature_date: mediaReleaseForm?.signature_date ?? null, + phone_number: + mediaReleaseForm?.phone_number ?? participant?.cell_phone_number ?? '', + email: mediaReleaseForm?.email ?? participant?.user.email ?? '', + }) + + return
+} + +export default MediaReleaseForm diff --git a/resources/js/types/app.ts b/resources/js/types/app.ts new file mode 100644 index 00000000..3ce8b25e --- /dev/null +++ b/resources/js/types/app.ts @@ -0,0 +1,6 @@ +export interface Region { + id: string + description: string + created_at: string + updated_at: string +} diff --git a/resources/js/types/intake-media-release-form.ts b/resources/js/types/intake-media-release-form.ts new file mode 100644 index 00000000..0a067c7e --- /dev/null +++ b/resources/js/types/intake-media-release-form.ts @@ -0,0 +1,11 @@ +export interface IntakeMediaReleaseForm { + id: string + participant_id: string + printed_name: string + signature: string + signature_date: string | null + phone_number: string + email: string + created_at: string + updated_at: string +} diff --git a/resources/js/types/participant.ts b/resources/js/types/participant.ts index faf2907a..3a0872fa 100644 --- a/resources/js/types/participant.ts +++ b/resources/js/types/participant.ts @@ -1,8 +1,40 @@ -import type { User } from '@/types/index' +import type { Child, User } from '@/types/index' +import type { Region } from '@/types/app' + +export type MaritalStatus = 'single' | 'married' | 'divorced' | 'widowed' +export type Ethnicity = + | 'white' + | 'african_american' + | 'native_american' + | 'asian' + | 'pacific_islander' + | 'hispanic' + | 'no_answer' export type Participant = { id: string user: User + name: string + user_id: string + + region_id: string + region?: Region + children?: Child[] + + address_line_1: string | null + address_line_2: string | null + city: string | null + state: string | null + zipcode: string | null + employer: string | null + cell_phone_number: string | null + home_phone_number: string | null + work_phone_number: string | null + at_contact_number: string | null + marital_status?: MaritalStatus + ethnicity?: Ethnicity + monthly_child_support: number | null + intake_date: string | null created_at: string updated_at: string From 905de6ca37fa6a08e7ba8934acadec748846c62c Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 17:58:30 -0500 Subject: [PATCH 14/40] Add Media Release Forms & Intake Complete --- .../Controllers/Intake/IntakeController.php | 24 +++ .../ParticipantMediaReleaseController.php | 4 +- package-lock.json | 9 +- package.json | 1 + .../js/Components/Intake/MediaReleaseForm.tsx | 152 ++++++++++++++++-- resources/js/Pages/Intake/IntakeComplete.tsx | 106 ++++++++++++ .../js/Pages/Intake/MediaRelease/Create.tsx | 24 ++- .../js/Pages/Intake/MediaRelease/Edit.tsx | 30 ++-- .../js/Pages/Intake/MediaRelease/Index.tsx | 109 +++++++++++-- .../js/Pages/Intake/MediaRelease/Show.tsx | 29 ++-- routes/web.php | 3 + 11 files changed, 427 insertions(+), 64 deletions(-) create mode 100644 resources/js/Pages/Intake/IntakeComplete.tsx diff --git a/app/Http/Controllers/Intake/IntakeController.php b/app/Http/Controllers/Intake/IntakeController.php index ecef70dd..0b50a9b5 100644 --- a/app/Http/Controllers/Intake/IntakeController.php +++ b/app/Http/Controllers/Intake/IntakeController.php @@ -3,6 +3,9 @@ namespace App\Http\Controllers\Intake; use App\Http\Controllers\Controller; +use App\Http\Resources\ParticipantResource; +use App\Models\Participant; +use Illuminate\Http\Request; use Inertia\Inertia; class IntakeController extends Controller @@ -11,4 +14,25 @@ public function index() { return Inertia::render('Intake/Index'); } + + + public function intakeComplete(Request $request) + { + return Inertia::render('Intake/IntakeComplete',[ + 'participant' => ParticipantResource::make($request->user()->participant), + ]); + } + + public function devAuth() + { + $participant = Participant::first(); + $user = $participant->user; + \Auth::login($user); + + $route = request()->query('route') ?? 'intake.media-release.index'; + + return redirect()->route($route); + } + + } diff --git a/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php b/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php index 08697e53..030d786a 100644 --- a/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php +++ b/app/Http/Controllers/Intake/ParticipantMediaReleaseController.php @@ -23,7 +23,7 @@ public function index(Request $request) return Inertia::render('Intake/MediaRelease/Index',[ 'participant' => ParticipantResource::make($participant), - 'mediaReleases' => ParticipantMediaReleaseResource::collection($participant?->mediaReleases?->toArray()), + 'mediaReleases' => ParticipantMediaReleaseResource::collection($participant?->mediaReleases), ]); } @@ -69,6 +69,8 @@ public function edit(Request $request, ParticipantMediaRelease $mediaRelease) { $participant = $request->user()->participant; + \Log::debug('media release', ['form' => ParticipantMediaReleaseResource::make($mediaRelease)->resolve()]); + return Inertia::render('Intake/MediaRelease/Edit', [ 'participant' => ParticipantResource::make($participant), 'mediaRelease' => ParticipantMediaReleaseResource::make($mediaRelease), diff --git a/package-lock.json b/package-lock.json index 30973e6d..134888cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "gooddads", + "name": "html", "lockfileVersion": 3, "requires": true, "packages": { @@ -38,6 +38,7 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "dayjs": "^1.11.13", "embla-carousel-react": "^8.3.0", "input-otp": "^1.2.4", "json-2-csv": "^5.5.7", @@ -8847,6 +8848,12 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", diff --git a/package.json b/package.json index ccb6f4cc..9431b01f 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "dayjs": "^1.11.13", "embla-carousel-react": "^8.3.0", "input-otp": "^1.2.4", "json-2-csv": "^5.5.7", diff --git a/resources/js/Components/Intake/MediaReleaseForm.tsx b/resources/js/Components/Intake/MediaReleaseForm.tsx index f79a5ec4..85770c23 100644 --- a/resources/js/Components/Intake/MediaReleaseForm.tsx +++ b/resources/js/Components/Intake/MediaReleaseForm.tsx @@ -1,17 +1,21 @@ import React from 'react' import type { Participant } from '@/types/participant' import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' -import { useForm } from '@inertiajs/react' +import { router, useForm } from '@inertiajs/react' +import { Button, Input, InputError, Label } from '@/Components/ui' +import dayjs from 'dayjs' interface MediaReleaseFormProps { participant: Participant - mediaReleaseForm: IntakeMediaReleaseForm + mediaReleaseForm?: IntakeMediaReleaseForm + viewOnly?: boolean + nextRoute?: string } interface MediaReleaseFormDefinition extends Record { printed_name: string signature: string - signature_date: string | null + signature_date: string phone_number: string email: string } @@ -19,17 +23,139 @@ interface MediaReleaseFormDefinition extends Record { export const MediaReleaseForm: React.FC = ({ participant, mediaReleaseForm, + viewOnly = false, + nextRoute = 'intake.complete', }) => { - const form = useForm({ - printed_name: mediaReleaseForm?.printed_name ?? participant?.name ?? '', - signature: mediaReleaseForm?.signature ?? '', - signature_date: mediaReleaseForm?.signature_date ?? null, - phone_number: - mediaReleaseForm?.phone_number ?? participant?.cell_phone_number ?? '', - email: mediaReleaseForm?.email ?? participant?.user.email ?? '', - }) - - return
+ const { data, setData, errors, processing, ...form } = + useForm({ + printed_name: mediaReleaseForm?.printed_name ?? participant?.name ?? '', + signature: mediaReleaseForm?.signature ?? '', + signature_date: + mediaReleaseForm?.signature_date ?? dayjs().format('MM/DD/YYYY') ?? '', + phone_number: + mediaReleaseForm?.phone_number ?? participant?.cell_phone_number ?? '', + email: mediaReleaseForm?.email ?? participant?.user.email ?? '', + }) + + const continueToNextStep = () => { + router.visit(route(nextRoute)) + } + + window.console.log(mediaReleaseForm) + + return ( +
{ + e.preventDefault() + if (viewOnly) { + continueToNextStep() + return + } + + if (mediaReleaseForm?.id) { + form.post(route('intake.media-release.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } else { + form.post(route('intake.media-release.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + className="flex flex-col gap-6 w-full max-w-[80%] mx-auto" + > +
+

MULTI-MEDIA RELEASE FORM

+

+ Good Dads has my permission to use my photograph, videos, or written + or spoken statements publicly to promote the Good Dads Organization. I + understand that the images and statements may be used in print + publications, online publications, presentations, websites, and social + media. I also understand that no royalty, fee or other compensation + shall become payable to me by reason of such use. +

+
+ + setData('printed_name', e.target.value)} + /> + +
+ +
+
+ + setData('signature', e.target.value)} + /> + +
+
+ + setData('signature_date', e.target.value)} + /> + +
+
+
+
+ + setData('phone_number', e.target.value)} + /> + +
+
+ + setData('email', e.target.value)} + /> + +
+
+
+
+ +
+
+ ) } export default MediaReleaseForm diff --git a/resources/js/Pages/Intake/IntakeComplete.tsx b/resources/js/Pages/Intake/IntakeComplete.tsx new file mode 100644 index 00000000..963f18c2 --- /dev/null +++ b/resources/js/Pages/Intake/IntakeComplete.tsx @@ -0,0 +1,106 @@ +import React from 'react' +import IntakeLayout from '@/Layouts/IntakeLayout' +import type { PageProps } from '@/types' +import { Participant } from '@/types/participant' +import { Button } from '@/Components/ui' +import { router } from '@inertiajs/react' + +interface IntakeCompleteProps extends PageProps { + participant: Participant +} + +export const IntakeComplete: React.FC = ({ + participant, +}) => { + return ( + +
+

+ Intake Complete. Please meet with the staff to sign physical copies of + the forms. +

+
+ +
+

+ Return and update forms if needed +

+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ ) +} + +export default IntakeComplete diff --git a/resources/js/Pages/Intake/MediaRelease/Create.tsx b/resources/js/Pages/Intake/MediaRelease/Create.tsx index 1d9bc10a..77c600fb 100644 --- a/resources/js/Pages/Intake/MediaRelease/Create.tsx +++ b/resources/js/Pages/Intake/MediaRelease/Create.tsx @@ -1,26 +1,20 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import MediaReleaseForm from '@/Components/Intake/MediaReleaseForm' +import IntakeLayout from '@/Layouts/IntakeLayout' +import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' -interface AssessmentPageProps extends PageProps { +interface ReleasePageProps extends PageProps { participant: Participant + mediaReleaseForm: IntakeMediaReleaseForm } -export const Create: React.FC = ({ - auth, - participant, -}) => { +export const Create: React.FC = ({ participant }) => { return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
-
+ + + ) } diff --git a/resources/js/Pages/Intake/MediaRelease/Edit.tsx b/resources/js/Pages/Intake/MediaRelease/Edit.tsx index 7e209d41..7aec549e 100644 --- a/resources/js/Pages/Intake/MediaRelease/Edit.tsx +++ b/resources/js/Pages/Intake/MediaRelease/Edit.tsx @@ -1,23 +1,29 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import MediaReleaseForm from '@/Components/Intake/MediaReleaseForm' +import IntakeLayout from '@/Layouts/IntakeLayout' +import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' -interface AssessmentPageProps extends PageProps { +interface ReleasePageProps extends PageProps { participant: Participant + mediaRelease: IntakeMediaReleaseForm } -export const Edit: React.FC = ({ auth, participant }) => { +export const Edit: React.FC = ({ + participant, + mediaRelease, +}) => { return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
-
+ + + ) } diff --git a/resources/js/Pages/Intake/MediaRelease/Index.tsx b/resources/js/Pages/Intake/MediaRelease/Index.tsx index d957666b..464f3823 100644 --- a/resources/js/Pages/Intake/MediaRelease/Index.tsx +++ b/resources/js/Pages/Intake/MediaRelease/Index.tsx @@ -1,23 +1,112 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import IntakeLayout from '@/Layouts/IntakeLayout' +import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' +import { Button } from '@/Components/ui' +import { router } from '@inertiajs/react' +import { clsx } from 'clsx' interface AssessmentPageProps extends PageProps { participant: Participant + mediaReleases: IntakeMediaReleaseForm[] } -export const Index: React.FC = ({ auth, participant }) => { +export const Index: React.FC = ({ + participant, + mediaReleases, +}) => { + if (mediaReleases.length === 0) { + router.visit(route('intake.media-release.create')) + } + return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
+ +
+
Signed Date
+
Signed Name
+
Actions
+ {mediaReleases.map((mediaRelease, index) => ( + +
+ {mediaRelease.signature_date} +
+
+ {mediaRelease.signature} +
+
+ + + +
+
+ ))} +
+
+ + + {mediaReleases.length > 0 && ( + + )}
- +
) } diff --git a/resources/js/Pages/Intake/MediaRelease/Show.tsx b/resources/js/Pages/Intake/MediaRelease/Show.tsx index f5858cc8..1f9459b3 100644 --- a/resources/js/Pages/Intake/MediaRelease/Show.tsx +++ b/resources/js/Pages/Intake/MediaRelease/Show.tsx @@ -1,23 +1,28 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import MediaReleaseForm from '@/Components/Intake/MediaReleaseForm' +import IntakeLayout from '@/Layouts/IntakeLayout' +import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' -interface AssessmentPageProps extends PageProps { +interface ReleasePageProps extends PageProps { participant: Participant + mediaReleaseForm: IntakeMediaReleaseForm } -export const Show: React.FC = ({ auth, participant }) => { +export const Show: React.FC = ({ + participant, + mediaReleaseForm, +}) => { return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
-
+ + + ) } diff --git a/routes/web.php b/routes/web.php index b7c676bd..93a50703 100644 --- a/routes/web.php +++ b/routes/web.php @@ -40,6 +40,9 @@ Route::name('intake.') ->prefix('intake') ->group(function () { + Route::get('/dev-auth', [IntakeController::class, 'devAuth']); + Route::get('/intake-complete', [IntakeController::class, 'intakeComplete'])->name('complete'); + Route::middleware(['role:intake'])->group(function () { Route::get('/', [IntakeController::class, 'index'])->name('index'); Route::get('register', [ParticipantRegistrationController::class, 'create'])->name('register'); From 0cca6aeb397e24f5d7d2c1823a4ea3cb52d14a8a Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Sun, 23 Mar 2025 18:34:30 -0500 Subject: [PATCH 15/40] Add Fatherhood Assessment Forms (WIP) --- .../Intake/FatherhoodAssessmentForm.tsx | 818 ++++++++++++++++++ .../js/Components/Intake/MediaReleaseForm.tsx | 2 +- .../Intake/FatherhoodAssessment/Create.tsx | 23 +- .../Intake/FatherhoodAssessment/Edit.tsx | 29 +- .../Intake/FatherhoodAssessment/Index.tsx | 112 ++- .../Intake/FatherhoodAssessment/Show.tsx | 29 +- .../intake-fatherhood-assessment-form.ts | 29 + 7 files changed, 991 insertions(+), 51 deletions(-) create mode 100644 resources/js/Components/Intake/FatherhoodAssessmentForm.tsx create mode 100644 resources/js/types/intake-fatherhood-assessment-form.ts diff --git a/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx b/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx new file mode 100644 index 00000000..e2377ad9 --- /dev/null +++ b/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx @@ -0,0 +1,818 @@ +import React, { useState, useEffect } from 'react' +import { router, useForm } from '@inertiajs/react' + +import dayjs from 'dayjs' +import type { IntakeFatherhoodAssessmentForm } from '@/types/intake-fatherhood-assessment-form' +import type { Participant } from '@/types/participant' + +// Type for our form data +interface FatherhoodAssessmentForm + extends Record { + vendor_name: string + participant_name: string + date_of_birth: string + social_security_number: string + is_missouri_resident: boolean + child_is_under_18: boolean + is_financially_eligible: boolean + drivers_license_provided: boolean + utility_bill_provided: boolean + pay_stub_provided: boolean + written_employer_statement_provided: boolean + social_security_benefits_provided: boolean + self_attestation_provided: boolean + unemployment_compensation_provided: boolean + other_provided: boolean + other_provided_name: string + gross_monthly_household_income: number | '' + number_of_family_members: number | '' + percentage_of_fpl: number | '' + approved_for_services: boolean | null + state_agency_review_date: string +} + +interface FatherhoodAssessmentFormProps { + fatherhoodAssessmentForm?: IntakeFatherhoodAssessmentForm + participant: Participant + viewOnly?: boolean + nextRoute?: string +} + +const FatherhoodAssessmentForm: React.FC = ({ + participant, + fatherhoodAssessmentForm, + viewOnly = false, + nextRoute = 'intake.fatherhood-survey.index', +}) => { + const { data, setData, processing, errors, ...form } = + useForm({ + vendor_name: 'Good Dads', // Default value based on form image + participant_name: + fatherhoodAssessmentForm?.participant_name ?? participant?.name ?? '', + date_of_birth: fatherhoodAssessmentForm?.date_of_birth ?? '', + social_security_number: + fatherhoodAssessmentForm?.social_security_number ?? '', + is_missouri_resident: + fatherhoodAssessmentForm?.is_missouri_resident ?? true, + child_is_under_18: fatherhoodAssessmentForm?.child_is_under_18 ?? true, + is_financially_eligible: + fatherhoodAssessmentForm?.is_financially_eligible ?? true, + drivers_license_provided: + fatherhoodAssessmentForm?.drivers_license_provided ?? false, + utility_bill_provided: + fatherhoodAssessmentForm?.utility_bill_provided ?? false, + pay_stub_provided: fatherhoodAssessmentForm?.pay_stub_provided ?? false, + written_employer_statement_provided: + fatherhoodAssessmentForm?.written_employer_statement_provided ?? false, + social_security_benefits_provided: + fatherhoodAssessmentForm?.social_security_benefits_provided ?? false, + self_attestation_provided: + fatherhoodAssessmentForm?.self_attestation_provided ?? false, + unemployment_compensation_provided: + fatherhoodAssessmentForm?.unemployment_compensation_provided ?? false, + other_provided: fatherhoodAssessmentForm?.other_provided ?? false, + other_provided_name: fatherhoodAssessmentForm?.other_provided_name ?? '', + gross_monthly_household_income: + fatherhoodAssessmentForm?.gross_monthly_household_income ?? '', + number_of_family_members: + fatherhoodAssessmentForm?.number_of_family_members ?? '', + percentage_of_fpl: 0, + approved_for_services: null, + state_agency_review_date: '', + }) + + // Calculate FPL percentage when income or family members change + useEffect(() => { + if ( + data.gross_monthly_household_income !== '' && + data.number_of_family_members !== '' + ) { + // This is a simplified calculation - in reality you would use the actual FPL guidelines + // For this example we'll do a simple calculation + const annualIncome = Number(data.gross_monthly_household_income) * 12 + const baseFPL = 14580 // 2023 base value for 1 person + const additionalPersonValue = 5140 // Additional amount per person + + const fplThreshold = + baseFPL + + (Number(data.number_of_family_members) - 1) * additionalPersonValue + const fplPercentage = (annualIncome / fplThreshold) * 100 + + setData('percentage_of_fpl', parseFloat(fplPercentage.toFixed(2))) + } + }, [data.gross_monthly_household_income, data.number_of_family_members]) + + // Format SSN as user types (XXX-XX-XXXX) + const handleSSNChange = (e: React.ChangeEvent) => { + let value = e.target.value.replace(/\D/g, '') + if (value.length > 9) { + value = value.slice(0, 9) + } + + // Format with hyphens + if (value.length > 5) { + value = `${value.slice(0, 3)}-${value.slice(3, 5)}-${value.slice(5)}` + } else if (value.length > 3) { + value = `${value.slice(0, 3)}-${value.slice(3)}` + } + + setData('social_security_number', value) + } + const continueToNextStep = () => { + router.visit(route(nextRoute)) + } + + return ( +
+
{ + e.preventDefault() + + if (viewOnly) { + continueToNextStep() + return + } + + if (fatherhoodAssessmentForm?.id) { + form.post(route('intake.fatherhood-assessment.update'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } else { + form.post(route('intake.fatherhood-assessment.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + className="bg-white rounded-lg shadow-md p-6" + > +

+ Healthy Marriage and Responsible Fatherhood Assessment Worksheet +

+ + {/* Vendor Information */} +
+ + setData('vendor_name', e.target.value)} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + /> + {errors.vendor_name && ( +
+ {errors.vendor_name} +
+ )} +
+ + {/* Participant Information Section */} +
+

+ Participant Information +

+ +
+ + setData('participant_name', e.target.value)} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> + {errors.participant_name && ( +
+ {errors.participant_name} +
+ )} +
+ +
+ + setData('date_of_birth', e.target.value)} + max={dayjs().format('YYYY-MM-DD')} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> + {errors.date_of_birth && ( +
+ {errors.date_of_birth} +
+ )} +
+ +
+ + + {errors.social_security_number && ( +
+ {errors.social_security_number} +
+ )} +
+
+ + {/* Eligibility Information Section */} +
+

+ Eligibility Information +

+ +
+
+ +
+ + +
+
+ {errors.is_missouri_resident && ( +
+ {errors.is_missouri_resident} +
+ )} +
+ +
+
+ +
+ + +
+
+ {errors.child_is_under_18 && ( +
+ {errors.child_is_under_18} +
+ )} +
+ +
+
+ +
+ + +
+
+ {errors.is_financially_eligible && ( +
+ {errors.is_financially_eligible} +
+ )} +
+
+ + {/* Financial Assessment Section */} +
+

+ Financial Assessment (Documents Provided) +

+ +
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + setData('other_provided_name', e.target.value) + } + className="mt-1 block w-40 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + disabled={!data.other_provided} + /> +
+
+ + +
+
+ {errors.other_provided_name && ( +
+ {errors.other_provided_name} +
+ )} +
+
+ + {/* Poverty Level Section */} +
+

+ Poverty Level Percentage Determination (using the provided tool): +

+ +
+
+ +
+
+ $ +
+ + setData( + 'gross_monthly_household_income', + e.target.value === '' ? '' : parseFloat(e.target.value), + ) + } + min="0" + step="0.01" + className="pl-7 mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> +
+ {errors.gross_monthly_household_income && ( +
+ {errors.gross_monthly_household_income} +
+ )} +
+ +
+ + + setData( + 'number_of_family_members', + e.target.value === '' ? '' : parseInt(e.target.value), + ) + } + min="1" + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> + {errors.number_of_family_members && ( +
+ {errors.number_of_family_members} +
+ )} +
+ +
+ +
+ + setData('percentage_of_fpl', parseFloat(e.target.value)) + } + min="0" + max="99.99" + step="0.01" + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + readOnly + /> +
+ % +
+
+ {errors.percentage_of_fpl && ( +
+ {errors.percentage_of_fpl} +
+ )} +
+
+
+ + {/* State Agency Section */} +
+

+ COMPLETED BY STATE AGENCY +

+ +
+
+ +
+ + +
+
+
+ +
+ + + setData('state_agency_review_date', e.target.value) + } + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + /> + {errors.state_agency_review_date && ( +
+ {errors.state_agency_review_date} +
+ )} +
+
+ +
+ +
+
+
+ ) +} + +export default FatherhoodAssessmentForm diff --git a/resources/js/Components/Intake/MediaReleaseForm.tsx b/resources/js/Components/Intake/MediaReleaseForm.tsx index 85770c23..3871a56f 100644 --- a/resources/js/Components/Intake/MediaReleaseForm.tsx +++ b/resources/js/Components/Intake/MediaReleaseForm.tsx @@ -53,7 +53,7 @@ export const MediaReleaseForm: React.FC = ({ } if (mediaReleaseForm?.id) { - form.post(route('intake.media-release.store'), { + form.put(route('intake.media-release.update'), { onSuccess: () => { continueToNextStep() }, diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx index 1d9bc10a..127fc7b5 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx @@ -1,26 +1,19 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import IntakeLayout from '@/Layouts/IntakeLayout' +import FatherhoodAssessmentForm from '@/Components/Intake/FatherhoodAssessmentForm' -interface AssessmentPageProps extends PageProps { +interface IntakePageProps extends PageProps { participant: Participant + fatherhoodAssessment: any } -export const Create: React.FC = ({ - auth, - participant, -}) => { +export const Create: React.FC = ({ participant }) => { return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
-
+ + + ) } diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx index 7e209d41..cb993389 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Edit.tsx @@ -1,24 +1,27 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import IntakeLayout from '@/Layouts/IntakeLayout' +import FatherhoodAssessmentForm from '@/Components/Intake/FatherhoodAssessmentForm' +import type { IntakeFatherhoodAssessmentForm } from '@/types/intake-fatherhood-assessment-form' -interface AssessmentPageProps extends PageProps { +interface IntakePageProps extends PageProps { participant: Participant + fatherhoodAssessment: IntakeFatherhoodAssessmentForm } -export const Edit: React.FC = ({ auth, participant }) => { +export const Create: React.FC = ({ + participant, + fatherhoodAssessment, +}) => { return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
-
+ + + ) } -export default Edit +export default Create diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx index d957666b..27c3102e 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx @@ -1,23 +1,115 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import IntakeLayout from '@/Layouts/IntakeLayout' +import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' +import { Button } from '@/Components/ui' +import { router } from '@inertiajs/react' +import { clsx } from 'clsx' +import type { IntakeFatherhoodAssessmentForm } from '@/types/intake-fatherhood-assessment-form' +import dayjs from 'dayjs' interface AssessmentPageProps extends PageProps { participant: Participant + fatherhoodAssessments: IntakeFatherhoodAssessmentForm[] } -export const Index: React.FC = ({ auth, participant }) => { +export const Index: React.FC = ({ + participant, + fatherhoodAssessments, +}) => { + if (fatherhoodAssessments.length === 0) { + router.visit(route('intake.fatherhood-assessment.create')) + } + return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
+ +
+
Signed Date
+
Signed Name
+
Actions
+ {fatherhoodAssessments.map((assessment, index) => ( + +
+ {assessment.participant_name} +
+
+ {dayjs(assessment.created_at).format('MM/DD/YYYY')} +
+
+ + + +
+
+ ))} +
+
+ + + {fatherhoodAssessments.length > 0 && ( + + )}
- +
) } diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx index f5858cc8..6594fd1d 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Show.tsx @@ -1,23 +1,28 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import IntakeLayout from '@/Layouts/IntakeLayout' +import FatherhoodAssessmentForm from '@/Components/Intake/FatherhoodAssessmentForm' +import type { IntakeFatherhoodAssessmentForm } from '@/types/intake-fatherhood-assessment-form' -interface AssessmentPageProps extends PageProps { +interface IntakePageProps extends PageProps { participant: Participant + fatherhoodAssessment: IntakeFatherhoodAssessmentForm } -export const Show: React.FC = ({ auth, participant }) => { +export const Show: React.FC = ({ + participant, + fatherhoodAssessment, +}) => { return ( - - -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
-
+ + + ) } diff --git a/resources/js/types/intake-fatherhood-assessment-form.ts b/resources/js/types/intake-fatherhood-assessment-form.ts new file mode 100644 index 00000000..20164afe --- /dev/null +++ b/resources/js/types/intake-fatherhood-assessment-form.ts @@ -0,0 +1,29 @@ +export interface IntakeFatherhoodAssessmentForm { + id: string + participant_id: string + + vendor_name: string + participant_name: string + date_of_birth: string + social_security_number: string + is_missouri_resident: boolean + child_is_under_18: boolean + is_financially_eligible: boolean + drivers_license_provided: boolean + utility_bill_provided: boolean + pay_stub_provided: boolean + written_employer_statement_provided: boolean + social_security_benefits_provided: boolean + self_attestation_provided: boolean + unemployment_compensation_provided: boolean + other_provided: boolean + other_provided_name: string + gross_monthly_household_income: number + number_of_family_members: number + percentage_of_fpl: number | null + approved_for_services: boolean | null + state_agency_review_date: string + + created_at: string + updated_at: string +} From f6bbd4f3cd6097fc5c417b84014dd9566d2d9d5b Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Tue, 25 Mar 2025 18:51:08 -0500 Subject: [PATCH 16/40] Refactor Disclosure Auth Test and Fix issues with ParticipantDisclosureAuthorizationFactory --- ...ticipantDisclosureAuthorizationFactory.php | 6 +- .../Intake/ParticipantDisclosureTest.php | 147 +++--------------- 2 files changed, 24 insertions(+), 129 deletions(-) diff --git a/database/factories/ParticipantDisclosureAuthorizationFactory.php b/database/factories/ParticipantDisclosureAuthorizationFactory.php index 2eb13340..41a040b2 100644 --- a/database/factories/ParticipantDisclosureAuthorizationFactory.php +++ b/database/factories/ParticipantDisclosureAuthorizationFactory.php @@ -25,6 +25,9 @@ class ParticipantDisclosureAuthorizationFactory extends Factory */ public function definition(): array { + + $isOtherAuthorized = $this->faker->boolean(); + return [ 'participant_id' => Participant::factory(), 'consumer_name' => $this->faker->name(), @@ -36,7 +39,8 @@ public function definition(): array 'is_fsd_authorized' => $this->faker->boolean(), 'is_cd_authorized' => $this->faker->boolean(), 'is_dls_authorized' => $this->faker->boolean(), - 'other_authorized_entity' => $this->faker->company(), + 'is_other_authorized' => $isOtherAuthorized, + 'other_authorized_entity' => $isOtherAuthorized ? $this->faker->company() : null, 'subject_name' => $this->faker->name(), 'subject_phone' => $this->faker->phoneNumber(), 'subject_dob' => $this->faker->date(), diff --git a/tests/Feature/Intake/ParticipantDisclosureTest.php b/tests/Feature/Intake/ParticipantDisclosureTest.php index f7fd49f9..76e89327 100644 --- a/tests/Feature/Intake/ParticipantDisclosureTest.php +++ b/tests/Feature/Intake/ParticipantDisclosureTest.php @@ -3,7 +3,7 @@ namespace Tests\Feature\Intake; use App\Models\Participant; -use App\Models\User; +use App\Models\ParticipantDisclosureAuthorization; use Database\Seeders\PermissionsSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; use Inertia\Testing\AssertableInertia; @@ -19,34 +19,21 @@ public function setUp(): void $this->seed(PermissionsSeeder::class); } - /** - * Test that the disclosure index is rendered properly - */ + public function test_disclosure_index_is_rendered(): void { - $participantUser = User::factory()->create(); - $participantUser->assignRole('participant'); - $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + $disclosure = ParticipantDisclosureAuthorization::factory()->create(); + $participantUser = $disclosure->participant->user; $response = $this->actingAs($participantUser)->get(route('intake.disclosure.index')); - $response->assertStatus(200); - - - $disclosureData = $this->getDisclosureData(); - $participant->disclosureAuthorizations()->create($disclosureData); - - $participantUser->refresh(); - $response = $this->actingAs($participantUser)->get(route('intake.disclosure.index')); - $response->assertInertia(fn(AssertableInertia $page) => $page->count('disclosureAuthorizations', 1)); - } + public function test_disclosure_create_form_is_rendered(): void { - $participantUser = User::factory()->create(); - $participantUser->assignRole('participant'); - Participant::factory()->create(['user_id' => $participantUser->id]); + $participant = Participant::factory()->create(); + $participantUser = $participant->user; $response = $this->actingAs($participantUser)->get(route('intake.disclosure.create')); @@ -55,12 +42,8 @@ public function test_disclosure_create_form_is_rendered(): void public function test_disclosure_edit_is_rendered(): void { - $participantUser = User::factory()->create(); - $participantUser->assignRole('participant'); - $participant = Participant::factory()->create(['user_id' => $participantUser->id]); - - $disclosureData = $this->getDisclosureData(); - $disclosure = $participant->disclosureAuthorizations()->create($disclosureData); + $disclosure = ParticipantDisclosureAuthorization::factory()->create(); + $participantUser = $disclosure->participant->user; $response = $this->actingAs($participantUser)->get(route('intake.disclosure.update', $disclosure->id)); @@ -72,35 +55,29 @@ public function test_disclosure_edit_is_rendered(): void */ public function test_participant_can_create_disclosure(): void { - $participantUser = User::factory()->create(); - $participantUser->assignRole('participant'); - $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + $participant = Participant::factory()->create(); + $participantUser = $participant->user; - $disclosureData = $this->getDisclosureData(); + $disclosureData = ParticipantDisclosureAuthorization::factory()->make()->attributesToArray(); $this->actingAs($participantUser)->post(route('intake.disclosure.store'), $disclosureData); $this->assertNotNull($participant->disclosureAuthorizations); } - /** - * Test that a participant can update an existing disclosure agreement. - */ public function test_participant_can_update_disclosure(): void { - $participantUser = User::factory()->create(); - $participantUser->assignRole('participant'); - $participant = Participant::factory()->create(['user_id' => $participantUser->id]); + $disclosure = ParticipantDisclosureAuthorization::factory()->create(); + $participant = $disclosure->participant; + $participantUser = $participant->user; - $disclosureData = $this->getDisclosureData(); - $disclosure = $participant->disclosureAuthorizations()->create($disclosureData); + $disclosureData = $disclosure->attributesToArray(); $updatedData = [ ...$disclosureData, 'consumer_name' => 'Jane Doe', ]; - // Test the update endpoint $updateRoute = route('intake.disclosure.update', ['disclosureAuthorization' => $disclosure->id]); $updateResponse = $this->actingAs($participantUser)->put($updateRoute, $updatedData); @@ -116,12 +93,9 @@ public function test_participant_can_update_disclosure(): void */ public function test_participant_can_delete_disclosure(): void { - $participantUser = User::factory()->create(); - $participantUser->assignRole('participant'); - $participant = Participant::factory()->create(['user_id' => $participantUser->id]); - - $disclosureData = $this->getDisclosureData(); - $disclosure = $participant->disclosureAuthorizations()->create($disclosureData); + $disclosure = ParticipantDisclosureAuthorization::factory()->create(); + $participant = $disclosure->participant; + $participantUser = $participant->user; // Test the update endpoint $updateRoute = route('intake.disclosure.destroy', ['disclosureAuthorization' => $disclosure->id]); @@ -132,87 +106,4 @@ public function test_participant_can_delete_disclosure(): void $this->assertCount(0, $participant->disclosureAuthorizations); } - - public function getDisclosureData(): array - { - return [ - 'consumer_name' => 'John Doe', - - // Authorized entities - 'is_dss_authorized' => true, - 'is_dys_authorized' => false, - 'is_mhd_authorized' => true, - 'is_dfas_authorized' => false, - 'is_mmac_authorized' => true, - 'is_fsd_authorized' => false, - 'is_cd_authorized' => true, - 'is_dls_authorized' => false, - 'is_other_authorized' => true, - 'other_authorized_entity' => 'Test Entity', - - // Subject information - 'subject_name' => 'John Doe', - 'subject_phone' => '555-123-4567', - 'subject_dob' => '1990-01-01', - 'subject_ssn' => '123-45-6789', - 'subject_address' => '123 Test St', - 'subject_email' => 'john@example.com', - - // Recipients - 'disclose_to_attorney' => true, - 'attorney_name' => 'Jane Smith', - 'disclose_to_employer' => false, - 'employer_name' => null, - 'disclose_to_legislator' => true, - 'legislator_name' => 'Sen. Johnson', - 'disclose_to_governors_staff' => false, - 'other_recipient_details' => 'Test Recipient', - - // Purpose of disclosure - 'purpose_eligibility_determination' => true, - 'purpose_legal_consultation' => false, - 'purpose_legal_proceedings' => true, - 'purpose_employment' => false, - 'purpose_complaint_investigation' => true, - 'purpose_treatment_planning' => false, - 'purpose_continuity_of_services' => true, - 'purpose_background_investigation' => false, - 'purpose_consumer_request' => true, - 'purpose_share_and_refer' => false, - 'purpose_other' => true, - 'other_purpose_details' => 'Test Purpose', - - // Information to disclose - 'disclose_entire_file' => true, - 'disclose_licensure_information' => false, - 'disclose_medical_psychiatric_records' => true, - 'disclose_hotline_investigations' => false, - 'disclose_home_studies' => true, - 'disclose_eligibility_determinations' => false, - 'disclose_substance_abuse_treatment' => true, - 'disclose_client_employment_records' => false, - 'disclose_benefits_received' => true, - 'disclose_other_information' => false, - 'other_disclosure_details' => null, - - // Communication preferences - 'accept_text_messages' => true, - - // Signatures - 'consumer_signature' => 'John Doe', - 'signature_date' => now()->format('Y-m-d'), - 'witness_signature' => 'Jane Witness', - 'witness_signature_date' => now()->format('Y-m-d'), - 'guardian_signature' => null, - 'guardian_signature_date' => null, - - // Survey preferences - 'survey_by_email' => true, - 'survey_by_mail' => false, - 'survey_by_online' => true, - - 'date_completed' => now()->format('Y-m-d'), - ]; - - } } \ No newline at end of file From 7bb20b251cfbf69575bb775ea7bd7021bf75135e Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:40:52 -0500 Subject: [PATCH 17/40] Basic implementation of Fatherhood Survey Form --- .../Intake/FatherhoodSurveyForm.tsx | 513 ++++++++++++++++++ .../Intake/FatherhoodAssessment/Index.tsx | 10 +- .../Pages/Intake/FatherhoodSurvey/Create.tsx | 16 +- .../js/Pages/Intake/FatherhoodSurvey/Edit.tsx | 20 +- .../Pages/Intake/FatherhoodSurvey/Index.tsx | 105 +++- .../js/Pages/Intake/FatherhoodSurvey/Show.tsx | 20 +- .../js/types/intake-fatherhood-survey-form.ts | 45 ++ 7 files changed, 689 insertions(+), 40 deletions(-) create mode 100644 resources/js/Components/Intake/FatherhoodSurveyForm.tsx create mode 100644 resources/js/types/intake-fatherhood-survey-form.ts diff --git a/resources/js/Components/Intake/FatherhoodSurveyForm.tsx b/resources/js/Components/Intake/FatherhoodSurveyForm.tsx new file mode 100644 index 00000000..1b47c177 --- /dev/null +++ b/resources/js/Components/Intake/FatherhoodSurveyForm.tsx @@ -0,0 +1,513 @@ +// Types + +// React Component +import React, { useState, FormEvent } from 'react' +import { router, useForm } from '@inertiajs/react' +import type { IntakeFatherhoodSurveyFormData } from '@/types/intake-fatherhood-survey-form' +import type { Participant } from '@/types/participant' + +interface FatherhoodSurveyFormData + extends Record { + date_of_birth: string + fatherhood_program: string + + // Reason fields + reason_become_responsible_father: boolean + reason_referred: boolean + reason_court_ordered: boolean + reason_address_child_support_concerns: boolean + reason_other: boolean + reason_other_description: string + + // Referred by fields + referred_by_word_of_mouth: boolean + referred_by_past_participant: boolean + referred_by_family_support_division: boolean + referred_by_prosecuting_attorney: boolean + referred_by_marketing: boolean + referred_by_organization_itself: boolean + referred_by_other: boolean + referred_by_other_source: string + + // Expectations fields + employment_opportunities_expected: boolean + assistance_with_alcohol_abuse_expected: boolean + increased_emphasis_on_parenting_skills_expected: boolean + access_to_mentors_resources_outside_program_expected: boolean + resume_building_skills_expected: boolean + free_legal_services_expected: boolean + + assistance_with_criminal_history_expected: boolean + assistance_with_credit_repair_expected: boolean + assistance_with_overcoming_homelessness_expected: boolean + assistance_with_visitation_custody_expected: boolean + increased_understanding_of_child_support_issues_expected: boolean + maintaining_hope_for_the_future_expected: boolean + help_obtaining_information_about_health_wellness_expected: boolean + + other_expected: boolean + other_expectations_description: string +} + +interface FatherhoodSurveyFormProps { + fatherhoodSurvey?: IntakeFatherhoodSurveyFormData + participant: Participant + viewOnly?: boolean + nextRoute?: string +} + +const FatherhoodSurveyForm: React.FC = ({ + participant, + fatherhoodSurvey, + viewOnly = false, + nextRoute = 'intake.service-plan.index', +}) => { + const { data, setData, processing, errors, ...form } = + useForm({ + fatherhood_program: 'Good Dads', // Default value + date_of_birth: '', + + // Reason fields - default to false + reason_become_responsible_father: + fatherhoodSurvey?.reason_become_responsible_father ?? false, + reason_referred: fatherhoodSurvey?.reason_referred ?? false, + reason_court_ordered: fatherhoodSurvey?.reason_court_ordered ?? false, + reason_address_child_support_concerns: + fatherhoodSurvey?.reason_address_child_support_concerns ?? false, + reason_other: fatherhoodSurvey?.reason_other ?? false, + reason_other_description: + fatherhoodSurvey?.reason_other_description ?? '', + + // Referred by fields - default to false + referred_by_word_of_mouth: + fatherhoodSurvey?.referred_by_word_of_mouth ?? false, + referred_by_past_participant: + fatherhoodSurvey?.referred_by_past_participant ?? false, + referred_by_family_support_division: + fatherhoodSurvey?.referred_by_family_support_division ?? false, + referred_by_prosecuting_attorney: + fatherhoodSurvey?.referred_by_prosecuting_attorney ?? false, + referred_by_marketing: fatherhoodSurvey?.referred_by_marketing ?? false, + referred_by_organization_itself: + fatherhoodSurvey?.referred_by_organization_itself ?? false, + referred_by_other: fatherhoodSurvey?.referred_by_other ?? false, + referred_by_other_source: + fatherhoodSurvey?.referred_by_other_source ?? '', + + // Expectations fields - default to false + employment_opportunities_expected: + fatherhoodSurvey?.employment_opportunities_expected ?? false, + assistance_with_alcohol_abuse_expected: + fatherhoodSurvey?.assistance_with_alcohol_abuse_expected ?? false, + increased_emphasis_on_parenting_skills_expected: + fatherhoodSurvey?.increased_emphasis_on_parenting_skills_expected ?? + false, + access_to_mentors_resources_outside_program_expected: + fatherhoodSurvey?.access_to_mentors_resources_outside_program_expected ?? + false, + resume_building_skills_expected: + fatherhoodSurvey?.resume_building_skills_expected ?? false, + free_legal_services_expected: + fatherhoodSurvey?.free_legal_services_expected ?? false, + + assistance_with_criminal_history_expected: + fatherhoodSurvey?.assistance_with_criminal_history_expected ?? false, + assistance_with_credit_repair_expected: + fatherhoodSurvey?.assistance_with_credit_repair_expected ?? false, + assistance_with_overcoming_homelessness_expected: + fatherhoodSurvey?.assistance_with_overcoming_homelessness_expected ?? + false, + assistance_with_visitation_custody_expected: + fatherhoodSurvey?.assistance_with_visitation_custody_expected ?? false, + increased_understanding_of_child_support_issues_expected: + fatherhoodSurvey?.increased_understanding_of_child_support_issues_expected ?? + false, + maintaining_hope_for_the_future_expected: + fatherhoodSurvey?.maintaining_hope_for_the_future_expected ?? false, + help_obtaining_information_about_health_wellness_expected: + fatherhoodSurvey?.help_obtaining_information_about_health_wellness_expected ?? + false, + other_expected: fatherhoodSurvey?.other_expected ?? false, + other_expectations_description: + fatherhoodSurvey?.other_expectations_description ?? '', + }) + + const toggleCheckbox = (field: keyof FatherhoodSurveyFormData) => { + setData(field, !data[field] as boolean) + } + const continueToNextStep = () => { + window.console.log(nextRoute) + + router.visit(route(nextRoute)) + } + + return ( +
+

+ Healthy Marriage and Responsible Fatherhood Survey +

+ +
{ + e.preventDefault() + + if (viewOnly) { + continueToNextStep() + return + } + + if (fatherhoodSurvey?.id) { + form.put( + route('intake.fatherhood-survey.update', [fatherhoodSurvey.id]), + { + onSuccess: () => { + continueToNextStep() + }, + }, + ) + } else { + form.post(route('intake.fatherhood-survey.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + className="space-y-6" + > + {/* Personal Information Section */} +
+

Personal Information

+
+
+ + setData('date_of_birth', e.target.value)} + className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3" + /> +
+
+ + setData('fatherhood_program', e.target.value)} + className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3" + required + /> + {errors.fatherhood_program && ( +

+ {errors.fatherhood_program} +

+ )} +
+
+
+ + {/* Reasons Section */} +
+

Why are you here?

+
+ {[ + { + field: 'reason_become_responsible_father', + label: 'I want to become a more responsible father', + }, + { + field: 'reason_referred', + label: 'I was referred (ex. From Probation and Parole, etc.)', + }, + { field: 'reason_court_ordered', label: 'I was court ordered' }, + { + field: 'reason_address_child_support_concerns', + label: 'I want to address my child support concerns', + }, + ].map(({ field, label }) => ( +
+ + toggleCheckbox(field as keyof FatherhoodSurveyFormData) + } + className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + /> + +
+ ))} + + {/* Other Reason */} +
+ toggleCheckbox('reason_other')} + className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + /> + +
+ + + setData('reason_other_description', e.target.value) + } + className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3" + placeholder="Please specify other reason" + required={data.reason_other} + /> +
+
+ + {/* Referral Source Section */} +
+

+ How did you hear about this program? +

+
+ {[ + { field: 'referred_by_word_of_mouth', label: 'Word of Mouth' }, + { + field: 'referred_by_past_participant', + label: 'From a Past Participant', + }, + { + field: 'referred_by_family_support_division', + label: 'Family Support Division', + }, + { + field: 'referred_by_prosecuting_attorney', + label: 'Prosecuting Attorney', + }, + { + field: 'referred_by_marketing', + label: 'Marketing (flyers, brochure, social media, etc.)', + }, + { + field: 'referred_by_organization_itself', + label: 'The Organization Itself', + }, + ].map(({ field, label }) => ( +
+ + toggleCheckbox(field as keyof FatherhoodSurveyFormData) + } + className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + /> + +
+ ))} + + {/* Other Referral Source */} +
+ toggleCheckbox('referred_by_other')} + className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + /> + +
+ + + setData('referred_by_other_source', e.target.value) + } + className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3" + placeholder="Please specify other referral source" + required={data.referred_by_other} + /> +
+
+ + {/* Expectations Section */} +
+

+ What do you expect to gain from this program? +

+
+ {[ + { + field: 'employment_opportunities_expected', + label: 'Employment Opportunities', + }, + { + field: 'assistance_with_alcohol_abuse_expected', + label: 'Assistance with Alcohol/Drug Abuse', + }, + { + field: 'increased_emphasis_on_parenting_skills_expected', + label: 'Increased Emphasis on Parenting Skills', + }, + { + field: 'access_to_mentors_resources_outside_program_expected', + label: 'Access to Mentors/Resources Outside of the Program', + }, + { + field: 'resume_building_skills_expected', + label: 'Resume Building Skills', + }, + { + field: 'free_legal_services_expected', + label: 'Free Legal Services', + }, + { + field: 'assistance_with_criminal_history_expected', + label: 'Assistance w/ Criminal History', + }, + { + field: 'assistance_with_credit_repair_expected', + label: 'Assistance w/ Credit Repair', + }, + { + field: 'assistance_with_overcoming_homelessness_expected', + label: 'Assistance w/ Overcoming Homelessness', + }, + { + field: 'assistance_with_visitation_custody_expected', + label: 'Assistance with Visitation/Custody', + }, + { + field: + 'increased_understanding_of_child_support_issues_expected', + label: 'Increased Understanding of Child Support Issues', + }, + { + field: 'maintaining_hope_for_the_future_expected', + label: 'Maintaining Hope for the Future', + }, + { + field: + 'help_obtaining_information_about_health_wellness_expected', + label: 'Help Obtaining Information About Health/Wellness', + }, + ].map(({ field, label }) => ( +
+ + toggleCheckbox(field as keyof FatherhoodSurveyFormData) + } + className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + /> + +
+ ))} + + {/* Other Expectations */} +
+ toggleCheckbox('other_expected')} + className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + /> + +
+
+ +
+ + setData('other_expectations_description', e.target.value) + } + className="block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3" + placeholder="Please specify other expectations" + required={data.other_expected} + /> +
+
+ + {/* Submit Button */} +
+ +
+
+
+ ) +} + +export default FatherhoodSurveyForm diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx index 27c3102e..7daf422e 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx @@ -11,14 +11,14 @@ import dayjs from 'dayjs' interface AssessmentPageProps extends PageProps { participant: Participant - fatherhoodAssessments: IntakeFatherhoodAssessmentForm[] + fatherhoodSurveys: IntakeFatherhoodAssessmentForm[] } export const Index: React.FC = ({ participant, - fatherhoodAssessments, + fatherhoodSurveys, }) => { - if (fatherhoodAssessments.length === 0) { + if (fatherhoodSurveys.length === 0) { router.visit(route('intake.fatherhood-assessment.create')) } @@ -31,7 +31,7 @@ export const Index: React.FC = ({
Signed Date
Signed Name
Actions
- {fatherhoodAssessments.map((assessment, index) => ( + {fatherhoodSurveys.map((assessment, index) => (
{assessment.participant_name} @@ -96,7 +96,7 @@ export const Index: React.FC = ({ Create New Assessment - {fatherhoodAssessments.length > 0 && ( + {fatherhoodSurveys.length > 0 && ( + + +
+
+ ))} +
+
+ + + {fatherhoodSurveys.length > 0 && ( + + )}
-
+ ) } diff --git a/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx b/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx index f5858cc8..908faff1 100644 --- a/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx +++ b/resources/js/Pages/Intake/FatherhoodSurvey/Show.tsx @@ -3,20 +3,28 @@ import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' +import type { IntakeFatherhoodSurveyFormData } from '@/types/intake-fatherhood-survey-form' +import FatherhoodSurveyForm from '@/Components/Intake/FatherhoodSurveyForm' interface AssessmentPageProps extends PageProps { participant: Participant + fatherhoodSurveyForm: IntakeFatherhoodSurveyFormData } -export const Show: React.FC = ({ auth, participant }) => { +export const Show: React.FC = ({ + auth, + participant, + fatherhoodSurveyForm, +}) => { return ( -
-
- Fatherhood Assessment for {participant.user.first_name} -
-
+
) } diff --git a/resources/js/types/intake-fatherhood-survey-form.ts b/resources/js/types/intake-fatherhood-survey-form.ts new file mode 100644 index 00000000..f2db49d5 --- /dev/null +++ b/resources/js/types/intake-fatherhood-survey-form.ts @@ -0,0 +1,45 @@ +export interface IntakeFatherhoodSurveyFormData { + id: string + participant_id: string + + date_of_birth: string + fatherhood_program: string + + // Reason fields + reason_become_responsible_father: boolean + reason_referred: boolean + reason_court_ordered: boolean + reason_address_child_support_concerns: boolean + reason_other: boolean + reason_other_description: string + + // Referred by fields + referred_by_word_of_mouth: boolean + referred_by_past_participant: boolean + referred_by_family_support_division: boolean + referred_by_prosecuting_attorney: boolean + referred_by_marketing: boolean + referred_by_organization_itself: boolean + referred_by_other: boolean + referred_by_other_source: string + + // Expectations fields + employment_opportunities_expected: boolean + assistance_with_alcohol_abuse_expected: boolean + increased_emphasis_on_parenting_skills_expected: boolean + access_to_mentors_resources_outside_program_expected: boolean + resume_building_skills_expected: boolean + free_legal_services_expected: boolean + assistance_with_criminal_history_expected: boolean + assistance_with_credit_repair_expected: boolean + assistance_with_overcoming_homelessness_expected: boolean + assistance_with_visitation_custody_expected: boolean + increased_understanding_of_child_support_issues_expected: boolean + maintaining_hope_for_the_future_expected: boolean + help_obtaining_information_about_health_wellness_expected: boolean + other_expected: boolean + other_expectations_description: string + + created_at: string + updated_at: string +} From 5a53970de703f86153a09922b41e8a37532db291 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:59:45 -0500 Subject: [PATCH 18/40] Rename Request Files --- .../Intake/ParticipantDisclosureController.php | 8 ++++---- .../Controllers/Intake/ParticipantSignupController.php | 4 ++-- ...=> StoreParticipantDisclosureAuthorizationRequest.php} | 2 +- ...StoreRequest.php => StoreParticipantSignupRequest.php} | 2 +- ...> UpdateParticipantDisclosureAuthorizationRequest.php} | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) rename app/Http/Requests/Intake/{ParticipantDisclosureAuthorizationStoreRequest.php => StoreParticipantDisclosureAuthorizationRequest.php} (98%) rename app/Http/Requests/Intake/{ParticipantSignupStoreRequest.php => StoreParticipantSignupRequest.php} (97%) rename app/Http/Requests/Intake/{ParticipantDisclosureAuthorizationUpdateRequest.php => UpdateParticipantDisclosureAuthorizationRequest.php} (98%) diff --git a/app/Http/Controllers/Intake/ParticipantDisclosureController.php b/app/Http/Controllers/Intake/ParticipantDisclosureController.php index 4fd4dc8d..a3debfa7 100644 --- a/app/Http/Controllers/Intake/ParticipantDisclosureController.php +++ b/app/Http/Controllers/Intake/ParticipantDisclosureController.php @@ -3,8 +3,8 @@ namespace App\Http\Controllers\Intake; use App\Http\Controllers\Controller; -use App\Http\Requests\Intake\ParticipantDisclosureAuthorizationStoreRequest; -use App\Http\Requests\Intake\ParticipantDisclosureAuthorizationUpdateRequest; +use App\Http\Requests\Intake\StoreParticipantDisclosureAuthorizationRequest; +use App\Http\Requests\Intake\UpdateParticipantDisclosureAuthorizationRequest; use App\Http\Resources\ParticipantResource; use App\Models\ParticipantDisclosureAuthorization; use Illuminate\Http\RedirectResponse; @@ -41,7 +41,7 @@ public function create(Request $request): Response * * @throws \Illuminate\Validation\ValidationException */ - public function store(ParticipantDisclosureAuthorizationStoreRequest $request) + public function store(StoreParticipantDisclosureAuthorizationRequest $request) { $validated = $request->validated(); @@ -85,7 +85,7 @@ public function edit(Request $request, ParticipantDisclosureAuthorization $discl * * @throws \Illuminate\Validation\ValidationException */ - public function update(ParticipantDisclosureAuthorizationUpdateRequest $request, ParticipantDisclosureAuthorization $disclosureAuthorization): RedirectResponse + public function update(UpdateParticipantDisclosureAuthorizationRequest $request, ParticipantDisclosureAuthorization $disclosureAuthorization): RedirectResponse { $validated = $request->validated(); diff --git a/app/Http/Controllers/Intake/ParticipantSignupController.php b/app/Http/Controllers/Intake/ParticipantSignupController.php index 85378481..686d4734 100644 --- a/app/Http/Controllers/Intake/ParticipantSignupController.php +++ b/app/Http/Controllers/Intake/ParticipantSignupController.php @@ -5,7 +5,7 @@ use App\Enums\Ethnicity; use App\Enums\MaritalStatus; use App\Http\Controllers\Controller; -use App\Http\Requests\Intake\ParticipantSignupStoreRequest; +use App\Http\Requests\Intake\StoreParticipantSignupRequest; use App\Models\Region; use App\Models\User; use App\Services\ParticipantService; @@ -39,7 +39,7 @@ public function create(): Response * * @throws \Illuminate\Validation\ValidationException */ - public function store(ParticipantSignupStoreRequest $request, ParticipantService $participantService): RedirectResponse + public function store(StoreParticipantSignupRequest $request, ParticipantService $participantService): RedirectResponse { $participantData = $request->validated(); $participantData['date'] ??= now(); diff --git a/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php b/app/Http/Requests/Intake/StoreParticipantDisclosureAuthorizationRequest.php similarity index 98% rename from app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php rename to app/Http/Requests/Intake/StoreParticipantDisclosureAuthorizationRequest.php index a01302fc..8cad7ae2 100644 --- a/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationStoreRequest.php +++ b/app/Http/Requests/Intake/StoreParticipantDisclosureAuthorizationRequest.php @@ -4,7 +4,7 @@ use Illuminate\Foundation\Http\FormRequest; -class ParticipantDisclosureAuthorizationStoreRequest extends FormRequest +class StoreParticipantDisclosureAuthorizationRequest extends FormRequest { /** * Determine if the user is authorized to make this request. diff --git a/app/Http/Requests/Intake/ParticipantSignupStoreRequest.php b/app/Http/Requests/Intake/StoreParticipantSignupRequest.php similarity index 97% rename from app/Http/Requests/Intake/ParticipantSignupStoreRequest.php rename to app/Http/Requests/Intake/StoreParticipantSignupRequest.php index b89c0d94..82035df0 100644 --- a/app/Http/Requests/Intake/ParticipantSignupStoreRequest.php +++ b/app/Http/Requests/Intake/StoreParticipantSignupRequest.php @@ -8,7 +8,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; -class ParticipantSignupStoreRequest extends FormRequest +class StoreParticipantSignupRequest extends FormRequest { public function authorize(): bool { diff --git a/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php b/app/Http/Requests/Intake/UpdateParticipantDisclosureAuthorizationRequest.php similarity index 98% rename from app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php rename to app/Http/Requests/Intake/UpdateParticipantDisclosureAuthorizationRequest.php index 8d2633b2..d632f729 100644 --- a/app/Http/Requests/Intake/ParticipantDisclosureAuthorizationUpdateRequest.php +++ b/app/Http/Requests/Intake/UpdateParticipantDisclosureAuthorizationRequest.php @@ -4,7 +4,7 @@ use Illuminate\Foundation\Http\FormRequest; -class ParticipantDisclosureAuthorizationUpdateRequest extends FormRequest +class UpdateParticipantDisclosureAuthorizationRequest extends FormRequest { /** * Determine if the user is authorized to make this request. From b2ea3608e5b1b0ec3139ba56bfe30aca034bcc36 Mon Sep 17 00:00:00 2001 From: Kevin DeBrecht <99675851+kd-at-mi@users.noreply.github.com> Date: Tue, 25 Mar 2025 20:00:46 -0500 Subject: [PATCH 19/40] fix: change method to put and add id to the route --- .../Components/Intake/FatherhoodAssessmentForm.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx b/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx index e2377ad9..dd64c8eb 100644 --- a/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx +++ b/resources/js/Components/Intake/FatherhoodAssessmentForm.tsx @@ -134,11 +134,16 @@ const FatherhoodAssessmentForm: React.FC = ({ } if (fatherhoodAssessmentForm?.id) { - form.post(route('intake.fatherhood-assessment.update'), { - onSuccess: () => { - continueToNextStep() + form.put( + route('intake.fatherhood-assessment.update', [ + fatherhoodAssessmentForm.id, + ]), + { + onSuccess: () => { + continueToNextStep() + }, }, - }) + ) } else { form.post(route('intake.fatherhood-assessment.store'), { onSuccess: () => { From 392971261d6036edd6474d7a310b8264c0da63e7 Mon Sep 17 00:00:00 2001 From: Daniel Rustad Date: Mon, 21 Apr 2025 00:39:03 -0500 Subject: [PATCH 20/40] Added disclosure release form and service plan form to Kevin's page flow. It is now possible to perform a complete signup and intake. --- .../ParticipantDisclosureController.php | 2 +- ...ticipantFatherhoodAssessmentController.php | 2 +- .../Intake/ParticipantSignupController.php | 1 + .../ParticipantDisclosureAuthorization.php | 4 + resources/js/Components/ChildrenTable.tsx | 2 +- .../Intake/DisclosureAuthorizationForm.tsx | 684 ++++++++++++++++ .../js/Components/Intake/MediaReleaseForm.tsx | 2 +- .../js/Components/Intake/ServicePlanForm.tsx | 745 ++++++++++++++++++ resources/js/Pages/Auth/Login.tsx | 8 +- .../js/Pages/Intake/Disclosure/Create.tsx | 20 +- .../js/Pages/Intake/Disclosure/Index.tsx | 115 ++- .../Intake/FatherhoodAssessment/Create.tsx | 2 +- .../Intake/FatherhoodAssessment/Index.tsx | 17 +- .../js/Pages/Intake/ParticipantRegister.tsx | 12 +- .../js/Pages/Intake/ServicePlan/Create.tsx | 18 +- .../js/Pages/Intake/ServicePlan/Index.tsx | 113 ++- .../js/Pages/Intake/ServicePlan/types.ts | 37 + resources/js/Pages/Intake/Signup.tsx | 6 +- resources/js/types/index.d.ts | 1 + .../intake-disclosure-authorization-form.ts | 82 ++ resources/js/types/intake-serviceplan-form.ts | 31 + routes/auth.php | 2 +- 22 files changed, 1848 insertions(+), 58 deletions(-) create mode 100644 resources/js/Components/Intake/DisclosureAuthorizationForm.tsx create mode 100644 resources/js/Components/Intake/ServicePlanForm.tsx create mode 100644 resources/js/Pages/Intake/ServicePlan/types.ts create mode 100644 resources/js/types/intake-disclosure-authorization-form.ts create mode 100644 resources/js/types/intake-serviceplan-form.ts diff --git a/app/Http/Controllers/Intake/ParticipantDisclosureController.php b/app/Http/Controllers/Intake/ParticipantDisclosureController.php index a3debfa7..b355a4f1 100644 --- a/app/Http/Controllers/Intake/ParticipantDisclosureController.php +++ b/app/Http/Controllers/Intake/ParticipantDisclosureController.php @@ -21,7 +21,7 @@ public function index(Request $request): Response return Inertia::render('Intake/Disclosure/Index',[ 'participant' => ParticipantResource::make($participant), - 'disclosureAuthorizations' => $participant?->disclosureAuthorizations?->toArray(), + 'disclosureAuthorizations' => $participant?->disclosureAuthorizations?->toArray() ?? [], ]); } diff --git a/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php b/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php index 965c48cd..c9fad643 100644 --- a/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php +++ b/app/Http/Controllers/Intake/ParticipantFatherhoodAssessmentController.php @@ -22,7 +22,7 @@ public function index(Request $request) return Inertia::render('Intake/FatherhoodAssessment/Index',[ 'participant' => ParticipantResource::make($participant), - 'fatherhoodAssessments' => $participant?->fatherhoodAssessments?->toArray(), + 'fatherhoodAssessments' => $participant?->fatherhoodAssessments?->toArray() ?? [], ]); } diff --git a/app/Http/Controllers/Intake/ParticipantSignupController.php b/app/Http/Controllers/Intake/ParticipantSignupController.php index 686d4734..6acfd03c 100644 --- a/app/Http/Controllers/Intake/ParticipantSignupController.php +++ b/app/Http/Controllers/Intake/ParticipantSignupController.php @@ -28,6 +28,7 @@ public function create(): Response { return Inertia::render('Intake/Signup',[ + 'user' => Auth::user(), 'ethnicity' => Ethnicity::displayArray(), 'maritalStatus' => MaritalStatus::displayArray(), 'regions' => Region::get(['id', 'description'])->toArray(), diff --git a/app/Models/ParticipantDisclosureAuthorization.php b/app/Models/ParticipantDisclosureAuthorization.php index 21b1355e..4c4e3d47 100644 --- a/app/Models/ParticipantDisclosureAuthorization.php +++ b/app/Models/ParticipantDisclosureAuthorization.php @@ -50,6 +50,8 @@ class ParticipantDisclosureAuthorization extends Model 'disclose_to_legislator', 'legislator_name', 'disclose_to_governors_staff', + 'governors_staff_details', + 'disclose_to_other_recipient', 'other_recipient_details', 'purpose_eligibility_determination', 'purpose_legal_consultation', @@ -61,6 +63,7 @@ class ParticipantDisclosureAuthorization extends Model 'purpose_background_investigation', 'purpose_consumer_request', 'purpose_share_and_refer', + 'share_and_refer_details', 'purpose_other', 'other_purpose_details', 'disclose_entire_file', @@ -112,6 +115,7 @@ class ParticipantDisclosureAuthorization extends Model 'disclose_to_employer' => 'boolean', 'disclose_to_legislator' => 'boolean', 'disclose_to_governors_staff' => 'boolean', + 'disclose_to_other_recipient' => 'boolean', 'purpose_eligibility_determination' => 'boolean', 'purpose_legal_consultation' => 'boolean', 'purpose_legal_proceedings' => 'boolean', diff --git a/resources/js/Components/ChildrenTable.tsx b/resources/js/Components/ChildrenTable.tsx index 4b25dd2c..4ae5c129 100644 --- a/resources/js/Components/ChildrenTable.tsx +++ b/resources/js/Components/ChildrenTable.tsx @@ -5,7 +5,7 @@ import { Button, Checkbox, Input, InputError, Label } from '@/Components/ui' export interface ChildrenTableProps { childrenInfo: Child[] setChildrenInfo: (childrenInfo: Child[]) => void - errors: Record + errors: Record } const ChildrenTable = React.forwardRef( diff --git a/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx b/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx new file mode 100644 index 00000000..515b8ded --- /dev/null +++ b/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx @@ -0,0 +1,684 @@ +import React from "react"; +import { router, useForm } from "@inertiajs/react"; +import { + Button, + Input, + Checkbox, + Label, + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/Components/ui"; +import { IntakeDisclosureAuthorizationForm } from "@/types/intake-disclosure-authorization-form"; +import type { Participant } from '@/types/participant' + +interface DisclosureAuthorizationFormProps { + participant: Participant + disclosureAuthorizationForm?: IntakeDisclosureAuthorizationForm + viewOnly?: boolean + nextRoute?: string +} + +interface DisclosureAuthorizationFormDefinition extends Record { + consumer_name: string, + is_dss_authorized: boolean, + is_fsd_authorized: boolean, + is_dys_authorized: boolean, + is_cd_authorized: boolean, + is_mhd_authorized: boolean, + is_dls_authorized: boolean, + is_dfas_authorized: boolean, + is_mmac_authorized: boolean, + is_other_authorized: boolean, + other_authorized_entity: string, + + subject_name: string, + subject_phone: string, + subject_dob: string, + subject_ssn: string, + subject_address: string, + subject_email: string, + + disclose_to_attorney: boolean, + attorney_name: string, + disclose_to_employer: boolean, + employer_name: string, + disclose_to_legislator: boolean, + legislator_name: string, + disclose_to_governors_staff: boolean, + governors_staff_details: string, + disclose_to_other_recipient: boolean, + other_recipient_name: string, + other_recipient_address: string, + + purpose_eligibility_determination: boolean, + purpose_legal_consultation: boolean, + purpose_legal_proceedings: boolean, + purpose_employment: boolean, + purpose_complaint_investigation: boolean, + purpose_treatment_planning: boolean, + purpose_continuity_of_services: boolean, + purpose_background_investigation: boolean, + purpose_consumer_request: boolean, + purpose_share_and_refer: boolean, + share_and_refer_details: string, + purpose_other: boolean, + other_purpose_details: string, + + disclose_entire_file: boolean, + disclose_licensure_information: boolean, + disclose_medical_psychiatric_records: boolean, + disclose_hotline_investigations: boolean, + disclose_home_studies: boolean, + disclose_eligibility_determinations: boolean, + disclose_substance_abuse_treatment: boolean, + disclose_client_employment_records: boolean, + disclose_benefits_received: boolean, + disclose_other_information: boolean, + other_disclosure_details: string, + + + accept_text_messages: boolean, + consumer_signature: string, + signature_date: string, + witness_signature: string, + witness_signature_date: string, + guardian_signature: string, + guardian_authority: string, + + survey_by_email: boolean, + survey_by_mail: boolean, + survey_by_online: boolean, +} + +export const DisclosureAuthorizationForm: React.FC = ({ + participant, + disclosureAuthorizationForm, + viewOnly = false, + nextRoute = 'intake.disclosure.store' +}) => { + const { data, setData, processing, errors, ...form } = + useForm({ + consumer_name: disclosureAuthorizationForm?.consumer_name ?? participant?.name ?? '', + is_dss_authorized: disclosureAuthorizationForm?.is_dss_authorized ?? true, + is_fsd_authorized: disclosureAuthorizationForm?.is_fsd_authorized ?? true, + is_dys_authorized: disclosureAuthorizationForm?.is_dys_authorized ?? false, + is_cd_authorized: disclosureAuthorizationForm?.is_cd_authorized ?? false, + is_mhd_authorized: disclosureAuthorizationForm?.is_mhd_authorized ?? false, + is_dls_authorized: disclosureAuthorizationForm?.is_dls_authorized ?? false, + is_dfas_authorized: disclosureAuthorizationForm?.is_dfas_authorized ?? false, + is_mmac_authorized: disclosureAuthorizationForm?.is_mmac_authorized ?? false, + is_other_authorized: disclosureAuthorizationForm?.is_other_authorized ?? false, + other_authorized_entity: disclosureAuthorizationForm?.other_authorized_entity ?? '', + + subject_name: disclosureAuthorizationForm?.subject_name ?? participant.name ?? '', + subject_phone: disclosureAuthorizationForm?.subject_phone ?? participant.home_phone_number ?? '', + subject_dob: disclosureAuthorizationForm?.subject_dob ?? '', + subject_ssn: disclosureAuthorizationForm?.subject_ssn ?? '', + subject_address: disclosureAuthorizationForm?.subject_address ?? participant.address_line_1 ?? '', + subject_email: disclosureAuthorizationForm?.subject_email ?? participant.user.email ?? '', + + disclose_to_attorney: disclosureAuthorizationForm?.disclose_to_attorney ?? false, + attorney_name: disclosureAuthorizationForm?.attorney_name ?? '', + disclose_to_employer: disclosureAuthorizationForm?.disclose_to_employer ?? false, + employer_name: disclosureAuthorizationForm?.employer_name ?? '', + disclose_to_legislator: disclosureAuthorizationForm?.disclose_to_legislator ?? false, + legislator_name: disclosureAuthorizationForm?.legislator_name ?? '', + disclose_to_governors_staff: disclosureAuthorizationForm?.disclose_to_governors_staff ?? false, + governors_staff_details: disclosureAuthorizationForm?.governors_staff_details ?? '', + disclose_to_other_recipient: disclosureAuthorizationForm?.disclose_to_other_recipient ?? true, + other_recipient_name: disclosureAuthorizationForm?.other_recipient_name ?? 'Good Dads/Jennifer Baker (and staff)', + other_recipient_address: disclosureAuthorizationForm?.other_recipient_address ?? '205 W. Walnut Street, Ste. 10, Springfield, MO 65806', + + purpose_eligibility_determination: disclosureAuthorizationForm?.purpose_eligibility_determination ?? false, + purpose_legal_consultation: disclosureAuthorizationForm?.purpose_legal_consultation ?? false, + purpose_legal_proceedings: disclosureAuthorizationForm?.purpose_legal_proceedings ?? false, + purpose_employment: disclosureAuthorizationForm?.purpose_employment ?? false, + purpose_complaint_investigation: disclosureAuthorizationForm?.purpose_complaint_investigation ?? false, + purpose_treatment_planning: disclosureAuthorizationForm?.purpose_treatment_planning ?? false, + purpose_continuity_of_services: disclosureAuthorizationForm?.purpose_continuity_of_services ?? false, + purpose_background_investigation: disclosureAuthorizationForm?.purpose_background_investigation ?? false, + purpose_consumer_request: disclosureAuthorizationForm?.purpose_consumer_request ?? false, + purpose_share_and_refer: disclosureAuthorizationForm?.purpose_share_and_refer ?? true, + share_and_refer_details: disclosureAuthorizationForm?.share_and_refer_details ?? 'Good Dads', + purpose_other: disclosureAuthorizationForm?.purpose_other ?? false, + other_purpose_details: disclosureAuthorizationForm?.other_purpose_details ?? '', + + disclose_entire_file: disclosureAuthorizationForm?.disclose_entire_file ?? true, + disclose_licensure_information: disclosureAuthorizationForm?.disclose_licensure_information ?? false, + disclose_medical_psychiatric_records: disclosureAuthorizationForm?.disclose_medical_psychiatric_records ?? false, + disclose_hotline_investigations: disclosureAuthorizationForm?.disclose_hotline_investigations ?? false, + disclose_home_studies: disclosureAuthorizationForm?.disclose_home_studies ?? false, + disclose_eligibility_determinations: disclosureAuthorizationForm?.disclose_eligibility_determinations ?? false, + disclose_substance_abuse_treatment: disclosureAuthorizationForm?.disclose_substance_abuse_treatment ?? false, + disclose_client_employment_records: disclosureAuthorizationForm?.disclose_client_employment_records ?? false, + disclose_benefits_received: disclosureAuthorizationForm?.disclose_benefits_received ?? true, + disclose_other_information: disclosureAuthorizationForm?.disclose_other_information ?? true, + other_disclosure_details: + disclosureAuthorizationForm?.other_disclosure_details ?? 'Child support records that FSD may release to the parent from his/her own case file.', + + accept_text_messages: disclosureAuthorizationForm?.accept_text_messages ?? false, + consumer_signature: disclosureAuthorizationForm?.consumer_signature ?? '', + signature_date: disclosureAuthorizationForm?.signature_date ?? '', + witness_signature: disclosureAuthorizationForm?.witness_signature ?? '', + witness_signature_date: disclosureAuthorizationForm?.witness_signature_date ?? '', + guardian_signature: disclosureAuthorizationForm?.guardian_signature ?? '', + guardian_authority: disclosureAuthorizationForm?.guardian_authority ?? '', + + survey_by_email: disclosureAuthorizationForm?.survey_by_email ?? false, + survey_by_mail: disclosureAuthorizationForm?.survey_by_mail ?? false, + survey_by_online: disclosureAuthorizationForm?.survey_by_online ?? false, + }); + + const continueToNextStep = () => { + router.visit(route(nextRoute)) + } + + const renderCheckbox = (id: string, label: string, is_checked: boolean, is_view_only: boolean) => ( +
+ setData(id, !!checked)} + disabled={is_view_only} + /> + +
+ ); + + const renderConditionalField = (id: string, checkbox_id: string, label: string, content: string, is_view_only: boolean, required = false) => ( +
+ + setData(id, e.target.value)} + disabled={is_view_only} + className="w-full" + /> + {errors[id] &&

{errors[id]}

} +
+ ); + + return ( +
+ + + AUTHORIZATION FOR DISCLOSURE OF CONFIDENTIAL INFORMATION + + +
{ + e.preventDefault() + if (viewOnly) { + continueToNextStep() + return + } + + if (disclosureAuthorizationForm?.id) { + form.put(route('intake.disclosure.update'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } else { + form.post(route('intake.disclosure.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + > + {/* Consumer Information */} +
+
+ + setData('participant_name', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + placeholder="NAME OF CLIENT, PARENT, GUARDIAN/LEGAL REPRESENTATIVE" + /> + {errors.consumer_name &&

{errors.consumer_name}

} +

authorize and request

+
+ + {/* Authorized Entities */} +
+

Check all that apply:

+
+
+ {renderCheckbox('is_dss_authorized', 'Department of Social Services (DSS)', data.is_dss_authorized, (viewOnly || processing))} + {renderCheckbox('is_dys_authorized', 'Division of Youth Services (DYS)', data.is_dys_authorized, (viewOnly || processing))} + {renderCheckbox('is_mhd_authorized', 'MO HealthNet Division (MHD)', data.is_mhd_authorized, (viewOnly || processing))} + {renderCheckbox('is_dfas_authorized', 'Division of Finance & Administrative Services (DFAS)', data.is_dfas_authorized, (viewOnly || processing))} +
+
+ {renderCheckbox('is_fsd_authorized', 'Family Support Division (FSD)', data.is_fsd_authorized, (viewOnly || processing))} + {renderCheckbox('is_cd_authorized', 'Children\'s Division (CD)', data.is_cd_authorized, (viewOnly || processing))} + {renderCheckbox('is_dls_authorized', 'Division of Legal Services (DLS)', data.is_dls_authorized, (viewOnly || processing))} + {renderCheckbox('is_mmac_authorized', 'Missouri Medicaid Audit and Compliance (MMAC)', data.is_mmac_authorized, (viewOnly || processing))} +
+
+ +
+ {renderCheckbox('is_other_authorized', 'Other', data.is_other_authorized, (viewOnly || processing))} + {renderConditionalField('other_authorized_entity', 'is_other_authorized', 'Name of facility, agency, mental health center, person', data.other_authorized_entity, (viewOnly || processing), true)} +
+
+
+ + {/* Subject Information */} +
+

to disclose/release the below specified information of:

+
+
+ + setData('subject_name', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.subject_name &&

{errors.subject_name}

} +
+ +
+ + setData('subject_phone', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.subject_phone &&

{errors.subject_phone}

} +
+ +
+ + setData('subject_email', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.subject_email &&

{errors.subject_email}

} +
+ +
+ + + setData('subject_dob', e.target.value) + } + /> + {errors.subject_dob &&

{errors.subject_dob}

} +
+ +
+ + setData('subject_ssn', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> +
+
+
+ + {/* Recipients */} +
+

to (check all that apply)

+
+
+ {renderCheckbox('disclose_to_attorney', 'Attorney', data.disclose_to_attorney, (viewOnly || processing))} + {renderConditionalField('attorney_name', 'disclose_to_attorney', 'Name', data.attorney_name, (viewOnly || processing))} + + {renderCheckbox('disclose_to_employer', 'Employer', data.disclose_to_employer, (viewOnly || processing))} + {renderConditionalField('employer_name', 'disclose_to_employer', 'Name', data.employer_name, (viewOnly || processing))} +
+ +
+ {renderCheckbox('disclose_to_legislator', 'Legislator/staff', data.disclose_to_legislator, (viewOnly || processing))} + {renderConditionalField('legislator_name', 'disclose_to_legislator', 'Name', data.legislator_name, (viewOnly || processing))} + + {renderCheckbox('disclose_to_governors_staff', 'Governor\'s staff', data.disclose_to_governors_staff, (viewOnly || processing))} + {renderConditionalField('governors_staff_details', 'disclose_to_governors_staff', 'Name', data.governors_staff_details, (viewOnly || processing))} +
+
+ +
+ {renderCheckbox('disclose_to_other_recipient', 'Other', data.disclose_to_other_recipient, (viewOnly || processing))} + {data.disclose_to_other_recipient && ( +
+
+ + setData('other_recipient_details', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> +
+ +
+ + setData('other_recipient_address', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> +
+
+ )} +
+
+ + {/* Purpose of Disclosure */} +
+

The purpose of this disclosure is (check all that apply)

+
+
+ {renderCheckbox('purpose_eligibility_determination', 'Eligibility determination', data.purpose_eligibility_determination, (viewOnly || processing))} + {renderCheckbox('purpose_employment', 'Employment', data.purpose_employment, (viewOnly || processing))} + {renderCheckbox('purpose_continuity_of_services', 'Continuity of services/care', data.purpose_continuity_of_services, (viewOnly || processing))} +
+ +
+ {renderCheckbox('purpose_legal_consultation', 'Legal consultation/representation', data.purpose_legal_consultation, (viewOnly || processing))} + {renderCheckbox('purpose_complaint_investigation', 'Complaint/investigation/resolution', data.purpose_complaint_investigation, (viewOnly || processing))} + {renderCheckbox('purpose_background_investigation', 'Background investigation', data.purpose_background_investigation, (viewOnly || processing))} +
+ +
+ {renderCheckbox('purpose_legal_proceedings', 'Legal proceedings', data.purpose_legal_consultation, (viewOnly || processing))} + {renderCheckbox('purpose_treatment_planning', 'Treatment planning', data.purpose_treatment_planning, (viewOnly || processing))} + {renderCheckbox('purpose_consumer_request', 'At consumer\'s request', data.purpose_consumer_request, (viewOnly || processing))} +
+
+ +
+ {renderCheckbox( + 'purpose_share_and_refer', + 'To share or refer my information to other Missouri state agencies (such as DMH, DHSS, DSS, DESE, etc.) to obtain services consistent with the', + data.purpose_share_and_refer, + (viewOnly || processing) + )} + {data.purpose_share_and_refer && ( +
+ + setData('share_and_refer_details', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + placeholder="program (please complete the name of the program in which you want to participate)" + /> +
+ )} +
+ +
+ {renderCheckbox('purpose_other', 'Other (specify)', data.purpose_other, (viewOnly || processing))} + {renderConditionalField('other_purpose_details', 'purpose_other', 'Details', data.other_purpose_details, (viewOnly || processing))} +
+
+ + {/* Information to be Disclosed */} +
+

The specific information to be disclosed is (check all that apply)

+
+
+ {renderCheckbox('disclose_entire_file', 'Entire file', data.disclose_entire_file, (viewOnly || processing))} + {renderCheckbox('disclose_licensure_information', 'Licensure information', data.disclose_licensure_information, (viewOnly || processing))} + {renderCheckbox('disclose_medical_psychiatric_records', 'Medical/psychiatric evaluation/treatment records', data.disclose_medical_psychiatric_records, (viewOnly || processing))} +
+ +
+ {renderCheckbox('disclose_hotline_investigations', 'Hotline investigations', data.disclose_hotline_investigations, (viewOnly || processing))} + {renderCheckbox('disclose_home_studies', 'Home studies', data.disclose_home_studies, (viewOnly || processing))} + {renderCheckbox('disclose_client_employment_records', 'Client employment records', data.disclose_client_employment_records, (viewOnly || processing))} +
+ +
+ {renderCheckbox('disclose_eligibility_determinations', 'Eligibility determinations', data.disclose_eligibility_determinations, (viewOnly || processing))} + {renderCheckbox('disclose_substance_abuse_treatment', 'Substance abuse treatment', data.disclose_substance_abuse_treatment, (viewOnly || processing))} + {renderCheckbox('disclose_benefits_received', 'Benefits received', data.disclose_benefits_received, (viewOnly || processing))} +
+
+ +
+ {renderCheckbox('disclose_other_information', 'Other', data.disclose_other_information, (viewOnly || processing))} + {renderConditionalField('other_disclosure_details', 'disclose_other_information', 'Details', data.other_disclosure_details, (viewOnly || processing))} +
+
+ + {/* Terms and Authorization */} +
+
+
    +
  1. READ CAREFULLY: I understand that my information and records with the Department of Social Services are confidential by law. I understand that by + signing this authorization, I am allowing the release of any and all of my information and records which I am authorized to receive as specified on this + document whether past, present or created in the future up to the expiration or revocation date of this authorization, unless otherwise authorized. The + protected information in my records may include medical treatment and/or evaluation information, mental/behavioral health information, information relating + to sexually transmitted diseases, acquired immunodeficiency syndrome (AIDS), human immunodeficiency virus (HIV), other communicable or + environmental diseases and conditions, alcohol/drug abuse, application for and/or receipt of public assistance benefits, alcohol/drug abuse information, + and/or information concerning child abuse and neglect.
  2. +
  3. This authorization includes both information presently compiled and information to be compiled during your association or dealings with the Department + of Social Services, during the specified time frame.
  4. +
  5. Unless otherwise indicated, this authorization becomes effective on the date of signature below and will expire one year from that date.
  6. +
  7. I understand that I have a right to revoke this authorization at any time. I understand that if I revoke this authorization I must do so IN WRITING and present + my written revocation to the Privacy Officer of the Department of Social Services at P.O. Box 1527, MO 65102. I further + understand that actions already taken based on this authorization, prior to revocation, will NOT be affected.
  8. +
  9. I understand that I have the right to receive a copy of this authorization upon request. A photographic copy of this authorization is as valid as the + original.
  10. +
  11. I understand that authorizing the disclosure of this information is voluntary. I can refuse to sign this authorization. I need not sign this form in order to receive + services from the Department of Social Services. I understand that I may request to inspect or request a copy of information to be used or disclosed, as + provided in 45 CFR section 164.524. I understand that any disclosure of information carries with it the potential for redisclosure by the party + receiving it and that the information may no longer be protected by law once it is in the possession of the receiving party. If I have questions about + disclosure of my information, I can contact the Privacy Officer of the Department of Social Services, my caseworker or family support eligibility specialist.
  12. +
  13. By signing this disclosure on paper or electronically, I am giving the Family Support Division (FSD) permission to deliver, or cause to be delivered, + phone calls or text messages to me regarding my case from an automated dialing system at my primary number. The FSD does not use an encryption system when + sending text messages. Such unencrypted systems are not secure and carry some level of risk that text messages could be read by a third party. By signing, + I am affirming that I nevertheless prefer to receive text messages from FSD and understand I do not have to consent to this as part of my application and + can opt out of getting these calls or text messages by checking “No” in the “Accept Text Messages” box below
  14. +
+ +

My signature below acknowledges that I have read and understood the text above, and authorize the release of my confidential information.

+
+
+ + {/* Signature Section */} +
+
+
+ + setData('consumer_signature', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.consumer_signature &&

{errors.consumer_signature}

} +
+ +
+ + + setData('signature_date', e.target.value) + } + /> + {errors.signature_date &&

{errors.signature_date}

} +
+
+ +
+
+ + setData('witness_signature', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.consumer_signature &&

{errors.witness_signature}

} +
+ +
+ + + setData('witness_signature_date', e.target.value) + } + /> + {errors.witness_signature_date &&

{errors.witness_signature_date}

} +
+
+ +
+
+
+ + setData('guardian_signature', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> +
+ +
+ + +
+ setData("accept_text_messages", Boolean(checked))} + disabled={viewOnly || processing} + /> + +
+ +
+ setData("accept_text_messages", Boolean(!checked))} + disabled={viewOnly || processing} + /> + +
+
+ +
+ +
+
+
+
+ +
+

Surveys

+

+ Family Support Division would like to know what services enrolled participants are seeking from our programs. In an effort to capture this data + Family Support Division is administering a survey through Survey Monkey. Plase selet the preferred mothod of survey delivery: +

+
+
+ {renderCheckbox('survey_by_email', 'Email', data.survey_by_email, (viewOnly || processing))} +
+
+
+
+ {renderCheckbox('survey_by_mail', 'Address', data.survey_by_mail, (viewOnly || processing))} +
+
+
+
+ {renderCheckbox('survey_by_online', 'Online', data.survey_by_online, (viewOnly || processing))} +
+
+
+ +
+ +
+
+
+
+
+ ); +}; + +export default DisclosureAuthorizationForm \ No newline at end of file diff --git a/resources/js/Components/Intake/MediaReleaseForm.tsx b/resources/js/Components/Intake/MediaReleaseForm.tsx index 3871a56f..618b78db 100644 --- a/resources/js/Components/Intake/MediaReleaseForm.tsx +++ b/resources/js/Components/Intake/MediaReleaseForm.tsx @@ -33,7 +33,7 @@ export const MediaReleaseForm: React.FC = ({ signature_date: mediaReleaseForm?.signature_date ?? dayjs().format('MM/DD/YYYY') ?? '', phone_number: - mediaReleaseForm?.phone_number ?? participant?.cell_phone_number ?? '', + mediaReleaseForm?.phone_number ?? participant?.home_phone_number ?? '', email: mediaReleaseForm?.email ?? participant?.user.email ?? '', }) diff --git a/resources/js/Components/Intake/ServicePlanForm.tsx b/resources/js/Components/Intake/ServicePlanForm.tsx new file mode 100644 index 00000000..1430cbf5 --- /dev/null +++ b/resources/js/Components/Intake/ServicePlanForm.tsx @@ -0,0 +1,745 @@ +import React from 'react'; +import { router, useForm } from '@inertiajs/react' + +import type { IntakeServicePlanForm } from '@/types/intake-serviceplan-form' +import type { Participant } from '@/types/participant' + +import { + Button, + Input, + Checkbox, + Label, +} from "@/Components/ui"; + + +interface ServicePlanFromProps { + participant: Participant + serviceplanForm?: IntakeServicePlanForm + viewOnly?: boolean + nextRoute?: string +} + +// Define types for form data +interface ServicePlanFormDefinition extends Record { + participant_name: string, + client_number: string, + review_date: string, + parenting_skill_development_is_service_area: boolean, + effective_co_parenting_is_service_area: boolean, + employment_and_education_is_service_area: boolean, + child_support_is_service_area: boolean, + domestic_violence_is_service_area: boolean, + service_identified_by_participant: string, + goal: string, + custody_visitation_strategy: string, + custody_visitation_person_responsible: string, + custody_visitation_timeline: string, + custody_visitation_measure_of_success: string, + education_employment_strategy: string, + education_employment_person_responsible: string, + education_employment_timeline: string, + education_employment_measure_of_success: string, + housing_transportation_strategy: string, + housing_transportation_person_responsible: string, + housing_transportation_timeline: string, + housing_transportation_measure_of_success: string, + participant_signature: string, + participant_signature_date: string, + case_manager_signature: string, + case_manager_signature_date: string, + date_completed: string, + +} + +export const ServicePlanForm: React.FC = ({ + participant, + serviceplanForm, + viewOnly = false, + nextRoute = 'intake.media-release.index' +}) => { + const { data, setData, processing, errors, ...form } = useForm({ + participant_name: serviceplanForm?.participant_name ?? participant?.name ?? '', + client_number: serviceplanForm?.client_number ?? '', + review_date: serviceplanForm?.review_date ?? '', + + parenting_skill_development_is_service_area: serviceplanForm?.parenting_skill_development_is_service_area ?? true, + effective_co_parenting_is_service_area: serviceplanForm?.effective_co_parenting_is_service_area ?? true, + employment_and_education_is_service_area: serviceplanForm?.employment_and_education_is_service_area ?? true, + child_support_is_service_area: serviceplanForm?.employment_and_education_is_service_area ?? true, + domestic_violence_is_service_area: serviceplanForm?.domestic_violence_is_service_area ?? false, + + service_identified_by_participant: serviceplanForm?.service_identified_by_participant ?? '', + goal: serviceplanForm?.goal ?? '', + + custody_visitation_strategy: serviceplanForm?.custody_visitation_strategy ?? '', + custody_visitation_person_responsible: serviceplanForm?.custody_visitation_person_responsible ?? '', + custody_visitation_timeline: serviceplanForm?.custody_visitation_timeline ?? '', + custody_visitation_measure_of_success: serviceplanForm?.custody_visitation_measure_of_success ?? '', + education_employment_strategy: serviceplanForm?.education_employment_strategy ?? '', + education_employment_person_responsible: serviceplanForm?.education_employment_person_responsible ?? '', + education_employment_timeline: serviceplanForm?.education_employment_timeline ?? '', + education_employment_measure_of_success: serviceplanForm?.education_employment_measure_of_success ?? '', + housing_transportation_strategy: serviceplanForm?.housing_transportation_strategy ?? '', + housing_transportation_person_responsible: serviceplanForm?.housing_transportation_person_responsible ?? '', + housing_transportation_timeline: serviceplanForm?.housing_transportation_timeline ?? '', + housing_transportation_measure_of_success: serviceplanForm?.housing_transportation_measure_of_success ?? '', + participant_signature: serviceplanForm?.participant_signature ?? '', + participant_signature_date: serviceplanForm?.participant_signature_date ?? '', + case_manager_signature: serviceplanForm?.case_manager_signature ?? '', + case_manager_signature_date: serviceplanForm?.case_manager_signature_date ?? '', + date_completed: serviceplanForm?.date_completed ?? '', + }); + + const continueToNextStep = () => { + console.log("ServicePlanForm.continueToNextStep - nextRoute: " + nextRoute) + router.visit(route(nextRoute)) + } + + return ( +
+
{ + e.preventDefault() + console.log("ServicePlanForm.onSubmit") + if (viewOnly) { + continueToNextStep() + return + } + + if (serviceplanForm?.id) { + form.put( + route('intake.service-plan.update', [ + serviceplanForm.id, + ]), + { + onSuccess: () => { + continueToNextStep() + }, + }, + ) + } else { + form.post(route('intake.service-plan.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + className="bg-white rounded-lg shadow-md p-6" + > +

+ Individualized Service Plan +

+ {/* Header Section */} +
+
+ + setData('participant_name', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.participant_name &&
{errors.participant_name}
} +
+ +
+
+ + setData('client_number', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.client_number &&
{errors.client_number}
} +
+ +
+ + setData('review_date', e.target.value)} + disabled={viewOnly || processing} + /> +
+
+
+ + {/* Service Areas Section */} +
+

Service Areas:

+
+
+ setData('parenting_skill_development_is_service_area', !!checked)} + disabled={viewOnly || processing} + /> + +
+ +
+ setData('effective_co_parenting_is_service_area', !!checked)} + disabled={viewOnly || processing} + /> + +
+ +
+ setData('employment_and_education_is_service_area', !!checked)} + disabled={viewOnly || processing} + /> + +
+ +
+ setData('child_support_is_service_area', !!checked)} + disabled={viewOnly || processing} + /> + +
+ +
+ setData('domestic_violence_is_service_area', !!checked)} + disabled={viewOnly || processing} + /> + +
+
+
+ + {/* Service Identified & Goal Section */} +
+
+ + setData('service_identified_by_participant', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.service_identified_by_participant &&
{errors.service_identified_by_participant}
} +
+ +
+ + setData('goal', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.goal &&
{errors.goal}
} +
+
+ + {/* Objectives Tables */} +
+

Objectives

+ + {/* Parenting Skills & Stress Management Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ObjectivesStrategies to Achieve ObjectivePerson ResponsibleTimelinesMeasure of Success
Parenting Skills Development + updateObjective('parenting_skills', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('parenting_skills', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('parenting_skills', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('parenting_skills', 'measure_of_success', e.target.value)} + disabled={true} + /> +
Managing Stress and Anger + updateObjective('stress_and_anger', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('stress_and_anger', 'person_responsible', e.target.value)} + disabled={true} + + /> + + updateObjective('stress_and_anger', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('stress_and_anger', 'measure_of_success', e.target.value)} + disabled={true} + /> +
+
+ + {/* Custody/Education/Housing Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ObjectivesStrategies to Achieve ObjectivePerson ResponsibleTimelinesMeasure of Success
Custody/Visitation* + setData('custody_visitation_strategy', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.custody_visitation_strategy &&
{errors.custody_visitation_strategy}
} +
+ setData('custody_visitation_person_responsible', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.custody_visitation_person_responsible &&
{errors.custody_visitation_person_responsible}
} +
+ setData('custody_visitation_timeline', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.custody_visitation_timeline &&
{errors.custody_visitation_timeline}
} +
+ setData('custody_visitation_measure_of_success', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.custody_visitation_measure_of_success &&
{errors.custody_visitation_measure_of_success}
} +
Education and or Employment* + setData('education_employment_strategy', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.education_employment_strategy &&
{errors.education_employment_strategy}
} +
+ setData('education_employment_person_responsible', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.education_employment_person_responsible &&
{errors.education_employment_person_responsible}
} +
+ setData('education_employment_timeline', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.education_employment_timeline &&
{errors.education_employment_timeline}
} +
+ setData('education_employment_measure_of_success', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.education_employment_measure_of_success &&
{errors.education_employment_measure_of_success}
} +
Housing / Transportation* + setData('housing_transportation_strategy', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.housing_transportation_strategy &&
{errors.housing_transportation_strategy}
} +
+ setData('housing_transportation_person_responsible', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.housing_transportation_person_responsible &&
{errors.housing_transportation_person_responsible}
} +
+ setData('housing_transportation_timeline', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.housing_transportation_timeline &&
{errors.housing_transportation_timeline}
} +
+ setData('housing_transportation_measure_of_success', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.housing_transportation_measure_of_success &&
{errors.housing_transportation_measure_of_success}
} +
+
+ + {/* Child Support Section */} +
+

Child Support Action Goal (Leave Blank)

+
+ + {/* Child Support/Co-Parenting Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ObjectivesStrategies to Achieve ObjectivePerson ResponsibleTimelinesMeasure of Success
Child Support Awareness and Information + updateObjective('child_support_awareness', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('child_support_awareness', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('child_support_awareness', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('child_support_awareness', 'measure_of_success', e.target.value)} + disabled={true} + /> +
Effective Co-Parenting + updateObjective('co_parenting', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('co_parenting', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('co_parenting', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('co_parenting', 'measure_of_success', e.target.value)} + disabled={true} + /> +
Father to Father Mentoring + updateObjective('mentoring', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('mentoring', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('mentoring', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('mentoring', 'measure_of_success', e.target.value)} + disabled={true} + /> +
+
+ + {/* Signature Section */} +
+
+ + setData('participant_signature', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.participant_signature &&
{errors.participant_signature}
} + +
+ + + setData('participant_signature_date', e.target.value) + } + /> + {errors.participant_signature_date &&
{errors.participant_signature_date}
} +
+
+ +
+ + setData('case_manager_signature', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.case_manager_signature &&
{errors.case_manager_signature}
} +
+ + + setData('case_manager_signature_date', e.target.value) + } + /> + {errors.case_manager_signature_date &&
{errors.case_manager_signature_date}
} +
+
+
+ + {/* Submit Button */} +
+ +
+
+
+
+ ); +}; + +export default ServicePlanForm; \ No newline at end of file diff --git a/resources/js/Pages/Auth/Login.tsx b/resources/js/Pages/Auth/Login.tsx index c431a976..a032e84a 100644 --- a/resources/js/Pages/Auth/Login.tsx +++ b/resources/js/Pages/Auth/Login.tsx @@ -3,6 +3,12 @@ import GuestLayout from '@/Layouts/GuestLayout' import { Button, Label, Input, InputError, Checkbox } from '@/Components/ui' import { Head, Link, useForm } from '@inertiajs/react' +interface LoginFormDefinition extends Record { + email: string + password: string + remember: boolean +} + export default function Login({ status, canResetPassword, @@ -10,7 +16,7 @@ export default function Login({ status?: string canResetPassword: boolean }) { - const { data, setData, post, processing, errors, reset } = useForm({ + const { data, setData, post, processing, errors, reset } = useForm({ email: '', password: '', remember: false, diff --git a/resources/js/Pages/Intake/Disclosure/Create.tsx b/resources/js/Pages/Intake/Disclosure/Create.tsx index d272a88a..29fd6aaf 100644 --- a/resources/js/Pages/Intake/Disclosure/Create.tsx +++ b/resources/js/Pages/Intake/Disclosure/Create.tsx @@ -1,18 +1,18 @@ import React from 'react' -import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import { Participant } from '@/types/participant' +import IntakeLayout from '@/Layouts/IntakeLayout' +import DisclosureAuthorizationForm from '@/Components/Intake/DisclosureAuthorizationForm' -interface DisclosureProps extends PageProps {} +interface DisclosureProps extends PageProps { + participant: Participant +} -export const Create: React.FC = ({ auth }) => { +export const Create: React.FC = ({ participant }) => { return ( - - -
-
Disclosure
-
-
+ + + ) } diff --git a/resources/js/Pages/Intake/Disclosure/Index.tsx b/resources/js/Pages/Intake/Disclosure/Index.tsx index c7beeeb2..b676f9d1 100644 --- a/resources/js/Pages/Intake/Disclosure/Index.tsx +++ b/resources/js/Pages/Intake/Disclosure/Index.tsx @@ -1,18 +1,115 @@ import React from 'react' -import { Head } from '@inertiajs/react' +// import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +// import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import type { Participant } from '@/types/participant' +import type { IntakeDisclosureAuthorizationForm } from '@/types/intake-disclosure-authorization-form' +import { Button } from '@/Components/ui' +import { router } from '@inertiajs/react' +import { clsx } from 'clsx' +import IntakeLayout from '@/Layouts/IntakeLayout' +import dayjs from 'dayjs' -interface DisclosureProps extends PageProps {} +interface DisclosureProps extends PageProps { + participant: Participant + disclosureAuthorizations: IntakeDisclosureAuthorizationForm[] +} -export const Index: React.FC = ({ auth }) => { +export const Index: React.FC = ({ + participant, + disclosureAuthorizations +}) => { + if (disclosureAuthorizations.length === 0) { + router.visit(route('intake.disclosure.create')) + } return ( - - -
-
Disclosure
+ +
+
Signed Date
+
Signed Name
+
Actions
+ {disclosureAuthorizations.map((disclosureAuthentication, index) => ( + +
+ {participant.name} +
+
+ {/* {dayjs(disclosureAuthentication.created_at).format('MM/DD/YYYY')} */} +
+
+ + + +
+
+ ))} +
+
+ + + {disclosureAuthorizations.length > 0 && ( + + )}
- +
) } diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx index 127fc7b5..a8d346e6 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Create.tsx @@ -11,7 +11,7 @@ interface IntakePageProps extends PageProps { export const Create: React.FC = ({ participant }) => { return ( - + ) diff --git a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx index 7daf422e..90c518ca 100644 --- a/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx +++ b/resources/js/Pages/Intake/FatherhoodAssessment/Index.tsx @@ -2,7 +2,6 @@ import React from 'react' import { type PageProps } from '@/types' import type { Participant } from '@/types/participant' import IntakeLayout from '@/Layouts/IntakeLayout' -import type { IntakeMediaReleaseForm } from '@/types/intake-media-release-form' import { Button } from '@/Components/ui' import { router } from '@inertiajs/react' import { clsx } from 'clsx' @@ -11,27 +10,29 @@ import dayjs from 'dayjs' interface AssessmentPageProps extends PageProps { participant: Participant - fatherhoodSurveys: IntakeFatherhoodAssessmentForm[] + fatherhoodAssessments: IntakeFatherhoodAssessmentForm[] } export const Index: React.FC = ({ participant, - fatherhoodSurveys, + fatherhoodAssessments, }) => { - if (fatherhoodSurveys.length === 0) { + + + if (fatherhoodAssessments.length === 0) { router.visit(route('intake.fatherhood-assessment.create')) } return (
Signed Date
Signed Name
Actions
- {fatherhoodSurveys.map((assessment, index) => ( + {fatherhoodAssessments.map((assessment, index) => (
{assessment.participant_name} @@ -96,7 +97,7 @@ export const Index: React.FC = ({ Create New Assessment - {fatherhoodSurveys.length > 0 && ( + {fatherhoodAssessments.length > 0 && ( + + +
+
+ ))} +
+
+ + + {servicePlans.length > 0 && ( + + )}
- +
) } diff --git a/resources/js/Pages/Intake/ServicePlan/types.ts b/resources/js/Pages/Intake/ServicePlan/types.ts new file mode 100644 index 00000000..2a932238 --- /dev/null +++ b/resources/js/Pages/Intake/ServicePlan/types.ts @@ -0,0 +1,37 @@ +// types.ts - Contains TypeScript interfaces for the application + +export interface ServicePlan { + id?: number; + participant_name: string; + client_number: string; + review_dates: string; + service_areas: { + parenting_skill_development: boolean; + effective_co_parenting: boolean; + employment_and_education: boolean; + child_support: boolean; + domestic_violence: boolean; + }; + service_identified_by_participant: string; + goal: string; + objectives: { + parenting_skills: Objective; + stress_and_anger: Objective; + custody_visitation: Objective; + education_employment: Objective; + housing_transportation: Objective; + child_support_awareness: Objective; + co_parenting: Objective; + mentoring: Objective; + }; + created_at?: string; + updated_at?: string; + } + + export interface Objective { + id?: number; + strategies: string; + person_responsible: string; + timelines: string; + measure_of_success: string; + } \ No newline at end of file diff --git a/resources/js/Pages/Intake/Signup.tsx b/resources/js/Pages/Intake/Signup.tsx index 23302f89..bf2e033e 100644 --- a/resources/js/Pages/Intake/Signup.tsx +++ b/resources/js/Pages/Intake/Signup.tsx @@ -1,10 +1,11 @@ -import { PageProps, Child } from '@/types' +import { PageProps, Child, User } from '@/types' import IntakeLayout from '@/Layouts/IntakeLayout' import { useForm } from '@inertiajs/react' import { Button, Input, InputError, Label } from '@/Components/ui' import { ChildrenTable } from '@/Components/ChildrenTable' interface StartPageProps extends PageProps { + user: User maritalStatus: Record ethnicity: Record regions: { @@ -14,6 +15,7 @@ interface StartPageProps extends PageProps { } export default function StartPage({ + user, maritalStatus, ethnicity, regions, @@ -26,7 +28,7 @@ export default function StartPage({ zipcode: '', employer: '', t_shirt_size: '', - home_phone_number: '', + home_phone_number: user.phone_number?.toString(), work_phone_number: '', cell_phone_number: '', alt_contact_number: '', diff --git a/resources/js/types/index.d.ts b/resources/js/types/index.d.ts index 0a5f2a13..43fb6d5f 100644 --- a/resources/js/types/index.d.ts +++ b/resources/js/types/index.d.ts @@ -5,6 +5,7 @@ export interface User { first_name: string last_name: string email: string + phone_number: number roles: string[] permissions: string[] } diff --git a/resources/js/types/intake-disclosure-authorization-form.ts b/resources/js/types/intake-disclosure-authorization-form.ts new file mode 100644 index 00000000..7768a760 --- /dev/null +++ b/resources/js/types/intake-disclosure-authorization-form.ts @@ -0,0 +1,82 @@ +export interface IntakeDisclosureAuthorizationForm { + id: string; + + // Consumer details + consumer_name: string; + + // Authorized entities + is_dss_authorized: boolean; + is_dys_authorized: boolean; + is_mhd_authorized: boolean; + is_dfas_authorized: boolean; + is_mmac_authorized: boolean; + is_fsd_authorized: boolean; + is_cd_authorized: boolean; + is_dls_authorized: boolean; + is_other_authorized: boolean; + other_authorized_entity: string; + + // Subject information + subject_name: string; + subject_phone: string; + subject_dob: string; // ISO format date string + subject_ssn: string; + subject_address: string; + subject_email: string; + + // Recipients + disclose_to_attorney: boolean; + attorney_name: string; + disclose_to_employer: boolean; + employer_name: string; + disclose_to_legislator: boolean; + legislator_name: string; + disclose_to_governors_staff: boolean; + governors_staff_details: string; + disclose_to_other_recipient: boolean; + other_recipient_name: string; + other_recipient_address: string; + + // Purpose of disclosure + purpose_eligibility_determination: boolean; + purpose_legal_consultation: boolean; + purpose_legal_proceedings: boolean; + purpose_employment: boolean; + purpose_complaint_investigation: boolean; + purpose_treatment_planning: boolean; + purpose_continuity_of_services: boolean; + purpose_background_investigation: boolean; + purpose_consumer_request: boolean; + purpose_share_and_refer: boolean; + share_and_refer_details: string, + purpose_other: boolean; + other_purpose_details: string; + + // Information to be disclosed + disclose_entire_file: boolean; + disclose_licensure_information: boolean; + disclose_medical_psychiatric_records: boolean; + disclose_hotline_investigations: boolean; + disclose_home_studies: boolean; + disclose_eligibility_determinations: boolean; + disclose_substance_abuse_treatment: boolean; + disclose_client_employment_records: boolean; + disclose_benefits_received: boolean; + disclose_other_information: boolean; + other_disclosure_details: string; + + // Communication preferences + accept_text_messages: boolean + // Signatures + consumer_signature: string; + signature_date: string; + witness_signature: string; + witness_signature_date: string; + guardian_signature: string; + guardian_authority: string; + + // Survey delivery methods + survey_by_email: boolean; + survey_by_mail: boolean; + survey_by_online: boolean; +}; \ No newline at end of file diff --git a/resources/js/types/intake-serviceplan-form.ts b/resources/js/types/intake-serviceplan-form.ts new file mode 100644 index 00000000..a396d4ea --- /dev/null +++ b/resources/js/types/intake-serviceplan-form.ts @@ -0,0 +1,31 @@ +export interface IntakeServicePlanForm { + id: string + + participant_name: string + client_number: string + review_date: string + parenting_skill_development_is_service_area: boolean + effective_co_parenting_is_service_area: boolean + employment_and_education_is_service_area: boolean + child_support_is_service_area: boolean + domestic_violence_is_service_area: boolean + service_identified_by_participant: string + goal: string + custody_visitation_strategy: string + custody_visitation_person_responsible: string + custody_visitation_timeline: string + custody_visitation_measure_of_success: string + education_employment_strategy: string + education_employment_person_responsible: string + education_employment_timeline: string + education_employment_measure_of_success: string + housing_transportation_strategy: string + housing_transportation_person_responsible: string + housing_transportation_timeline: string + housing_transportation_measure_of_success: string + participant_signature: string + participant_signature_date: string + case_manager_signature: string + case_manager_signature_date: string + date_completed: string +} diff --git a/routes/auth.php b/routes/auth.php index 94f985fb..2b711fa8 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -17,7 +17,7 @@ Route::post('register', [RegisteredUserController::class, 'store']); - Route::post('login', [AuthenticatedSessionController::class, 'store']); + Route::post('login', [AuthenticatedSessionController::class, 'store']) -> name('login'); Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) ->name('password.request'); From 9ac702134cdb0d5c89a2deba1ce5d82b782eb552 Mon Sep 17 00:00:00 2001 From: Daniel Rustad Date: Mon, 21 Apr 2025 00:46:15 -0500 Subject: [PATCH 21/40] Passing npx prettier --check now. --- .../Intake/DisclosureAuthorizationForm.tsx | 1824 +++++++++++------ .../js/Components/Intake/ServicePlanForm.tsx | 1640 ++++++++------- resources/js/Pages/Auth/Login.tsx | 11 +- .../js/Pages/Intake/Disclosure/Create.tsx | 10 +- .../js/Pages/Intake/Disclosure/Index.tsx | 16 +- .../Intake/FatherhoodAssessment/Create.tsx | 5 +- .../Intake/FatherhoodAssessment/Index.tsx | 2 - .../js/Pages/Intake/ParticipantRegister.tsx | 22 +- .../js/Pages/Intake/ServicePlan/Create.tsx | 11 +- .../js/Pages/Intake/ServicePlan/Index.tsx | 17 +- .../js/Pages/Intake/ServicePlan/types.ts | 68 +- .../intake-disclosure-authorization-form.ts | 158 +- resources/js/types/intake-serviceplan-form.ts | 2 +- 13 files changed, 2243 insertions(+), 1543 deletions(-) diff --git a/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx b/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx index 515b8ded..eaccaa84 100644 --- a/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx +++ b/resources/js/Components/Intake/DisclosureAuthorizationForm.tsx @@ -1,684 +1,1156 @@ -import React from "react"; -import { router, useForm } from "@inertiajs/react"; -import { - Button, - Input, - Checkbox, - Label, - Card, - CardContent, - CardHeader, - CardTitle, -} from "@/Components/ui"; -import { IntakeDisclosureAuthorizationForm } from "@/types/intake-disclosure-authorization-form"; +import React from 'react' +import { router, useForm } from '@inertiajs/react' +import { + Button, + Input, + Checkbox, + Label, + Card, + CardContent, + CardHeader, + CardTitle, +} from '@/Components/ui' +import { IntakeDisclosureAuthorizationForm } from '@/types/intake-disclosure-authorization-form' import type { Participant } from '@/types/participant' interface DisclosureAuthorizationFormProps { - participant: Participant - disclosureAuthorizationForm?: IntakeDisclosureAuthorizationForm - viewOnly?: boolean - nextRoute?: string + participant: Participant + disclosureAuthorizationForm?: IntakeDisclosureAuthorizationForm + viewOnly?: boolean + nextRoute?: string } -interface DisclosureAuthorizationFormDefinition extends Record { - consumer_name: string, - is_dss_authorized: boolean, - is_fsd_authorized: boolean, - is_dys_authorized: boolean, - is_cd_authorized: boolean, - is_mhd_authorized: boolean, - is_dls_authorized: boolean, - is_dfas_authorized: boolean, - is_mmac_authorized: boolean, - is_other_authorized: boolean, - other_authorized_entity: string, - - subject_name: string, - subject_phone: string, - subject_dob: string, - subject_ssn: string, - subject_address: string, - subject_email: string, - - disclose_to_attorney: boolean, - attorney_name: string, - disclose_to_employer: boolean, - employer_name: string, - disclose_to_legislator: boolean, - legislator_name: string, - disclose_to_governors_staff: boolean, - governors_staff_details: string, - disclose_to_other_recipient: boolean, - other_recipient_name: string, - other_recipient_address: string, - - purpose_eligibility_determination: boolean, - purpose_legal_consultation: boolean, - purpose_legal_proceedings: boolean, - purpose_employment: boolean, - purpose_complaint_investigation: boolean, - purpose_treatment_planning: boolean, - purpose_continuity_of_services: boolean, - purpose_background_investigation: boolean, - purpose_consumer_request: boolean, - purpose_share_and_refer: boolean, - share_and_refer_details: string, - purpose_other: boolean, - other_purpose_details: string, - - disclose_entire_file: boolean, - disclose_licensure_information: boolean, - disclose_medical_psychiatric_records: boolean, - disclose_hotline_investigations: boolean, - disclose_home_studies: boolean, - disclose_eligibility_determinations: boolean, - disclose_substance_abuse_treatment: boolean, - disclose_client_employment_records: boolean, - disclose_benefits_received: boolean, - disclose_other_information: boolean, - other_disclosure_details: string, - - - accept_text_messages: boolean, - consumer_signature: string, - signature_date: string, - witness_signature: string, - witness_signature_date: string, - guardian_signature: string, - guardian_authority: string, - - survey_by_email: boolean, - survey_by_mail: boolean, - survey_by_online: boolean, +interface DisclosureAuthorizationFormDefinition + extends Record { + consumer_name: string + is_dss_authorized: boolean + is_fsd_authorized: boolean + is_dys_authorized: boolean + is_cd_authorized: boolean + is_mhd_authorized: boolean + is_dls_authorized: boolean + is_dfas_authorized: boolean + is_mmac_authorized: boolean + is_other_authorized: boolean + other_authorized_entity: string + + subject_name: string + subject_phone: string + subject_dob: string + subject_ssn: string + subject_address: string + subject_email: string + + disclose_to_attorney: boolean + attorney_name: string + disclose_to_employer: boolean + employer_name: string + disclose_to_legislator: boolean + legislator_name: string + disclose_to_governors_staff: boolean + governors_staff_details: string + disclose_to_other_recipient: boolean + other_recipient_name: string + other_recipient_address: string + + purpose_eligibility_determination: boolean + purpose_legal_consultation: boolean + purpose_legal_proceedings: boolean + purpose_employment: boolean + purpose_complaint_investigation: boolean + purpose_treatment_planning: boolean + purpose_continuity_of_services: boolean + purpose_background_investigation: boolean + purpose_consumer_request: boolean + purpose_share_and_refer: boolean + share_and_refer_details: string + purpose_other: boolean + other_purpose_details: string + + disclose_entire_file: boolean + disclose_licensure_information: boolean + disclose_medical_psychiatric_records: boolean + disclose_hotline_investigations: boolean + disclose_home_studies: boolean + disclose_eligibility_determinations: boolean + disclose_substance_abuse_treatment: boolean + disclose_client_employment_records: boolean + disclose_benefits_received: boolean + disclose_other_information: boolean + other_disclosure_details: string + + accept_text_messages: boolean + consumer_signature: string + signature_date: string + witness_signature: string + witness_signature_date: string + guardian_signature: string + guardian_authority: string + + survey_by_email: boolean + survey_by_mail: boolean + survey_by_online: boolean } -export const DisclosureAuthorizationForm: React.FC = ({ - participant, - disclosureAuthorizationForm, - viewOnly = false, - nextRoute = 'intake.disclosure.store' +export const DisclosureAuthorizationForm: React.FC< + DisclosureAuthorizationFormProps +> = ({ + participant, + disclosureAuthorizationForm, + viewOnly = false, + nextRoute = 'intake.disclosure.store', }) => { - const { data, setData, processing, errors, ...form } = - useForm({ - consumer_name: disclosureAuthorizationForm?.consumer_name ?? participant?.name ?? '', - is_dss_authorized: disclosureAuthorizationForm?.is_dss_authorized ?? true, - is_fsd_authorized: disclosureAuthorizationForm?.is_fsd_authorized ?? true, - is_dys_authorized: disclosureAuthorizationForm?.is_dys_authorized ?? false, - is_cd_authorized: disclosureAuthorizationForm?.is_cd_authorized ?? false, - is_mhd_authorized: disclosureAuthorizationForm?.is_mhd_authorized ?? false, - is_dls_authorized: disclosureAuthorizationForm?.is_dls_authorized ?? false, - is_dfas_authorized: disclosureAuthorizationForm?.is_dfas_authorized ?? false, - is_mmac_authorized: disclosureAuthorizationForm?.is_mmac_authorized ?? false, - is_other_authorized: disclosureAuthorizationForm?.is_other_authorized ?? false, - other_authorized_entity: disclosureAuthorizationForm?.other_authorized_entity ?? '', - - subject_name: disclosureAuthorizationForm?.subject_name ?? participant.name ?? '', - subject_phone: disclosureAuthorizationForm?.subject_phone ?? participant.home_phone_number ?? '', - subject_dob: disclosureAuthorizationForm?.subject_dob ?? '', - subject_ssn: disclosureAuthorizationForm?.subject_ssn ?? '', - subject_address: disclosureAuthorizationForm?.subject_address ?? participant.address_line_1 ?? '', - subject_email: disclosureAuthorizationForm?.subject_email ?? participant.user.email ?? '', - - disclose_to_attorney: disclosureAuthorizationForm?.disclose_to_attorney ?? false, - attorney_name: disclosureAuthorizationForm?.attorney_name ?? '', - disclose_to_employer: disclosureAuthorizationForm?.disclose_to_employer ?? false, - employer_name: disclosureAuthorizationForm?.employer_name ?? '', - disclose_to_legislator: disclosureAuthorizationForm?.disclose_to_legislator ?? false, - legislator_name: disclosureAuthorizationForm?.legislator_name ?? '', - disclose_to_governors_staff: disclosureAuthorizationForm?.disclose_to_governors_staff ?? false, - governors_staff_details: disclosureAuthorizationForm?.governors_staff_details ?? '', - disclose_to_other_recipient: disclosureAuthorizationForm?.disclose_to_other_recipient ?? true, - other_recipient_name: disclosureAuthorizationForm?.other_recipient_name ?? 'Good Dads/Jennifer Baker (and staff)', - other_recipient_address: disclosureAuthorizationForm?.other_recipient_address ?? '205 W. Walnut Street, Ste. 10, Springfield, MO 65806', - - purpose_eligibility_determination: disclosureAuthorizationForm?.purpose_eligibility_determination ?? false, - purpose_legal_consultation: disclosureAuthorizationForm?.purpose_legal_consultation ?? false, - purpose_legal_proceedings: disclosureAuthorizationForm?.purpose_legal_proceedings ?? false, - purpose_employment: disclosureAuthorizationForm?.purpose_employment ?? false, - purpose_complaint_investigation: disclosureAuthorizationForm?.purpose_complaint_investigation ?? false, - purpose_treatment_planning: disclosureAuthorizationForm?.purpose_treatment_planning ?? false, - purpose_continuity_of_services: disclosureAuthorizationForm?.purpose_continuity_of_services ?? false, - purpose_background_investigation: disclosureAuthorizationForm?.purpose_background_investigation ?? false, - purpose_consumer_request: disclosureAuthorizationForm?.purpose_consumer_request ?? false, - purpose_share_and_refer: disclosureAuthorizationForm?.purpose_share_and_refer ?? true, - share_and_refer_details: disclosureAuthorizationForm?.share_and_refer_details ?? 'Good Dads', - purpose_other: disclosureAuthorizationForm?.purpose_other ?? false, - other_purpose_details: disclosureAuthorizationForm?.other_purpose_details ?? '', - - disclose_entire_file: disclosureAuthorizationForm?.disclose_entire_file ?? true, - disclose_licensure_information: disclosureAuthorizationForm?.disclose_licensure_information ?? false, - disclose_medical_psychiatric_records: disclosureAuthorizationForm?.disclose_medical_psychiatric_records ?? false, - disclose_hotline_investigations: disclosureAuthorizationForm?.disclose_hotline_investigations ?? false, - disclose_home_studies: disclosureAuthorizationForm?.disclose_home_studies ?? false, - disclose_eligibility_determinations: disclosureAuthorizationForm?.disclose_eligibility_determinations ?? false, - disclose_substance_abuse_treatment: disclosureAuthorizationForm?.disclose_substance_abuse_treatment ?? false, - disclose_client_employment_records: disclosureAuthorizationForm?.disclose_client_employment_records ?? false, - disclose_benefits_received: disclosureAuthorizationForm?.disclose_benefits_received ?? true, - disclose_other_information: disclosureAuthorizationForm?.disclose_other_information ?? true, - other_disclosure_details: - disclosureAuthorizationForm?.other_disclosure_details ?? 'Child support records that FSD may release to the parent from his/her own case file.', - - accept_text_messages: disclosureAuthorizationForm?.accept_text_messages ?? false, - consumer_signature: disclosureAuthorizationForm?.consumer_signature ?? '', - signature_date: disclosureAuthorizationForm?.signature_date ?? '', - witness_signature: disclosureAuthorizationForm?.witness_signature ?? '', - witness_signature_date: disclosureAuthorizationForm?.witness_signature_date ?? '', - guardian_signature: disclosureAuthorizationForm?.guardian_signature ?? '', - guardian_authority: disclosureAuthorizationForm?.guardian_authority ?? '', - - survey_by_email: disclosureAuthorizationForm?.survey_by_email ?? false, - survey_by_mail: disclosureAuthorizationForm?.survey_by_mail ?? false, - survey_by_online: disclosureAuthorizationForm?.survey_by_online ?? false, - }); - - const continueToNextStep = () => { - router.visit(route(nextRoute)) - } - - const renderCheckbox = (id: string, label: string, is_checked: boolean, is_view_only: boolean) => ( -
- setData(id, !!checked)} - disabled={is_view_only} - /> - -
- ); - - const renderConditionalField = (id: string, checkbox_id: string, label: string, content: string, is_view_only: boolean, required = false) => ( -
- - setData(id, e.target.value)} - disabled={is_view_only} - className="w-full" - /> - {errors[id] &&

{errors[id]}

} -
- ); - - return ( -
- - - AUTHORIZATION FOR DISCLOSURE OF CONFIDENTIAL INFORMATION - - -
{ - e.preventDefault() - if (viewOnly) { - continueToNextStep() - return - } - - if (disclosureAuthorizationForm?.id) { - form.put(route('intake.disclosure.update'), { - onSuccess: () => { - continueToNextStep() - }, - }) - } else { - form.post(route('intake.disclosure.store'), { - onSuccess: () => { - continueToNextStep() - }, - }) - } - }} - > - {/* Consumer Information */} -
-
- - setData('participant_name', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - placeholder="NAME OF CLIENT, PARENT, GUARDIAN/LEGAL REPRESENTATIVE" - /> - {errors.consumer_name &&

{errors.consumer_name}

} -

authorize and request

-
- - {/* Authorized Entities */} -
-

Check all that apply:

-
-
- {renderCheckbox('is_dss_authorized', 'Department of Social Services (DSS)', data.is_dss_authorized, (viewOnly || processing))} - {renderCheckbox('is_dys_authorized', 'Division of Youth Services (DYS)', data.is_dys_authorized, (viewOnly || processing))} - {renderCheckbox('is_mhd_authorized', 'MO HealthNet Division (MHD)', data.is_mhd_authorized, (viewOnly || processing))} - {renderCheckbox('is_dfas_authorized', 'Division of Finance & Administrative Services (DFAS)', data.is_dfas_authorized, (viewOnly || processing))} -
-
- {renderCheckbox('is_fsd_authorized', 'Family Support Division (FSD)', data.is_fsd_authorized, (viewOnly || processing))} - {renderCheckbox('is_cd_authorized', 'Children\'s Division (CD)', data.is_cd_authorized, (viewOnly || processing))} - {renderCheckbox('is_dls_authorized', 'Division of Legal Services (DLS)', data.is_dls_authorized, (viewOnly || processing))} - {renderCheckbox('is_mmac_authorized', 'Missouri Medicaid Audit and Compliance (MMAC)', data.is_mmac_authorized, (viewOnly || processing))} -
-
- -
- {renderCheckbox('is_other_authorized', 'Other', data.is_other_authorized, (viewOnly || processing))} - {renderConditionalField('other_authorized_entity', 'is_other_authorized', 'Name of facility, agency, mental health center, person', data.other_authorized_entity, (viewOnly || processing), true)} -
-
-
- - {/* Subject Information */} -
-

to disclose/release the below specified information of:

-
-
- - setData('subject_name', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.subject_name &&

{errors.subject_name}

} -
- -
- - setData('subject_phone', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.subject_phone &&

{errors.subject_phone}

} -
- -
- - setData('subject_email', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.subject_email &&

{errors.subject_email}

} -
- -
- - - setData('subject_dob', e.target.value) - } - /> - {errors.subject_dob &&

{errors.subject_dob}

} -
- -
- - setData('subject_ssn', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> -
-
-
- - {/* Recipients */} -
-

to (check all that apply)

-
-
- {renderCheckbox('disclose_to_attorney', 'Attorney', data.disclose_to_attorney, (viewOnly || processing))} - {renderConditionalField('attorney_name', 'disclose_to_attorney', 'Name', data.attorney_name, (viewOnly || processing))} - - {renderCheckbox('disclose_to_employer', 'Employer', data.disclose_to_employer, (viewOnly || processing))} - {renderConditionalField('employer_name', 'disclose_to_employer', 'Name', data.employer_name, (viewOnly || processing))} -
- -
- {renderCheckbox('disclose_to_legislator', 'Legislator/staff', data.disclose_to_legislator, (viewOnly || processing))} - {renderConditionalField('legislator_name', 'disclose_to_legislator', 'Name', data.legislator_name, (viewOnly || processing))} - - {renderCheckbox('disclose_to_governors_staff', 'Governor\'s staff', data.disclose_to_governors_staff, (viewOnly || processing))} - {renderConditionalField('governors_staff_details', 'disclose_to_governors_staff', 'Name', data.governors_staff_details, (viewOnly || processing))} -
-
- -
- {renderCheckbox('disclose_to_other_recipient', 'Other', data.disclose_to_other_recipient, (viewOnly || processing))} - {data.disclose_to_other_recipient && ( -
-
- - setData('other_recipient_details', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> -
- -
- - setData('other_recipient_address', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> -
-
- )} -
-
- - {/* Purpose of Disclosure */} -
-

The purpose of this disclosure is (check all that apply)

-
-
- {renderCheckbox('purpose_eligibility_determination', 'Eligibility determination', data.purpose_eligibility_determination, (viewOnly || processing))} - {renderCheckbox('purpose_employment', 'Employment', data.purpose_employment, (viewOnly || processing))} - {renderCheckbox('purpose_continuity_of_services', 'Continuity of services/care', data.purpose_continuity_of_services, (viewOnly || processing))} -
- -
- {renderCheckbox('purpose_legal_consultation', 'Legal consultation/representation', data.purpose_legal_consultation, (viewOnly || processing))} - {renderCheckbox('purpose_complaint_investigation', 'Complaint/investigation/resolution', data.purpose_complaint_investigation, (viewOnly || processing))} - {renderCheckbox('purpose_background_investigation', 'Background investigation', data.purpose_background_investigation, (viewOnly || processing))} -
- -
- {renderCheckbox('purpose_legal_proceedings', 'Legal proceedings', data.purpose_legal_consultation, (viewOnly || processing))} - {renderCheckbox('purpose_treatment_planning', 'Treatment planning', data.purpose_treatment_planning, (viewOnly || processing))} - {renderCheckbox('purpose_consumer_request', 'At consumer\'s request', data.purpose_consumer_request, (viewOnly || processing))} -
-
- -
- {renderCheckbox( - 'purpose_share_and_refer', - 'To share or refer my information to other Missouri state agencies (such as DMH, DHSS, DSS, DESE, etc.) to obtain services consistent with the', - data.purpose_share_and_refer, - (viewOnly || processing) - )} - {data.purpose_share_and_refer && ( -
- - setData('share_and_refer_details', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - placeholder="program (please complete the name of the program in which you want to participate)" - /> -
- )} -
- -
- {renderCheckbox('purpose_other', 'Other (specify)', data.purpose_other, (viewOnly || processing))} - {renderConditionalField('other_purpose_details', 'purpose_other', 'Details', data.other_purpose_details, (viewOnly || processing))} -
-
- - {/* Information to be Disclosed */} -
-

The specific information to be disclosed is (check all that apply)

-
-
- {renderCheckbox('disclose_entire_file', 'Entire file', data.disclose_entire_file, (viewOnly || processing))} - {renderCheckbox('disclose_licensure_information', 'Licensure information', data.disclose_licensure_information, (viewOnly || processing))} - {renderCheckbox('disclose_medical_psychiatric_records', 'Medical/psychiatric evaluation/treatment records', data.disclose_medical_psychiatric_records, (viewOnly || processing))} -
- -
- {renderCheckbox('disclose_hotline_investigations', 'Hotline investigations', data.disclose_hotline_investigations, (viewOnly || processing))} - {renderCheckbox('disclose_home_studies', 'Home studies', data.disclose_home_studies, (viewOnly || processing))} - {renderCheckbox('disclose_client_employment_records', 'Client employment records', data.disclose_client_employment_records, (viewOnly || processing))} -
- -
- {renderCheckbox('disclose_eligibility_determinations', 'Eligibility determinations', data.disclose_eligibility_determinations, (viewOnly || processing))} - {renderCheckbox('disclose_substance_abuse_treatment', 'Substance abuse treatment', data.disclose_substance_abuse_treatment, (viewOnly || processing))} - {renderCheckbox('disclose_benefits_received', 'Benefits received', data.disclose_benefits_received, (viewOnly || processing))} -
-
- -
- {renderCheckbox('disclose_other_information', 'Other', data.disclose_other_information, (viewOnly || processing))} - {renderConditionalField('other_disclosure_details', 'disclose_other_information', 'Details', data.other_disclosure_details, (viewOnly || processing))} -
-
- - {/* Terms and Authorization */} -
-
-
    -
  1. READ CAREFULLY: I understand that my information and records with the Department of Social Services are confidential by law. I understand that by - signing this authorization, I am allowing the release of any and all of my information and records which I am authorized to receive as specified on this - document whether past, present or created in the future up to the expiration or revocation date of this authorization, unless otherwise authorized. The - protected information in my records may include medical treatment and/or evaluation information, mental/behavioral health information, information relating - to sexually transmitted diseases, acquired immunodeficiency syndrome (AIDS), human immunodeficiency virus (HIV), other communicable or - environmental diseases and conditions, alcohol/drug abuse, application for and/or receipt of public assistance benefits, alcohol/drug abuse information, - and/or information concerning child abuse and neglect.
  2. -
  3. This authorization includes both information presently compiled and information to be compiled during your association or dealings with the Department - of Social Services, during the specified time frame.
  4. -
  5. Unless otherwise indicated, this authorization becomes effective on the date of signature below and will expire one year from that date.
  6. -
  7. I understand that I have a right to revoke this authorization at any time. I understand that if I revoke this authorization I must do so IN WRITING and present - my written revocation to the Privacy Officer of the Department of Social Services at P.O. Box 1527, MO 65102. I further - understand that actions already taken based on this authorization, prior to revocation, will NOT be affected.
  8. -
  9. I understand that I have the right to receive a copy of this authorization upon request. A photographic copy of this authorization is as valid as the - original.
  10. -
  11. I understand that authorizing the disclosure of this information is voluntary. I can refuse to sign this authorization. I need not sign this form in order to receive - services from the Department of Social Services. I understand that I may request to inspect or request a copy of information to be used or disclosed, as - provided in 45 CFR section 164.524. I understand that any disclosure of information carries with it the potential for redisclosure by the party - receiving it and that the information may no longer be protected by law once it is in the possession of the receiving party. If I have questions about - disclosure of my information, I can contact the Privacy Officer of the Department of Social Services, my caseworker or family support eligibility specialist.
  12. -
  13. By signing this disclosure on paper or electronically, I am giving the Family Support Division (FSD) permission to deliver, or cause to be delivered, - phone calls or text messages to me regarding my case from an automated dialing system at my primary number. The FSD does not use an encryption system when - sending text messages. Such unencrypted systems are not secure and carry some level of risk that text messages could be read by a third party. By signing, - I am affirming that I nevertheless prefer to receive text messages from FSD and understand I do not have to consent to this as part of my application and - can opt out of getting these calls or text messages by checking “No” in the “Accept Text Messages” box below
  14. -
- -

My signature below acknowledges that I have read and understood the text above, and authorize the release of my confidential information.

-
-
- - {/* Signature Section */} -
-
-
- - setData('consumer_signature', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.consumer_signature &&

{errors.consumer_signature}

} -
- -
- - - setData('signature_date', e.target.value) - } - /> - {errors.signature_date &&

{errors.signature_date}

} -
-
- -
-
- - setData('witness_signature', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.consumer_signature &&

{errors.witness_signature}

} -
- -
- - - setData('witness_signature_date', e.target.value) - } - /> - {errors.witness_signature_date &&

{errors.witness_signature_date}

} -
-
- -
-
-
- - setData('guardian_signature', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> -
- -
- - -
- setData("accept_text_messages", Boolean(checked))} - disabled={viewOnly || processing} - /> - -
- -
- setData("accept_text_messages", Boolean(!checked))} - disabled={viewOnly || processing} - /> - -
-
- -
- -
-
-
-
- -
-

Surveys

-

- Family Support Division would like to know what services enrolled participants are seeking from our programs. In an effort to capture this data - Family Support Division is administering a survey through Survey Monkey. Plase selet the preferred mothod of survey delivery: -

-
-
- {renderCheckbox('survey_by_email', 'Email', data.survey_by_email, (viewOnly || processing))} -
-
-
-
- {renderCheckbox('survey_by_mail', 'Address', data.survey_by_mail, (viewOnly || processing))} -
-
-
-
- {renderCheckbox('survey_by_online', 'Online', data.survey_by_online, (viewOnly || processing))} -
-
-
- -
- -
-
-
-
-
- ); -}; - -export default DisclosureAuthorizationForm \ No newline at end of file + const { data, setData, processing, errors, ...form } = + useForm({ + consumer_name: + disclosureAuthorizationForm?.consumer_name ?? participant?.name ?? '', + is_dss_authorized: disclosureAuthorizationForm?.is_dss_authorized ?? true, + is_fsd_authorized: disclosureAuthorizationForm?.is_fsd_authorized ?? true, + is_dys_authorized: + disclosureAuthorizationForm?.is_dys_authorized ?? false, + is_cd_authorized: disclosureAuthorizationForm?.is_cd_authorized ?? false, + is_mhd_authorized: + disclosureAuthorizationForm?.is_mhd_authorized ?? false, + is_dls_authorized: + disclosureAuthorizationForm?.is_dls_authorized ?? false, + is_dfas_authorized: + disclosureAuthorizationForm?.is_dfas_authorized ?? false, + is_mmac_authorized: + disclosureAuthorizationForm?.is_mmac_authorized ?? false, + is_other_authorized: + disclosureAuthorizationForm?.is_other_authorized ?? false, + other_authorized_entity: + disclosureAuthorizationForm?.other_authorized_entity ?? '', + + subject_name: + disclosureAuthorizationForm?.subject_name ?? participant.name ?? '', + subject_phone: + disclosureAuthorizationForm?.subject_phone ?? + participant.home_phone_number ?? + '', + subject_dob: disclosureAuthorizationForm?.subject_dob ?? '', + subject_ssn: disclosureAuthorizationForm?.subject_ssn ?? '', + subject_address: + disclosureAuthorizationForm?.subject_address ?? + participant.address_line_1 ?? + '', + subject_email: + disclosureAuthorizationForm?.subject_email ?? + participant.user.email ?? + '', + + disclose_to_attorney: + disclosureAuthorizationForm?.disclose_to_attorney ?? false, + attorney_name: disclosureAuthorizationForm?.attorney_name ?? '', + disclose_to_employer: + disclosureAuthorizationForm?.disclose_to_employer ?? false, + employer_name: disclosureAuthorizationForm?.employer_name ?? '', + disclose_to_legislator: + disclosureAuthorizationForm?.disclose_to_legislator ?? false, + legislator_name: disclosureAuthorizationForm?.legislator_name ?? '', + disclose_to_governors_staff: + disclosureAuthorizationForm?.disclose_to_governors_staff ?? false, + governors_staff_details: + disclosureAuthorizationForm?.governors_staff_details ?? '', + disclose_to_other_recipient: + disclosureAuthorizationForm?.disclose_to_other_recipient ?? true, + other_recipient_name: + disclosureAuthorizationForm?.other_recipient_name ?? + 'Good Dads/Jennifer Baker (and staff)', + other_recipient_address: + disclosureAuthorizationForm?.other_recipient_address ?? + '205 W. Walnut Street, Ste. 10, Springfield, MO 65806', + + purpose_eligibility_determination: + disclosureAuthorizationForm?.purpose_eligibility_determination ?? false, + purpose_legal_consultation: + disclosureAuthorizationForm?.purpose_legal_consultation ?? false, + purpose_legal_proceedings: + disclosureAuthorizationForm?.purpose_legal_proceedings ?? false, + purpose_employment: + disclosureAuthorizationForm?.purpose_employment ?? false, + purpose_complaint_investigation: + disclosureAuthorizationForm?.purpose_complaint_investigation ?? false, + purpose_treatment_planning: + disclosureAuthorizationForm?.purpose_treatment_planning ?? false, + purpose_continuity_of_services: + disclosureAuthorizationForm?.purpose_continuity_of_services ?? false, + purpose_background_investigation: + disclosureAuthorizationForm?.purpose_background_investigation ?? false, + purpose_consumer_request: + disclosureAuthorizationForm?.purpose_consumer_request ?? false, + purpose_share_and_refer: + disclosureAuthorizationForm?.purpose_share_and_refer ?? true, + share_and_refer_details: + disclosureAuthorizationForm?.share_and_refer_details ?? 'Good Dads', + purpose_other: disclosureAuthorizationForm?.purpose_other ?? false, + other_purpose_details: + disclosureAuthorizationForm?.other_purpose_details ?? '', + + disclose_entire_file: + disclosureAuthorizationForm?.disclose_entire_file ?? true, + disclose_licensure_information: + disclosureAuthorizationForm?.disclose_licensure_information ?? false, + disclose_medical_psychiatric_records: + disclosureAuthorizationForm?.disclose_medical_psychiatric_records ?? + false, + disclose_hotline_investigations: + disclosureAuthorizationForm?.disclose_hotline_investigations ?? false, + disclose_home_studies: + disclosureAuthorizationForm?.disclose_home_studies ?? false, + disclose_eligibility_determinations: + disclosureAuthorizationForm?.disclose_eligibility_determinations ?? + false, + disclose_substance_abuse_treatment: + disclosureAuthorizationForm?.disclose_substance_abuse_treatment ?? + false, + disclose_client_employment_records: + disclosureAuthorizationForm?.disclose_client_employment_records ?? + false, + disclose_benefits_received: + disclosureAuthorizationForm?.disclose_benefits_received ?? true, + disclose_other_information: + disclosureAuthorizationForm?.disclose_other_information ?? true, + other_disclosure_details: + disclosureAuthorizationForm?.other_disclosure_details ?? + 'Child support records that FSD may release to the parent from his/her own case file.', + + accept_text_messages: + disclosureAuthorizationForm?.accept_text_messages ?? false, + consumer_signature: disclosureAuthorizationForm?.consumer_signature ?? '', + signature_date: disclosureAuthorizationForm?.signature_date ?? '', + witness_signature: disclosureAuthorizationForm?.witness_signature ?? '', + witness_signature_date: + disclosureAuthorizationForm?.witness_signature_date ?? '', + guardian_signature: disclosureAuthorizationForm?.guardian_signature ?? '', + guardian_authority: disclosureAuthorizationForm?.guardian_authority ?? '', + + survey_by_email: disclosureAuthorizationForm?.survey_by_email ?? false, + survey_by_mail: disclosureAuthorizationForm?.survey_by_mail ?? false, + survey_by_online: disclosureAuthorizationForm?.survey_by_online ?? false, + }) + + const continueToNextStep = () => { + router.visit(route(nextRoute)) + } + + const renderCheckbox = ( + id: string, + label: string, + is_checked: boolean, + is_view_only: boolean, + ) => ( +
+ setData(id, !!checked)} + disabled={is_view_only} + /> + +
+ ) + + const renderConditionalField = ( + id: string, + checkbox_id: string, + label: string, + content: string, + is_view_only: boolean, + required = false, + ) => ( +
+ + setData(id, e.target.value)} + disabled={is_view_only} + className="w-full" + /> + {errors[id] &&

{errors[id]}

} +
+ ) + + return ( +
+ + + + AUTHORIZATION FOR DISCLOSURE OF CONFIDENTIAL INFORMATION + + + +
{ + e.preventDefault() + if (viewOnly) { + continueToNextStep() + return + } + + if (disclosureAuthorizationForm?.id) { + form.put(route('intake.disclosure.update'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } else { + form.post(route('intake.disclosure.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + > + {/* Consumer Information */} +
+
+ + setData('participant_name', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + placeholder="NAME OF CLIENT, PARENT, GUARDIAN/LEGAL REPRESENTATIVE" + /> + {errors.consumer_name && ( +

+ {errors.consumer_name} +

+ )} +

+ authorize and request +

+
+ + {/* Authorized Entities */} +
+

+ Check all that apply: +

+
+
+ {renderCheckbox( + 'is_dss_authorized', + 'Department of Social Services (DSS)', + data.is_dss_authorized, + viewOnly || processing, + )} + {renderCheckbox( + 'is_dys_authorized', + 'Division of Youth Services (DYS)', + data.is_dys_authorized, + viewOnly || processing, + )} + {renderCheckbox( + 'is_mhd_authorized', + 'MO HealthNet Division (MHD)', + data.is_mhd_authorized, + viewOnly || processing, + )} + {renderCheckbox( + 'is_dfas_authorized', + 'Division of Finance & Administrative Services (DFAS)', + data.is_dfas_authorized, + viewOnly || processing, + )} +
+
+ {renderCheckbox( + 'is_fsd_authorized', + 'Family Support Division (FSD)', + data.is_fsd_authorized, + viewOnly || processing, + )} + {renderCheckbox( + 'is_cd_authorized', + "Children's Division (CD)", + data.is_cd_authorized, + viewOnly || processing, + )} + {renderCheckbox( + 'is_dls_authorized', + 'Division of Legal Services (DLS)', + data.is_dls_authorized, + viewOnly || processing, + )} + {renderCheckbox( + 'is_mmac_authorized', + 'Missouri Medicaid Audit and Compliance (MMAC)', + data.is_mmac_authorized, + viewOnly || processing, + )} +
+
+ +
+ {renderCheckbox( + 'is_other_authorized', + 'Other', + data.is_other_authorized, + viewOnly || processing, + )} + {renderConditionalField( + 'other_authorized_entity', + 'is_other_authorized', + 'Name of facility, agency, mental health center, person', + data.other_authorized_entity, + viewOnly || processing, + true, + )} +
+
+
+ + {/* Subject Information */} +
+

+ to disclose/release the below specified information of: +

+
+
+ + setData('subject_name', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.subject_name && ( +

+ {errors.subject_name} +

+ )} +
+ +
+ + setData('subject_phone', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.subject_phone && ( +

+ {errors.subject_phone} +

+ )} +
+ +
+ + setData('subject_email', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> + {errors.subject_email && ( +

+ {errors.subject_email} +

+ )} +
+ +
+ + setData('subject_dob', e.target.value)} + /> + {errors.subject_dob && ( +

+ {errors.subject_dob} +

+ )} +
+ +
+ + setData('subject_ssn', e.target.value)} + disabled={viewOnly || processing} + className="w-full" + /> +
+
+
+ + {/* Recipients */} +
+

+ to (check all that apply) +

+
+
+ {renderCheckbox( + 'disclose_to_attorney', + 'Attorney', + data.disclose_to_attorney, + viewOnly || processing, + )} + {renderConditionalField( + 'attorney_name', + 'disclose_to_attorney', + 'Name', + data.attorney_name, + viewOnly || processing, + )} + + {renderCheckbox( + 'disclose_to_employer', + 'Employer', + data.disclose_to_employer, + viewOnly || processing, + )} + {renderConditionalField( + 'employer_name', + 'disclose_to_employer', + 'Name', + data.employer_name, + viewOnly || processing, + )} +
+ +
+ {renderCheckbox( + 'disclose_to_legislator', + 'Legislator/staff', + data.disclose_to_legislator, + viewOnly || processing, + )} + {renderConditionalField( + 'legislator_name', + 'disclose_to_legislator', + 'Name', + data.legislator_name, + viewOnly || processing, + )} + + {renderCheckbox( + 'disclose_to_governors_staff', + "Governor's staff", + data.disclose_to_governors_staff, + viewOnly || processing, + )} + {renderConditionalField( + 'governors_staff_details', + 'disclose_to_governors_staff', + 'Name', + data.governors_staff_details, + viewOnly || processing, + )} +
+
+ +
+ {renderCheckbox( + 'disclose_to_other_recipient', + 'Other', + data.disclose_to_other_recipient, + viewOnly || processing, + )} + {data.disclose_to_other_recipient && ( +
+
+ + + setData('other_recipient_details', e.target.value) + } + disabled={viewOnly || processing} + className="w-full" + /> +
+ +
+ + + setData('other_recipient_address', e.target.value) + } + disabled={viewOnly || processing} + className="w-full" + /> +
+
+ )} +
+
+ + {/* Purpose of Disclosure */} +
+

+ The purpose of this disclosure is (check all that apply) +

+
+
+ {renderCheckbox( + 'purpose_eligibility_determination', + 'Eligibility determination', + data.purpose_eligibility_determination, + viewOnly || processing, + )} + {renderCheckbox( + 'purpose_employment', + 'Employment', + data.purpose_employment, + viewOnly || processing, + )} + {renderCheckbox( + 'purpose_continuity_of_services', + 'Continuity of services/care', + data.purpose_continuity_of_services, + viewOnly || processing, + )} +
+ +
+ {renderCheckbox( + 'purpose_legal_consultation', + 'Legal consultation/representation', + data.purpose_legal_consultation, + viewOnly || processing, + )} + {renderCheckbox( + 'purpose_complaint_investigation', + 'Complaint/investigation/resolution', + data.purpose_complaint_investigation, + viewOnly || processing, + )} + {renderCheckbox( + 'purpose_background_investigation', + 'Background investigation', + data.purpose_background_investigation, + viewOnly || processing, + )} +
+ +
+ {renderCheckbox( + 'purpose_legal_proceedings', + 'Legal proceedings', + data.purpose_legal_consultation, + viewOnly || processing, + )} + {renderCheckbox( + 'purpose_treatment_planning', + 'Treatment planning', + data.purpose_treatment_planning, + viewOnly || processing, + )} + {renderCheckbox( + 'purpose_consumer_request', + "At consumer's request", + data.purpose_consumer_request, + viewOnly || processing, + )} +
+
+ +
+ {renderCheckbox( + 'purpose_share_and_refer', + 'To share or refer my information to other Missouri state agencies (such as DMH, DHSS, DSS, DESE, etc.) to obtain services consistent with the', + data.purpose_share_and_refer, + viewOnly || processing, + )} + {data.purpose_share_and_refer && ( +
+ + + setData('share_and_refer_details', e.target.value) + } + disabled={viewOnly || processing} + className="w-full" + placeholder="program (please complete the name of the program in which you want to participate)" + /> +
+ )} +
+ +
+ {renderCheckbox( + 'purpose_other', + 'Other (specify)', + data.purpose_other, + viewOnly || processing, + )} + {renderConditionalField( + 'other_purpose_details', + 'purpose_other', + 'Details', + data.other_purpose_details, + viewOnly || processing, + )} +
+
+ + {/* Information to be Disclosed */} +
+

+ The specific information to be disclosed is (check all that + apply) +

+
+
+ {renderCheckbox( + 'disclose_entire_file', + 'Entire file', + data.disclose_entire_file, + viewOnly || processing, + )} + {renderCheckbox( + 'disclose_licensure_information', + 'Licensure information', + data.disclose_licensure_information, + viewOnly || processing, + )} + {renderCheckbox( + 'disclose_medical_psychiatric_records', + 'Medical/psychiatric evaluation/treatment records', + data.disclose_medical_psychiatric_records, + viewOnly || processing, + )} +
+ +
+ {renderCheckbox( + 'disclose_hotline_investigations', + 'Hotline investigations', + data.disclose_hotline_investigations, + viewOnly || processing, + )} + {renderCheckbox( + 'disclose_home_studies', + 'Home studies', + data.disclose_home_studies, + viewOnly || processing, + )} + {renderCheckbox( + 'disclose_client_employment_records', + 'Client employment records', + data.disclose_client_employment_records, + viewOnly || processing, + )} +
+ +
+ {renderCheckbox( + 'disclose_eligibility_determinations', + 'Eligibility determinations', + data.disclose_eligibility_determinations, + viewOnly || processing, + )} + {renderCheckbox( + 'disclose_substance_abuse_treatment', + 'Substance abuse treatment', + data.disclose_substance_abuse_treatment, + viewOnly || processing, + )} + {renderCheckbox( + 'disclose_benefits_received', + 'Benefits received', + data.disclose_benefits_received, + viewOnly || processing, + )} +
+
+ +
+ {renderCheckbox( + 'disclose_other_information', + 'Other', + data.disclose_other_information, + viewOnly || processing, + )} + {renderConditionalField( + 'other_disclosure_details', + 'disclose_other_information', + 'Details', + data.other_disclosure_details, + viewOnly || processing, + )} +
+
+ + {/* Terms and Authorization */} +
+
+
    +
  1. + READ CAREFULLY: I understand that my + information and records with the Department of Social + Services are confidential by law. I understand that by + signing this authorization, I am allowing the release of any + and all of my information and records which I am authorized + to receive as specified on this document whether past, + present or created in the future up to the expiration or + revocation date of this authorization, unless otherwise + authorized. The protected information in my records may + include medical treatment and/or evaluation information, + mental/behavioral health information, information relating + to sexually transmitted diseases, acquired immunodeficiency + syndrome (AIDS), human immunodeficiency virus (HIV), other + communicable or environmental diseases and conditions, + alcohol/drug abuse, application for and/or receipt of public + assistance benefits, alcohol/drug abuse information, and/or + information concerning child abuse and neglect. +
  2. +
  3. + This authorization includes both information presently + compiled and information to be compiled during your + association or dealings with the Department of Social + Services, during the specified time frame. +
  4. +
  5. + Unless otherwise indicated, this authorization becomes + effective on the date of signature below and will expire one + year from that date. +
  6. +
  7. + I understand that I have a right to revoke this + authorization at any time. I understand that if I revoke + this authorization I must do so IN WRITING{' '} + and present my written revocation to the Privacy Officer of + the Department of Social Services at P.O. Box 1527, MO + 65102. I further understand that actions already taken based + on this authorization, prior to revocation, will{' '} + NOT be affected. +
  8. +
  9. + I understand that I have the right to receive a copy of this + authorization upon request.{' '} + + A photographic copy of this authorization is as valid as + the original. + +
  10. +
  11. + I understand that authorizing the disclosure of this + information is voluntary. I can refuse to sign this + authorization. I need not sign this form in order to receive + services from the Department of Social Services. I + understand that I may request to inspect or request a copy + of information to be used or disclosed, as provided in 45 + CFR section 164.524. I understand that any disclosure of + information carries with it the potential for redisclosure + by the party receiving it and that the information may no + longer be protected by law once it is in the possession of + the receiving party. If I have questions about disclosure of + my information, I can contact the Privacy Officer of the + Department of Social Services, my caseworker or family + support eligibility specialist. +
  12. +
  13. + By signing this disclosure on paper or electronically, I am + giving the Family Support Division (FSD) permission to + deliver, or cause to be delivered, phone calls or text + messages to me regarding my case from an automated dialing + system at my primary number. The FSD does not use an + encryption system when sending text messages. Such + unencrypted systems are not secure and carry some level of + risk that text messages could be read by a third party. By + signing, I am affirming that I nevertheless prefer to + receive text messages from FSD and understand I do not have + to consent to this as part of my application and can opt out + of getting these calls or text messages by checking “No” in + the “Accept Text Messages” box below +
  14. +
+ +

+ My signature below acknowledges that I have read and + understood the text above, and authorize the release of my + confidential information. +

+
+
+ + {/* Signature Section */} +
+
+
+ + + setData('consumer_signature', e.target.value) + } + disabled={viewOnly || processing} + className="w-full" + /> + {errors.consumer_signature && ( +

+ {errors.consumer_signature} +

+ )} +
+ +
+ + setData('signature_date', e.target.value)} + /> + {errors.signature_date && ( +

+ {errors.signature_date} +

+ )} +
+
+ +
+
+ + + setData('witness_signature', e.target.value) + } + disabled={viewOnly || processing} + className="w-full" + /> + {errors.consumer_signature && ( +

+ {errors.witness_signature} +

+ )} +
+ +
+ + + setData('witness_signature_date', e.target.value) + } + /> + {errors.witness_signature_date && ( +

+ {errors.witness_signature_date} +

+ )} +
+
+ +
+
+
+ + + setData('guardian_signature', e.target.value) + } + disabled={viewOnly || processing} + className="w-full" + /> +
+ +
+ + +
+ + setData('accept_text_messages', Boolean(checked)) + } + disabled={viewOnly || processing} + /> + +
+ +
+ + setData('accept_text_messages', Boolean(!checked)) + } + disabled={viewOnly || processing} + /> + +
+
+ +
+ +
+
+
+
+ +
+

Surveys

+

+ Family Support Division would like to know what services + enrolled participants are seeking from our programs. In an + effort to capture this data Family Support Division is + administering a survey through Survey Monkey. Plase selet the + preferred mothod of survey delivery: +

+
+
+ {renderCheckbox( + 'survey_by_email', + 'Email', + data.survey_by_email, + viewOnly || processing, + )} +
+
+
+
+ {renderCheckbox( + 'survey_by_mail', + 'Address', + data.survey_by_mail, + viewOnly || processing, + )} +
+
+
+
+ {renderCheckbox( + 'survey_by_online', + 'Online', + data.survey_by_online, + viewOnly || processing, + )} +
+
+
+ +
+ +
+
+
+
+
+ ) +} + +export default DisclosureAuthorizationForm diff --git a/resources/js/Components/Intake/ServicePlanForm.tsx b/resources/js/Components/Intake/ServicePlanForm.tsx index 1430cbf5..a5392e95 100644 --- a/resources/js/Components/Intake/ServicePlanForm.tsx +++ b/resources/js/Components/Intake/ServicePlanForm.tsx @@ -1,745 +1,965 @@ -import React from 'react'; +import React from 'react' import { router, useForm } from '@inertiajs/react' import type { IntakeServicePlanForm } from '@/types/intake-serviceplan-form' import type { Participant } from '@/types/participant' -import { - Button, - Input, - Checkbox, - Label, -} from "@/Components/ui"; - +import { Button, Input, Checkbox, Label } from '@/Components/ui' interface ServicePlanFromProps { - participant: Participant - serviceplanForm?: IntakeServicePlanForm - viewOnly?: boolean - nextRoute?: string + participant: Participant + serviceplanForm?: IntakeServicePlanForm + viewOnly?: boolean + nextRoute?: string } // Define types for form data -interface ServicePlanFormDefinition extends Record { - participant_name: string, - client_number: string, - review_date: string, - parenting_skill_development_is_service_area: boolean, - effective_co_parenting_is_service_area: boolean, - employment_and_education_is_service_area: boolean, - child_support_is_service_area: boolean, - domestic_violence_is_service_area: boolean, - service_identified_by_participant: string, - goal: string, - custody_visitation_strategy: string, - custody_visitation_person_responsible: string, - custody_visitation_timeline: string, - custody_visitation_measure_of_success: string, - education_employment_strategy: string, - education_employment_person_responsible: string, - education_employment_timeline: string, - education_employment_measure_of_success: string, - housing_transportation_strategy: string, - housing_transportation_person_responsible: string, - housing_transportation_timeline: string, - housing_transportation_measure_of_success: string, - participant_signature: string, - participant_signature_date: string, - case_manager_signature: string, - case_manager_signature_date: string, - date_completed: string, - +interface ServicePlanFormDefinition + extends Record { + participant_name: string + client_number: string + review_date: string + parenting_skill_development_is_service_area: boolean + effective_co_parenting_is_service_area: boolean + employment_and_education_is_service_area: boolean + child_support_is_service_area: boolean + domestic_violence_is_service_area: boolean + service_identified_by_participant: string + goal: string + custody_visitation_strategy: string + custody_visitation_person_responsible: string + custody_visitation_timeline: string + custody_visitation_measure_of_success: string + education_employment_strategy: string + education_employment_person_responsible: string + education_employment_timeline: string + education_employment_measure_of_success: string + housing_transportation_strategy: string + housing_transportation_person_responsible: string + housing_transportation_timeline: string + housing_transportation_measure_of_success: string + participant_signature: string + participant_signature_date: string + case_manager_signature: string + case_manager_signature_date: string + date_completed: string } -export const ServicePlanForm: React.FC = ({ - participant, - serviceplanForm, - viewOnly = false, - nextRoute = 'intake.media-release.index' +export const ServicePlanForm: React.FC = ({ + participant, + serviceplanForm, + viewOnly = false, + nextRoute = 'intake.media-release.index', }) => { - const { data, setData, processing, errors, ...form } = useForm({ - participant_name: serviceplanForm?.participant_name ?? participant?.name ?? '', - client_number: serviceplanForm?.client_number ?? '', - review_date: serviceplanForm?.review_date ?? '', + const { data, setData, processing, errors, ...form } = + useForm({ + participant_name: + serviceplanForm?.participant_name ?? participant?.name ?? '', + client_number: serviceplanForm?.client_number ?? '', + review_date: serviceplanForm?.review_date ?? '', + + parenting_skill_development_is_service_area: + serviceplanForm?.parenting_skill_development_is_service_area ?? true, + effective_co_parenting_is_service_area: + serviceplanForm?.effective_co_parenting_is_service_area ?? true, + employment_and_education_is_service_area: + serviceplanForm?.employment_and_education_is_service_area ?? true, + child_support_is_service_area: + serviceplanForm?.employment_and_education_is_service_area ?? true, + domestic_violence_is_service_area: + serviceplanForm?.domestic_violence_is_service_area ?? false, - parenting_skill_development_is_service_area: serviceplanForm?.parenting_skill_development_is_service_area ?? true, - effective_co_parenting_is_service_area: serviceplanForm?.effective_co_parenting_is_service_area ?? true, - employment_and_education_is_service_area: serviceplanForm?.employment_and_education_is_service_area ?? true, - child_support_is_service_area: serviceplanForm?.employment_and_education_is_service_area ?? true, - domestic_violence_is_service_area: serviceplanForm?.domestic_violence_is_service_area ?? false, - - service_identified_by_participant: serviceplanForm?.service_identified_by_participant ?? '', - goal: serviceplanForm?.goal ?? '', - - custody_visitation_strategy: serviceplanForm?.custody_visitation_strategy ?? '', - custody_visitation_person_responsible: serviceplanForm?.custody_visitation_person_responsible ?? '', - custody_visitation_timeline: serviceplanForm?.custody_visitation_timeline ?? '', - custody_visitation_measure_of_success: serviceplanForm?.custody_visitation_measure_of_success ?? '', - education_employment_strategy: serviceplanForm?.education_employment_strategy ?? '', - education_employment_person_responsible: serviceplanForm?.education_employment_person_responsible ?? '', - education_employment_timeline: serviceplanForm?.education_employment_timeline ?? '', - education_employment_measure_of_success: serviceplanForm?.education_employment_measure_of_success ?? '', - housing_transportation_strategy: serviceplanForm?.housing_transportation_strategy ?? '', - housing_transportation_person_responsible: serviceplanForm?.housing_transportation_person_responsible ?? '', - housing_transportation_timeline: serviceplanForm?.housing_transportation_timeline ?? '', - housing_transportation_measure_of_success: serviceplanForm?.housing_transportation_measure_of_success ?? '', - participant_signature: serviceplanForm?.participant_signature ?? '', - participant_signature_date: serviceplanForm?.participant_signature_date ?? '', - case_manager_signature: serviceplanForm?.case_manager_signature ?? '', - case_manager_signature_date: serviceplanForm?.case_manager_signature_date ?? '', - date_completed: serviceplanForm?.date_completed ?? '', - }); + service_identified_by_participant: + serviceplanForm?.service_identified_by_participant ?? '', + goal: serviceplanForm?.goal ?? '', - const continueToNextStep = () => { - console.log("ServicePlanForm.continueToNextStep - nextRoute: " + nextRoute) - router.visit(route(nextRoute)) - } + custody_visitation_strategy: + serviceplanForm?.custody_visitation_strategy ?? '', + custody_visitation_person_responsible: + serviceplanForm?.custody_visitation_person_responsible ?? '', + custody_visitation_timeline: + serviceplanForm?.custody_visitation_timeline ?? '', + custody_visitation_measure_of_success: + serviceplanForm?.custody_visitation_measure_of_success ?? '', + education_employment_strategy: + serviceplanForm?.education_employment_strategy ?? '', + education_employment_person_responsible: + serviceplanForm?.education_employment_person_responsible ?? '', + education_employment_timeline: + serviceplanForm?.education_employment_timeline ?? '', + education_employment_measure_of_success: + serviceplanForm?.education_employment_measure_of_success ?? '', + housing_transportation_strategy: + serviceplanForm?.housing_transportation_strategy ?? '', + housing_transportation_person_responsible: + serviceplanForm?.housing_transportation_person_responsible ?? '', + housing_transportation_timeline: + serviceplanForm?.housing_transportation_timeline ?? '', + housing_transportation_measure_of_success: + serviceplanForm?.housing_transportation_measure_of_success ?? '', + participant_signature: serviceplanForm?.participant_signature ?? '', + participant_signature_date: + serviceplanForm?.participant_signature_date ?? '', + case_manager_signature: serviceplanForm?.case_manager_signature ?? '', + case_manager_signature_date: + serviceplanForm?.case_manager_signature_date ?? '', + date_completed: serviceplanForm?.date_completed ?? '', + }) - return ( -
-
{ - e.preventDefault() - console.log("ServicePlanForm.onSubmit") - if (viewOnly) { - continueToNextStep() - return - } + const continueToNextStep = () => { + console.log('ServicePlanForm.continueToNextStep - nextRoute: ' + nextRoute) + router.visit(route(nextRoute)) + } - if (serviceplanForm?.id) { - form.put( - route('intake.service-plan.update', [ - serviceplanForm.id, - ]), - { - onSuccess: () => { - continueToNextStep() - }, - }, - ) - } else { - form.post(route('intake.service-plan.store'), { - onSuccess: () => { - continueToNextStep() - }, - }) - } - }} - className="bg-white rounded-lg shadow-md p-6" - > -

- Individualized Service Plan -

- {/* Header Section */} -
-
- - setData('participant_name', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.participant_name &&
{errors.participant_name}
} -
+ return ( +
+ { + e.preventDefault() + console.log('ServicePlanForm.onSubmit') + if (viewOnly) { + continueToNextStep() + return + } -
-
- - setData('client_number', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.client_number &&
{errors.client_number}
} -
+ if (serviceplanForm?.id) { + form.put( + route('intake.service-plan.update', [serviceplanForm.id]), + { + onSuccess: () => { + continueToNextStep() + }, + }, + ) + } else { + form.post(route('intake.service-plan.store'), { + onSuccess: () => { + continueToNextStep() + }, + }) + } + }} + className="bg-white rounded-lg shadow-md p-6" + > +

+ Individualized Service Plan +

+ {/* Header Section */} +
+
+ + setData('participant_name', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.participant_name && ( +
+ {errors.participant_name} +
+ )} +
-
- - setData('review_date', e.target.value)} - disabled={viewOnly || processing} - /> -
-
-
+
+
+ + setData('client_number', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.client_number && ( +
+ {errors.client_number} +
+ )} +
- {/* Service Areas Section */} -
-

Service Areas:

-
-
- setData('parenting_skill_development_is_service_area', !!checked)} - disabled={viewOnly || processing} - /> - -
+
+ + setData('review_date', e.target.value)} + disabled={viewOnly || processing} + /> +
+
+
-
- setData('effective_co_parenting_is_service_area', !!checked)} - disabled={viewOnly || processing} - /> - -
+ {/* Service Areas Section */} +
+

Service Areas:

+
+
+ + setData( + 'parenting_skill_development_is_service_area', + !!checked, + ) + } + disabled={viewOnly || processing} + /> + +
-
- setData('employment_and_education_is_service_area', !!checked)} - disabled={viewOnly || processing} - /> - -
+
+ + setData('effective_co_parenting_is_service_area', !!checked) + } + disabled={viewOnly || processing} + /> + +
-
- setData('child_support_is_service_area', !!checked)} - disabled={viewOnly || processing} - /> - -
+
+ + setData('employment_and_education_is_service_area', !!checked) + } + disabled={viewOnly || processing} + /> + +
-
- setData('domestic_violence_is_service_area', !!checked)} - disabled={viewOnly || processing} - /> - -
-
-
+
+ + setData('child_support_is_service_area', !!checked) + } + disabled={viewOnly || processing} + /> + +
- {/* Service Identified & Goal Section */} -
-
- - setData('service_identified_by_participant', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.service_identified_by_participant &&
{errors.service_identified_by_participant}
} -
+
+ + setData('domestic_violence_is_service_area', !!checked) + } + disabled={viewOnly || processing} + /> + +
+
+
-
- - setData('goal', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.goal &&
{errors.goal}
} -
-
+ {/* Service Identified & Goal Section */} +
+
+ + + setData('service_identified_by_participant', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.service_identified_by_participant && ( +
+ {errors.service_identified_by_participant} +
+ )} +
- {/* Objectives Tables */} -
-

Objectives

+
+ + setData('goal', e.target.value)} + disabled={viewOnly || processing} + /> + {errors.goal && ( +
{errors.goal}
+ )} +
+
- {/* Parenting Skills & Stress Management Table */} -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
ObjectivesStrategies to Achieve ObjectivePerson ResponsibleTimelinesMeasure of Success
Parenting Skills Development - updateObjective('parenting_skills', 'strategies', e.target.value)} - disabled={true} - /> - - updateObjective('parenting_skills', 'person_responsible', e.target.value)} - disabled={true} - /> - - updateObjective('parenting_skills', 'timelines', e.target.value)} - disabled={true} - /> - - updateObjective('parenting_skills', 'measure_of_success', e.target.value)} - disabled={true} - /> -
Managing Stress and Anger - updateObjective('stress_and_anger', 'strategies', e.target.value)} - disabled={true} - /> - - updateObjective('stress_and_anger', 'person_responsible', e.target.value)} - disabled={true} - - /> - - updateObjective('stress_and_anger', 'timelines', e.target.value)} - disabled={true} - /> - - updateObjective('stress_and_anger', 'measure_of_success', e.target.value)} - disabled={true} - /> -
-
+ {/* Objectives Tables */} +
+

Objectives

- {/* Custody/Education/Housing Table */} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ObjectivesStrategies to Achieve ObjectivePerson ResponsibleTimelinesMeasure of Success
Custody/Visitation* - setData('custody_visitation_strategy', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.custody_visitation_strategy &&
{errors.custody_visitation_strategy}
} -
- setData('custody_visitation_person_responsible', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.custody_visitation_person_responsible &&
{errors.custody_visitation_person_responsible}
} -
- setData('custody_visitation_timeline', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.custody_visitation_timeline &&
{errors.custody_visitation_timeline}
} -
- setData('custody_visitation_measure_of_success', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.custody_visitation_measure_of_success &&
{errors.custody_visitation_measure_of_success}
} -
Education and or Employment* - setData('education_employment_strategy', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.education_employment_strategy &&
{errors.education_employment_strategy}
} -
- setData('education_employment_person_responsible', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.education_employment_person_responsible &&
{errors.education_employment_person_responsible}
} -
- setData('education_employment_timeline', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.education_employment_timeline &&
{errors.education_employment_timeline}
} -
- setData('education_employment_measure_of_success', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.education_employment_measure_of_success &&
{errors.education_employment_measure_of_success}
} -
Housing / Transportation* - setData('housing_transportation_strategy', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.housing_transportation_strategy &&
{errors.housing_transportation_strategy}
} -
- setData('housing_transportation_person_responsible', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.housing_transportation_person_responsible &&
{errors.housing_transportation_person_responsible}
} -
- setData('housing_transportation_timeline', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.housing_transportation_timeline &&
{errors.housing_transportation_timeline}
} -
- setData('housing_transportation_measure_of_success', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.housing_transportation_measure_of_success &&
{errors.housing_transportation_measure_of_success}
} -
-
+ {/* Parenting Skills & Stress Management Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Objectives + Strategies to Achieve Objective + Person ResponsibleTimelinesMeasure of Success
+ Parenting Skills Development + + updateObjective('parenting_skills', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('parenting_skills', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('parenting_skills', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('parenting_skills', 'measure_of_success', e.target.value)} + disabled={true} + /> +
+ Managing Stress and Anger + + updateObjective('stress_and_anger', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('stress_and_anger', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('stress_and_anger', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('stress_and_anger', 'measure_of_success', e.target.value)} + disabled={true} + /> +
+
- {/* Child Support Section */} -
-

Child Support Action Goal (Leave Blank)

-
+ {/* Custody/Education/Housing Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Objectives + Strategies to Achieve Objective + Person ResponsibleTimelinesMeasure of Success
+ Custody/Visitation* + + + setData('custody_visitation_strategy', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.custody_visitation_strategy && ( +
+ {errors.custody_visitation_strategy} +
+ )} +
+ + setData( + 'custody_visitation_person_responsible', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.custody_visitation_person_responsible && ( +
+ {errors.custody_visitation_person_responsible} +
+ )} +
+ + setData('custody_visitation_timeline', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.custody_visitation_timeline && ( +
+ {errors.custody_visitation_timeline} +
+ )} +
+ + setData( + 'custody_visitation_measure_of_success', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.custody_visitation_measure_of_success && ( +
+ {errors.custody_visitation_measure_of_success} +
+ )} +
+ Education and or Employment + * + + + setData('education_employment_strategy', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.education_employment_strategy && ( +
+ {errors.education_employment_strategy} +
+ )} +
+ + setData( + 'education_employment_person_responsible', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.education_employment_person_responsible && ( +
+ {errors.education_employment_person_responsible} +
+ )} +
+ + setData('education_employment_timeline', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.education_employment_timeline && ( +
+ {errors.education_employment_timeline} +
+ )} +
+ + setData( + 'education_employment_measure_of_success', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.education_employment_measure_of_success && ( +
+ {errors.education_employment_measure_of_success} +
+ )} +
+ Housing / Transportation + * + + + setData( + 'housing_transportation_strategy', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.housing_transportation_strategy && ( +
+ {errors.housing_transportation_strategy} +
+ )} +
+ + setData( + 'housing_transportation_person_responsible', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.housing_transportation_person_responsible && ( +
+ {errors.housing_transportation_person_responsible} +
+ )} +
+ + setData( + 'housing_transportation_timeline', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.housing_transportation_timeline && ( +
+ {errors.housing_transportation_timeline} +
+ )} +
+ + setData( + 'housing_transportation_measure_of_success', + e.target.value, + ) + } + disabled={viewOnly || processing} + /> + {errors.housing_transportation_measure_of_success && ( +
+ {errors.housing_transportation_measure_of_success} +
+ )} +
+
- {/* Child Support/Co-Parenting Table */} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ObjectivesStrategies to Achieve ObjectivePerson ResponsibleTimelinesMeasure of Success
Child Support Awareness and Information - updateObjective('child_support_awareness', 'strategies', e.target.value)} - disabled={true} - /> - - updateObjective('child_support_awareness', 'person_responsible', e.target.value)} - disabled={true} - /> - - updateObjective('child_support_awareness', 'timelines', e.target.value)} - disabled={true} - /> - - updateObjective('child_support_awareness', 'measure_of_success', e.target.value)} - disabled={true} - /> -
Effective Co-Parenting - updateObjective('co_parenting', 'strategies', e.target.value)} - disabled={true} - /> - - updateObjective('co_parenting', 'person_responsible', e.target.value)} - disabled={true} - /> - - updateObjective('co_parenting', 'timelines', e.target.value)} - disabled={true} - /> - - updateObjective('co_parenting', 'measure_of_success', e.target.value)} - disabled={true} - /> -
Father to Father Mentoring - updateObjective('mentoring', 'strategies', e.target.value)} - disabled={true} - /> - - updateObjective('mentoring', 'person_responsible', e.target.value)} - disabled={true} - /> - - updateObjective('mentoring', 'timelines', e.target.value)} - disabled={true} - /> - - updateObjective('mentoring', 'measure_of_success', e.target.value)} - disabled={true} - /> -
-
+ {/* Child Support Section */} +
+

+ Child Support Action Goal (Leave Blank) +

+
- {/* Signature Section */} -
-
- - setData('participant_signature', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.participant_signature &&
{errors.participant_signature}
} + {/* Child Support/Co-Parenting Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Objectives + Strategies to Achieve Objective + Person ResponsibleTimelinesMeasure of Success
+ Child Support Awareness and Information + + updateObjective('child_support_awareness', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('child_support_awareness', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('child_support_awareness', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('child_support_awareness', 'measure_of_success', e.target.value)} + disabled={true} + /> +
Effective Co-Parenting + updateObjective('co_parenting', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('co_parenting', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('co_parenting', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('co_parenting', 'measure_of_success', e.target.value)} + disabled={true} + /> +
+ Father to Father Mentoring + + updateObjective('mentoring', 'strategies', e.target.value)} + disabled={true} + /> + + updateObjective('mentoring', 'person_responsible', e.target.value)} + disabled={true} + /> + + updateObjective('mentoring', 'timelines', e.target.value)} + disabled={true} + /> + + updateObjective('mentoring', 'measure_of_success', e.target.value)} + disabled={true} + /> +
+
-
- - - setData('participant_signature_date', e.target.value) - } - /> - {errors.participant_signature_date &&
{errors.participant_signature_date}
} -
-
- -
- - setData('case_manager_signature', e.target.value)} - disabled={viewOnly || processing} - /> - {errors.case_manager_signature &&
{errors.case_manager_signature}
} -
- - - setData('case_manager_signature_date', e.target.value) - } - /> - {errors.case_manager_signature_date &&
{errors.case_manager_signature_date}
} -
-
-
+ {/* Signature Section */} +
+
+ + + setData('participant_signature', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.participant_signature && ( +
+ {errors.participant_signature} +
+ )} - {/* Submit Button */} -
- -
-
- -
- ); -}; +
+ + + setData('participant_signature_date', e.target.value) + } + /> + {errors.participant_signature_date && ( +
+ {errors.participant_signature_date} +
+ )} +
+
+ +
+ + + setData('case_manager_signature', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.case_manager_signature && ( +
+ {errors.case_manager_signature} +
+ )} +
+ + + setData('case_manager_signature_date', e.target.value) + } + /> + {errors.case_manager_signature_date && ( +
+ {errors.case_manager_signature_date} +
+ )} +
+
+
+ + {/* Submit Button */} +
+ +
+
+ +
+ ) +} -export default ServicePlanForm; \ No newline at end of file +export default ServicePlanForm diff --git a/resources/js/Pages/Auth/Login.tsx b/resources/js/Pages/Auth/Login.tsx index a032e84a..ac5dddfe 100644 --- a/resources/js/Pages/Auth/Login.tsx +++ b/resources/js/Pages/Auth/Login.tsx @@ -16,11 +16,12 @@ export default function Login({ status?: string canResetPassword: boolean }) { - const { data, setData, post, processing, errors, reset } = useForm({ - email: '', - password: '', - remember: false, - }) + const { data, setData, post, processing, errors, reset } = + useForm({ + email: '', + password: '', + remember: false, + }) useEffect(() => { return () => { diff --git a/resources/js/Pages/Intake/Disclosure/Create.tsx b/resources/js/Pages/Intake/Disclosure/Create.tsx index 29fd6aaf..0bc5306b 100644 --- a/resources/js/Pages/Intake/Disclosure/Create.tsx +++ b/resources/js/Pages/Intake/Disclosure/Create.tsx @@ -10,8 +10,14 @@ interface DisclosureProps extends PageProps { export const Create: React.FC = ({ participant }) => { return ( - - + + ) } diff --git a/resources/js/Pages/Intake/Disclosure/Index.tsx b/resources/js/Pages/Intake/Disclosure/Index.tsx index b676f9d1..5145fa43 100644 --- a/resources/js/Pages/Intake/Disclosure/Index.tsx +++ b/resources/js/Pages/Intake/Disclosure/Index.tsx @@ -11,13 +11,13 @@ import IntakeLayout from '@/Layouts/IntakeLayout' import dayjs from 'dayjs' interface DisclosureProps extends PageProps { - participant: Participant - disclosureAuthorizations: IntakeDisclosureAuthorizationForm[] + participant: Participant + disclosureAuthorizations: IntakeDisclosureAuthorizationForm[] } export const Index: React.FC = ({ participant, - disclosureAuthorizations + disclosureAuthorizations, }) => { if (disclosureAuthorizations.length === 0) { router.visit(route('intake.disclosure.create')) @@ -43,7 +43,10 @@ export const Index: React.FC = ({ -
-
- - - ) -} + {/* Submit Button */} +
+ +
+ + + + ); +}; -export default ServicePlanForm +export default ServicePlanForm; \ No newline at end of file diff --git a/resources/js/Pages/Intake/Disclosure/Index.tsx b/resources/js/Pages/Intake/Disclosure/Index.tsx index 5145fa43..3ff7ef73 100644 --- a/resources/js/Pages/Intake/Disclosure/Index.tsx +++ b/resources/js/Pages/Intake/Disclosure/Index.tsx @@ -1,14 +1,11 @@ import React from 'react' -// import { Head } from '@inertiajs/react' import { type PageProps } from '@/types' -// import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import type { Participant } from '@/types/participant' import type { IntakeDisclosureAuthorizationForm } from '@/types/intake-disclosure-authorization-form' import { Button } from '@/Components/ui' import { router } from '@inertiajs/react' import { clsx } from 'clsx' import IntakeLayout from '@/Layouts/IntakeLayout' -import dayjs from 'dayjs' interface DisclosureProps extends PageProps { participant: Participant @@ -72,13 +69,14 @@ export const Index: React.FC = ({ - - - - - ); -}; +
+ + + setData('participant_signature_date', e.target.value) + } + /> + {errors.participant_signature_date && ( +
+ {errors.participant_signature_date} +
+ )} +
+ + +
+ + + setData('case_manager_signature', e.target.value) + } + disabled={viewOnly || processing} + /> + {errors.case_manager_signature && ( +
+ {errors.case_manager_signature} +
+ )} +
+ + + setData('case_manager_signature_date', e.target.value) + } + /> + {errors.case_manager_signature_date && ( +
+ {errors.case_manager_signature_date} +
+ )} +
+
+ + + {/* Submit Button */} +
+ +
+ + + + ) +} -export default ServicePlanForm; \ No newline at end of file +export default ServicePlanForm diff --git a/resources/js/Pages/Intake/Disclosure/Create.tsx b/resources/js/Pages/Intake/Disclosure/Create.tsx index 0bc5306b..9bc430ce 100644 --- a/resources/js/Pages/Intake/Disclosure/Create.tsx +++ b/resources/js/Pages/Intake/Disclosure/Create.tsx @@ -1,14 +1,13 @@ import React from 'react' -import { type PageProps } from '@/types' -import { Participant } from '@/types/participant' +import { PageProps, ParticipantData } from '@/types' import IntakeLayout from '@/Layouts/IntakeLayout' import DisclosureAuthorizationForm from '@/Components/Intake/DisclosureAuthorizationForm' interface DisclosureProps extends PageProps { - participant: Participant + participant: ParticipantData } -export const Create: React.FC = ({ participant }) => { +export const Create = ({ participant }: DisclosureProps) => { return ( = ({ auth }) => { +export const Edit = ({ auth }: DisclosureProps) => { return ( diff --git a/resources/js/Pages/Intake/Disclosure/Index.tsx b/resources/js/Pages/Intake/Disclosure/Index.tsx index 3ff7ef73..b262aee2 100644 --- a/resources/js/Pages/Intake/Disclosure/Index.tsx +++ b/resources/js/Pages/Intake/Disclosure/Index.tsx @@ -1,28 +1,27 @@ import React from 'react' -import { type PageProps } from '@/types' -import type { Participant } from '@/types/participant' +import { PageProps, ParticipantData } from '@/types' import type { IntakeDisclosureAuthorizationForm } from '@/types/intake-disclosure-authorization-form' import { Button } from '@/Components/ui' import { router } from '@inertiajs/react' -import { clsx } from 'clsx' import IntakeLayout from '@/Layouts/IntakeLayout' +import { cn } from '@/lib/utils' interface DisclosureProps extends PageProps { - participant: Participant + participant: ParticipantData disclosureAuthorizations: IntakeDisclosureAuthorizationForm[] } -export const Index: React.FC = ({ +export const Index = ({ participant, disclosureAuthorizations, -}) => { +}: DisclosureProps) => { if (disclosureAuthorizations.length === 0) { router.visit(route('intake.disclosure.create')) } return (
Signed Date
@@ -30,13 +29,13 @@ export const Index: React.FC = ({
Actions
{disclosureAuthorizations.map((disclosureAuthentication, index) => ( -
+
{participant.name}
-
+
{/* {dayjs(disclosureAuthentication.created_at).format('MM/DD/YYYY')} */}
-
+
- {childrenInfo.map((child, index) => ( + {children?.map((child, index) => (
@@ -47,8 +48,8 @@ const ChildrenTable = React.forwardRef( } /> ( } /> ( } /> ( />
@@ -121,24 +120,22 @@ const ChildrenTable = React.forwardRef( />
handleInputChange(index, 'phoneContact', isChecked) } /> - + (
- ( } /> { - consumer_name: string - is_dss_authorized: boolean - is_fsd_authorized: boolean - is_dys_authorized: boolean - is_cd_authorized: boolean - is_mhd_authorized: boolean - is_dls_authorized: boolean - is_dfas_authorized: boolean - is_mmac_authorized: boolean - is_other_authorized: boolean - other_authorized_entity: string - - subject_name: string - subject_phone: string - subject_dob: string - subject_ssn: string - subject_address: string - subject_email: string - - disclose_to_attorney: boolean - attorney_name: string - disclose_to_employer: boolean - employer_name: string - disclose_to_legislator: boolean - legislator_name: string - disclose_to_governors_staff: boolean - governors_staff_details: string - disclose_to_other_recipient: boolean - other_recipient_name: string - other_recipient_address: string - - purpose_eligibility_determination: boolean - purpose_legal_consultation: boolean - purpose_legal_proceedings: boolean - purpose_employment: boolean - purpose_complaint_investigation: boolean - purpose_treatment_planning: boolean - purpose_continuity_of_services: boolean - purpose_background_investigation: boolean - purpose_consumer_request: boolean - purpose_share_and_refer: boolean - share_and_refer_details: string - purpose_other: boolean - other_purpose_details: string - - disclose_entire_file: boolean - disclose_licensure_information: boolean - disclose_medical_psychiatric_records: boolean - disclose_hotline_investigations: boolean - disclose_home_studies: boolean - disclose_eligibility_determinations: boolean - disclose_substance_abuse_treatment: boolean - disclose_client_employment_records: boolean - disclose_benefits_received: boolean - disclose_other_information: boolean - other_disclosure_details: string - - accept_text_messages: boolean - consumer_signature: string - signature_date: string - witness_signature: string - witness_signature_date: string - guardian_signature: string - guardian_authority: string - - survey_by_email: boolean - survey_by_mail: boolean - survey_by_online: boolean -} - export const DisclosureAuthorizationForm: React.FC< DisclosureAuthorizationFormProps > = ({ @@ -101,133 +35,80 @@ export const DisclosureAuthorizationForm: React.FC< nextRoute = 'intake.disclosure.store', }) => { const { data, setData, processing, errors, ...form } = - useForm({ - consumer_name: - disclosureAuthorizationForm?.consumer_name ?? participant?.name ?? '', - is_dss_authorized: disclosureAuthorizationForm?.is_dss_authorized ?? true, - is_fsd_authorized: disclosureAuthorizationForm?.is_fsd_authorized ?? true, - is_dys_authorized: - disclosureAuthorizationForm?.is_dys_authorized ?? false, - is_cd_authorized: disclosureAuthorizationForm?.is_cd_authorized ?? false, - is_mhd_authorized: - disclosureAuthorizationForm?.is_mhd_authorized ?? false, - is_dls_authorized: - disclosureAuthorizationForm?.is_dls_authorized ?? false, - is_dfas_authorized: - disclosureAuthorizationForm?.is_dfas_authorized ?? false, - is_mmac_authorized: - disclosureAuthorizationForm?.is_mmac_authorized ?? false, - is_other_authorized: - disclosureAuthorizationForm?.is_other_authorized ?? false, - other_authorized_entity: - disclosureAuthorizationForm?.other_authorized_entity ?? '', - - subject_name: - disclosureAuthorizationForm?.subject_name ?? participant.name ?? '', - subject_phone: - disclosureAuthorizationForm?.subject_phone ?? + useForm({ + id: disclosureAuthorizationForm?.id ?? '', + + // Consumer name is pulled from participant name in the backend + consumerName: disclosureAuthorizationForm?.consumerName ?? participant?.name ?? null, + + isDssAuthorized: disclosureAuthorizationForm?.isDssAuthorized ?? true, + isDysAuthorized: disclosureAuthorizationForm?.isDysAuthorized ?? false, + isMhdAuthorized: disclosureAuthorizationForm?.isMhdAuthorized ?? false, + isDfasAuthorized: disclosureAuthorizationForm?.isDfasAuthorized ?? false, + isMmacAuthorized: disclosureAuthorizationForm?.isMmacAuthorized ?? false, + isFsdAuthorized: disclosureAuthorizationForm?.isFsdAuthorized ?? true, + isCdAuthorized: disclosureAuthorizationForm?.isCdAuthorized ?? false, + isDlsAuthorized: disclosureAuthorizationForm?.isDlsAuthorized ?? false, + isOtherAuthorized: disclosureAuthorizationForm?.isOtherAuthorized ?? false, + otherAuthorizedEntity: disclosureAuthorizationForm?.otherAuthorizedEntity ?? null, + + subjectName: + disclosureAuthorizationForm?.subjectName ?? participant.name ?? null, + subjectPhone: + disclosureAuthorizationForm?.subjectPhone ?? participant.homePhoneNumber ?? - '', - subject_dob: disclosureAuthorizationForm?.subject_dob ?? '', - subject_ssn: disclosureAuthorizationForm?.subject_ssn ?? '', - subject_address: - disclosureAuthorizationForm?.subject_address ?? + null, + subjectDob: disclosureAuthorizationForm?.subjectDob ?? null, + subjectSsn: disclosureAuthorizationForm?.subjectSsn ?? null, + subjectAddress: + disclosureAuthorizationForm?.subjectAddress ?? participant.addressLine1 ?? - '', - subject_email: - disclosureAuthorizationForm?.subject_email ?? + null, + subjectEmail: + disclosureAuthorizationForm?.subjectEmail ?? participant.user.email ?? - '', - - disclose_to_attorney: - disclosureAuthorizationForm?.disclose_to_attorney ?? false, - attorney_name: disclosureAuthorizationForm?.attorney_name ?? '', - disclose_to_employer: - disclosureAuthorizationForm?.disclose_to_employer ?? false, - employer_name: disclosureAuthorizationForm?.employer_name ?? '', - disclose_to_legislator: - disclosureAuthorizationForm?.disclose_to_legislator ?? false, - legislator_name: disclosureAuthorizationForm?.legislator_name ?? '', - disclose_to_governors_staff: - disclosureAuthorizationForm?.disclose_to_governors_staff ?? false, - governors_staff_details: - disclosureAuthorizationForm?.governors_staff_details ?? '', - disclose_to_other_recipient: - disclosureAuthorizationForm?.disclose_to_other_recipient ?? true, - other_recipient_name: - disclosureAuthorizationForm?.other_recipient_name ?? - 'Good Dads/Jennifer Baker (and staff)', - other_recipient_address: - disclosureAuthorizationForm?.other_recipient_address ?? - '205 W. Walnut Street, Ste. 10, Springfield, MO 65806', - - purpose_eligibility_determination: - disclosureAuthorizationForm?.purpose_eligibility_determination ?? false, - purpose_legal_consultation: - disclosureAuthorizationForm?.purpose_legal_consultation ?? false, - purpose_legal_proceedings: - disclosureAuthorizationForm?.purpose_legal_proceedings ?? false, - purpose_employment: - disclosureAuthorizationForm?.purpose_employment ?? false, - purpose_complaint_investigation: - disclosureAuthorizationForm?.purpose_complaint_investigation ?? false, - purpose_treatment_planning: - disclosureAuthorizationForm?.purpose_treatment_planning ?? false, - purpose_continuity_of_services: - disclosureAuthorizationForm?.purpose_continuity_of_services ?? false, - purpose_background_investigation: - disclosureAuthorizationForm?.purpose_background_investigation ?? false, - purpose_consumer_request: - disclosureAuthorizationForm?.purpose_consumer_request ?? false, - purpose_share_and_refer: - disclosureAuthorizationForm?.purpose_share_and_refer ?? true, - share_and_refer_details: - disclosureAuthorizationForm?.share_and_refer_details ?? 'Good Dads', - purpose_other: disclosureAuthorizationForm?.purpose_other ?? false, - other_purpose_details: - disclosureAuthorizationForm?.other_purpose_details ?? '', - - disclose_entire_file: - disclosureAuthorizationForm?.disclose_entire_file ?? true, - disclose_licensure_information: - disclosureAuthorizationForm?.disclose_licensure_information ?? false, - disclose_medical_psychiatric_records: - disclosureAuthorizationForm?.disclose_medical_psychiatric_records ?? - false, - disclose_hotline_investigations: - disclosureAuthorizationForm?.disclose_hotline_investigations ?? false, - disclose_home_studies: - disclosureAuthorizationForm?.disclose_home_studies ?? false, - disclose_eligibility_determinations: - disclosureAuthorizationForm?.disclose_eligibility_determinations ?? - false, - disclose_substance_abuse_treatment: - disclosureAuthorizationForm?.disclose_substance_abuse_treatment ?? - false, - disclose_client_employment_records: - disclosureAuthorizationForm?.disclose_client_employment_records ?? - false, - disclose_benefits_received: - disclosureAuthorizationForm?.disclose_benefits_received ?? true, - disclose_other_information: - disclosureAuthorizationForm?.disclose_other_information ?? true, - other_disclosure_details: - disclosureAuthorizationForm?.other_disclosure_details ?? + null, + + discloseToAttorney: + disclosureAuthorizationForm?.discloseToAttorney ?? false, + attorneyName: disclosureAuthorizationForm?.attorneyName ?? null, + discloseToEmployer: + disclosureAuthorizationForm?.discloseToEmployer ?? false, + employerName: disclosureAuthorizationForm?.employerName ?? null, + discloseToLegislator: + disclosureAuthorizationForm?.discloseToLegislator ?? false, + legislatorName: disclosureAuthorizationForm?.legislatorName ?? null, + discloseToGovernorsStaff: + disclosureAuthorizationForm?.discloseToGovernorsStaff ?? false, + otherRecipientDetails: disclosureAuthorizationForm?.otherRecipientDetails ?? 'Good Dads/Jennifer Baker (and staff)', + + // Purpose of disclosure - using array of enum values + purposes: disclosureAuthorizationForm?.purposes ?? [DisclosurePurposeType.SHARE_AND_REFER], + otherPurposeDetails: disclosureAuthorizationForm?.otherPurposeDetails ?? null, + + // Information to be disclosed - using array of enum values + contentTypes: disclosureAuthorizationForm?.contentTypes ?? [ + DisclosureContentType.ENTIRE_FILE, + DisclosureContentType.BENEFITS_RECEIVED, + DisclosureContentType.OTHER + ], + otherDisclosureDetails: + disclosureAuthorizationForm?.otherDisclosureDetails ?? 'Child support records that FSD may release to the parent from his/her own case file.', - accept_text_messages: - disclosureAuthorizationForm?.accept_text_messages ?? false, - consumer_signature: disclosureAuthorizationForm?.consumer_signature ?? '', - signature_date: disclosureAuthorizationForm?.signature_date ?? '', - witness_signature: disclosureAuthorizationForm?.witness_signature ?? '', - witness_signature_date: - disclosureAuthorizationForm?.witness_signature_date ?? '', - guardian_signature: disclosureAuthorizationForm?.guardian_signature ?? '', - guardian_authority: disclosureAuthorizationForm?.guardian_authority ?? '', - - survey_by_email: disclosureAuthorizationForm?.survey_by_email ?? false, - survey_by_mail: disclosureAuthorizationForm?.survey_by_mail ?? false, - survey_by_online: disclosureAuthorizationForm?.survey_by_online ?? false, + acceptTextMessages: + disclosureAuthorizationForm?.acceptTextMessages ?? false, + consumerSignature: disclosureAuthorizationForm?.consumerSignature ?? null, + signatureDate: disclosureAuthorizationForm?.signatureDate ?? null, + witnessSignature: disclosureAuthorizationForm?.witnessSignature ?? null, + witnessSignatureDate: + disclosureAuthorizationForm?.witnessSignatureDate ?? null, + guardianSignature: disclosureAuthorizationForm?.guardianSignature ?? null, + guardianSignatureDate: disclosureAuthorizationForm?.guardianSignatureDate ?? null, + + surveyByEmail: disclosureAuthorizationForm?.surveyByEmail ?? false, + surveyByMail: disclosureAuthorizationForm?.surveyByMail ?? false, + surveyByOnline: disclosureAuthorizationForm?.surveyByOnline ?? false, }) const continueToNextStep = () => { @@ -235,921 +116,831 @@ export const DisclosureAuthorizationForm: React.FC< } const renderCheckbox = ( - id: string, + id: keyof ParticipantDisclosureAuthorizationForm, label: string, - is_checked: boolean, + is_checked: boolean | null, is_view_only: boolean, ) => (
setData(id, !!checked)} disabled={is_view_only} /> -
) + + const renderPurposeCheckbox = ( + purposeType: DisclosurePurposeType, + label: string, + is_view_only: boolean, + ) => { + const isChecked = data.purposes.includes(purposeType); + + return ( +
+ { + const purposes = [...data.purposes]; + if (checked) { + if (!purposes.includes(purposeType)) { + purposes.push(purposeType); + } + } else { + const index = purposes.indexOf(purposeType); + if (index > -1) { + purposes.splice(index, 1); + } + } + setData('purposes', purposes); + }} + disabled={is_view_only} + /> + +
+ ) + } + + const renderContentTypeCheckbox = ( + contentType: DisclosureContentType, + label: string, + is_view_only: boolean, + ) => { + const isChecked = data.contentTypes.includes(contentType); + + return ( +
+ { + const contentTypes = [...data.contentTypes]; + if (checked) { + if (!contentTypes.includes(contentType)) { + contentTypes.push(contentType); + } + } else { + const index = contentTypes.indexOf(contentType); + if (index > -1) { + contentTypes.splice(index, 1); + } + } + setData('contentTypes', contentTypes); + }} + disabled={is_view_only} + /> + +
+ ) + } const renderConditionalField = ( - id: string, - checkbox_id: string, + id: keyof ParticipantDisclosureAuthorizationForm, + checkbox_id: keyof ParticipantDisclosureAuthorizationForm, label: string, - content: string, + content: string | null, is_view_only: boolean, required = false, ) => ( -
- - setData(id, e.target.value)} - disabled={is_view_only} - className="w-full" - /> - {errors[id] &&

{errors[id]}

} +
+
+ + setData(id, e.target.value)} + className={errors[id] ? 'border-red-500' : ''} + /> + {errors[id] && ( +

{errors[id]}

+ )} +
) - - return ( -
- - - - AUTHORIZATION FOR DISCLOSURE OF CONFIDENTIAL INFORMATION - - - -
{ - e.preventDefault() - if (viewOnly) { - continueToNextStep() - return - } - - if (disclosureAuthorizationForm?.id) { - form.put(route('intake.disclosure.update'), { - onSuccess: () => { - continueToNextStep() - }, - }) - } else { - form.post(route('intake.disclosure.store'), { - onSuccess: () => { - continueToNextStep() - }, - }) - } - }} + + const renderEnumConditionalField = ( + id: keyof ParticipantDisclosureAuthorizationForm, + enumValue: string, + enumArray: keyof ParticipantDisclosureAuthorizationForm, + label: string, + content: string | null, + is_view_only: boolean, + required = false, + ) => { + const isChecked = (data[enumArray] as string[]).includes(enumValue); + + return ( +
+
+
+ ) + } - {/* Authorized Entities */} -
-

- Check all that apply: -

-
-
- {renderCheckbox( - 'is_dss_authorized', - 'Department of Social Services (DSS)', - data.is_dss_authorized, - viewOnly || processing, - )} - {renderCheckbox( - 'is_dys_authorized', - 'Division of Youth Services (DYS)', - data.is_dys_authorized, - viewOnly || processing, - )} - {renderCheckbox( - 'is_mhd_authorized', - 'MO HealthNet Division (MHD)', - data.is_mhd_authorized, - viewOnly || processing, - )} - {renderCheckbox( - 'is_dfas_authorized', - 'Division of Finance & Administrative Services (DFAS)', - data.is_dfas_authorized, - viewOnly || processing, - )} -
-
- {renderCheckbox( - 'is_fsd_authorized', - 'Family Support Division (FSD)', - data.is_fsd_authorized, - viewOnly || processing, - )} - {renderCheckbox( - 'is_cd_authorized', - "Children's Division (CD)", - data.is_cd_authorized, - viewOnly || processing, - )} - {renderCheckbox( - 'is_dls_authorized', - 'Division of Legal Services (DLS)', - data.is_dls_authorized, - viewOnly || processing, - )} - {renderCheckbox( - 'is_mmac_authorized', - 'Missouri Medicaid Audit and Compliance (MMAC)', - data.is_mmac_authorized, - viewOnly || processing, - )} -
-
+ const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (viewOnly) { + return + } -
- {renderCheckbox( - 'is_other_authorized', - 'Other', - data.is_other_authorized, - viewOnly || processing, - )} - {renderConditionalField( - 'other_authorized_entity', - 'is_other_authorized', - 'Name of facility, agency, mental health center, person', - data.other_authorized_entity, - viewOnly || processing, - true, - )} -
-
-
+ form.submit('post', route(nextRoute, { absolute: false })) + } - {/* Subject Information */} -
-

- to disclose/release the below specified information of: + return ( + + + + Consumer Name + + +

+ + setData('consumerName', e.target.value)} + className={errors.consumerName ? 'border-red-500' : ''} + /> + {errors.consumerName && ( +

+ {errors.consumerName}

-
-
- - setData('subject_name', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.subject_name && ( -

- {errors.subject_name} -

- )} -
- -
- - setData('subject_phone', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.subject_phone && ( -

- {errors.subject_phone} -

- )} -
- -
- - setData('subject_email', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> - {errors.subject_email && ( -

- {errors.subject_email} -

- )} -
- -
- - setData('subject_dob', e.target.value)} - /> - {errors.subject_dob && ( -

- {errors.subject_dob} -

- )} -
+ )} +
+ + -
- - setData('subject_ssn', e.target.value)} - disabled={viewOnly || processing} - className="w-full" - /> -
-
+ + + Authorized Entities + + +
+ {renderCheckbox( + 'isDssAuthorized', + 'Department of Social Services (DSS)', + data.isDssAuthorized, + viewOnly, + )} + {renderCheckbox( + 'isFsdAuthorized', + 'Family Support Division (FSD)', + data.isFsdAuthorized, + viewOnly, + )} + {renderCheckbox( + 'isDysAuthorized', + 'Division of Youth Services (DYS)', + data.isDysAuthorized, + viewOnly, + )} + {renderCheckbox( + 'isCdAuthorized', + "Children's Division (CD)", + data.isCdAuthorized, + viewOnly, + )} + {renderCheckbox( + 'isMhdAuthorized', + 'Department of Mental Health (MHD)', + data.isMhdAuthorized, + viewOnly, + )} +
+
+ {renderCheckbox( + 'isDlsAuthorized', + 'Division of Legal Services (DLS)', + data.isDlsAuthorized, + viewOnly, + )} + {renderCheckbox( + 'isDfasAuthorized', + 'Division of Finance and Administrative Services (DFAS)', + data.isDfasAuthorized, + viewOnly, + )} + {renderCheckbox( + 'isMmacAuthorized', + 'Missouri Medicaid Audit and Compliance (MMAC)', + data.isMmacAuthorized, + viewOnly, + )} +
+ {renderCheckbox( + 'isOtherAuthorized', + 'Other (specify)', + data.isOtherAuthorized, + viewOnly, + )} + {renderConditionalField( + 'otherAuthorizedEntity', + 'isOtherAuthorized', + 'Other Entity', + data.otherAuthorizedEntity, + viewOnly, + true, + )}
+
+
+
- {/* Recipients */} -
-

- to (check all that apply) -

-
-
- {renderCheckbox( - 'disclose_to_attorney', - 'Attorney', - data.disclose_to_attorney, - viewOnly || processing, - )} - {renderConditionalField( - 'attorney_name', - 'disclose_to_attorney', - 'Name', - data.attorney_name, - viewOnly || processing, - )} - - {renderCheckbox( - 'disclose_to_employer', - 'Employer', - data.disclose_to_employer, - viewOnly || processing, - )} - {renderConditionalField( - 'employer_name', - 'disclose_to_employer', - 'Name', - data.employer_name, - viewOnly || processing, - )} -
- -
- {renderCheckbox( - 'disclose_to_legislator', - 'Legislator/staff', - data.disclose_to_legislator, - viewOnly || processing, - )} - {renderConditionalField( - 'legislator_name', - 'disclose_to_legislator', - 'Name', - data.legislator_name, - viewOnly || processing, - )} - - {renderCheckbox( - 'disclose_to_governors_staff', - "Governor's staff", - data.disclose_to_governors_staff, - viewOnly || processing, - )} - {renderConditionalField( - 'governors_staff_details', - 'disclose_to_governors_staff', - 'Name', - data.governors_staff_details, - viewOnly || processing, - )} -
-
- -
- {renderCheckbox( - 'disclose_to_other_recipient', - 'Other', - data.disclose_to_other_recipient, - viewOnly || processing, - )} - {data.disclose_to_other_recipient && ( -
-
- - - setData('other_recipient_details', e.target.value) - } - disabled={viewOnly || processing} - className="w-full" - /> -
- -
- - - setData('other_recipient_address', e.target.value) - } - disabled={viewOnly || processing} - className="w-full" - /> -
-
- )} -
+ + + Subject Information + + +
+
+ + setData('subjectName', e.target.value)} + className={errors.subjectName ? 'border-red-500' : ''} + /> + {errors.subjectName && ( +

+ {errors.subjectName} +

+ )}
- - {/* Purpose of Disclosure */} -
-

- The purpose of this disclosure is (check all that apply) -

-
-
- {renderCheckbox( - 'purpose_eligibility_determination', - 'Eligibility determination', - data.purpose_eligibility_determination, - viewOnly || processing, - )} - {renderCheckbox( - 'purpose_employment', - 'Employment', - data.purpose_employment, - viewOnly || processing, - )} - {renderCheckbox( - 'purpose_continuity_of_services', - 'Continuity of services/care', - data.purpose_continuity_of_services, - viewOnly || processing, - )} -
- -
- {renderCheckbox( - 'purpose_legal_consultation', - 'Legal consultation/representation', - data.purpose_legal_consultation, - viewOnly || processing, - )} - {renderCheckbox( - 'purpose_complaint_investigation', - 'Complaint/investigation/resolution', - data.purpose_complaint_investigation, - viewOnly || processing, - )} - {renderCheckbox( - 'purpose_background_investigation', - 'Background investigation', - data.purpose_background_investigation, - viewOnly || processing, - )} -
- -
- {renderCheckbox( - 'purpose_legal_proceedings', - 'Legal proceedings', - data.purpose_legal_consultation, - viewOnly || processing, - )} - {renderCheckbox( - 'purpose_treatment_planning', - 'Treatment planning', - data.purpose_treatment_planning, - viewOnly || processing, - )} - {renderCheckbox( - 'purpose_consumer_request', - "At consumer's request", - data.purpose_consumer_request, - viewOnly || processing, - )} -
-
- -
- {renderCheckbox( - 'purpose_share_and_refer', - 'To share or refer my information to other Missouri state agencies (such as DMH, DHSS, DSS, DESE, etc.) to obtain services consistent with the', - data.purpose_share_and_refer, - viewOnly || processing, - )} - {data.purpose_share_and_refer && ( -
- - - setData('share_and_refer_details', e.target.value) - } - disabled={viewOnly || processing} - className="w-full" - placeholder="program (please complete the name of the program in which you want to participate)" - /> -
- )} -
- -
- {renderCheckbox( - 'purpose_other', - 'Other (specify)', - data.purpose_other, - viewOnly || processing, - )} - {renderConditionalField( - 'other_purpose_details', - 'purpose_other', - 'Details', - data.other_purpose_details, - viewOnly || processing, - )} -
+
+ + setData('subjectPhone', e.target.value)} + className={errors.subjectPhone ? 'border-red-500' : ''} + /> + {errors.subjectPhone && ( +

+ {errors.subjectPhone} +

+ )}
- - {/* Information to be Disclosed */} -
-

- The specific information to be disclosed is (check all that - apply) -

-
-
- {renderCheckbox( - 'disclose_entire_file', - 'Entire file', - data.disclose_entire_file, - viewOnly || processing, - )} - {renderCheckbox( - 'disclose_licensure_information', - 'Licensure information', - data.disclose_licensure_information, - viewOnly || processing, - )} - {renderCheckbox( - 'disclose_medical_psychiatric_records', - 'Medical/psychiatric evaluation/treatment records', - data.disclose_medical_psychiatric_records, - viewOnly || processing, - )} -
- -
- {renderCheckbox( - 'disclose_hotline_investigations', - 'Hotline investigations', - data.disclose_hotline_investigations, - viewOnly || processing, - )} - {renderCheckbox( - 'disclose_home_studies', - 'Home studies', - data.disclose_home_studies, - viewOnly || processing, - )} - {renderCheckbox( - 'disclose_client_employment_records', - 'Client employment records', - data.disclose_client_employment_records, - viewOnly || processing, - )} -
- -
- {renderCheckbox( - 'disclose_eligibility_determinations', - 'Eligibility determinations', - data.disclose_eligibility_determinations, - viewOnly || processing, - )} - {renderCheckbox( - 'disclose_substance_abuse_treatment', - 'Substance abuse treatment', - data.disclose_substance_abuse_treatment, - viewOnly || processing, - )} - {renderCheckbox( - 'disclose_benefits_received', - 'Benefits received', - data.disclose_benefits_received, - viewOnly || processing, - )} -
-
- -
- {renderCheckbox( - 'disclose_other_information', - 'Other', - data.disclose_other_information, - viewOnly || processing, - )} - {renderConditionalField( - 'other_disclosure_details', - 'disclose_other_information', - 'Details', - data.other_disclosure_details, - viewOnly || processing, - )} -
+
+ + setData('subjectDob', e.target.value)} + className={errors.subjectDob ? 'border-red-500' : ''} + /> + {errors.subjectDob && ( +

{errors.subjectDob}

+ )}
- - {/* Terms and Authorization */} -
-
-
    -
  1. - READ CAREFULLY: I understand that my - information and records with the Department of Social - Services are confidential by law. I understand that by - signing this authorization, I am allowing the release of any - and all of my information and records which I am authorized - to receive as specified on this document whether past, - present or created in the future up to the expiration or - revocation date of this authorization, unless otherwise - authorized. The protected information in my records may - include medical treatment and/or evaluation information, - mental/behavioral health information, information relating - to sexually transmitted diseases, acquired immunodeficiency - syndrome (AIDS), human immunodeficiency virus (HIV), other - communicable or environmental diseases and conditions, - alcohol/drug abuse, application for and/or receipt of public - assistance benefits, alcohol/drug abuse information, and/or - information concerning child abuse and neglect. -
  2. -
  3. - This authorization includes both information presently - compiled and information to be compiled during your - association or dealings with the Department of Social - Services, during the specified time frame. -
  4. -
  5. - Unless otherwise indicated, this authorization becomes - effective on the date of signature below and will expire one - year from that date. -
  6. -
  7. - I understand that I have a right to revoke this - authorization at any time. I understand that if I revoke - this authorization I must do so IN WRITING{' '} - and present my written revocation to the Privacy Officer of - the Department of Social Services at P.O. Box 1527, MO - 65102. I further understand that actions already taken based - on this authorization, prior to revocation, will{' '} - NOT be affected. -
  8. -
  9. - I understand that I have the right to receive a copy of this - authorization upon request.{' '} - - A photographic copy of this authorization is as valid as - the original. - -
  10. -
  11. - I understand that authorizing the disclosure of this - information is voluntary. I can refuse to sign this - authorization. I need not sign this form in order to receive - services from the Department of Social Services. I - understand that I may request to inspect or request a copy - of information to be used or disclosed, as provided in 45 - CFR section 164.524. I understand that any disclosure of - information carries with it the potential for redisclosure - by the party receiving it and that the information may no - longer be protected by law once it is in the possession of - the receiving party. If I have questions about disclosure of - my information, I can contact the Privacy Officer of the - Department of Social Services, my caseworker or family - support eligibility specialist. -
  12. -
  13. - By signing this disclosure on paper or electronically, I am - giving the Family Support Division (FSD) permission to - deliver, or cause to be delivered, phone calls or text - messages to me regarding my case from an automated dialing - system at my primary number. The FSD does not use an - encryption system when sending text messages. Such - unencrypted systems are not secure and carry some level of - risk that text messages could be read by a third party. By - signing, I am affirming that I nevertheless prefer to - receive text messages from FSD and understand I do not have - to consent to this as part of my application and can opt out - of getting these calls or text messages by checking “No” in - the “Accept Text Messages” box below -
  14. -
- -

- My signature below acknowledges that I have read and - understood the text above, and authorize the release of my - confidential information. +

+
+
+ + setData('subjectSsn', e.target.value)} + className={errors.subjectSsn ? 'border-red-500' : ''} + /> + {errors.subjectSsn && ( +

{errors.subjectSsn}

+ )} +
+
+ + setData('subjectAddress', e.target.value)} + className={errors.subjectAddress ? 'border-red-500' : ''} + /> + {errors.subjectAddress && ( +

+ {errors.subjectAddress}

-
+ )}
+
+ + setData('subjectEmail', e.target.value)} + className={errors.subjectEmail ? 'border-red-500' : ''} + /> + {errors.subjectEmail && ( +

+ {errors.subjectEmail} +

+ )} +
+
+ + - {/* Signature Section */} -
-
-
- - - setData('consumer_signature', e.target.value) - } - disabled={viewOnly || processing} - className="w-full" - /> - {errors.consumer_signature && ( -

- {errors.consumer_signature} -

- )} -
- -
- - setData('signature_date', e.target.value)} - /> - {errors.signature_date && ( -

- {errors.signature_date} -

- )} -
-
- -
-
- - - setData('witness_signature', e.target.value) - } - disabled={viewOnly || processing} - className="w-full" - /> - {errors.consumer_signature && ( -

- {errors.witness_signature} -

- )} -
- -
- - - setData('witness_signature_date', e.target.value) - } - /> - {errors.witness_signature_date && ( -

- {errors.witness_signature_date} -

- )} -
-
- -
-
-
- - - setData('guardian_signature', e.target.value) - } - disabled={viewOnly || processing} - className="w-full" - /> -
+ + + Recipients +

+ I authorize the disclosure of information to: +

+
+ +
+
+ {renderCheckbox( + 'discloseToAttorney', + 'Attorney', + data.discloseToAttorney, + viewOnly, + )} + {renderConditionalField( + 'attorneyName', + 'discloseToAttorney', + 'Attorney Name', + data.attorneyName, + viewOnly, + true, + )} +
+
+ {renderCheckbox( + 'discloseToEmployer', + 'Employer', + data.discloseToEmployer, + viewOnly, + )} + {renderConditionalField( + 'employerName', + 'discloseToEmployer', + 'Employer Name', + data.employerName, + viewOnly, + true, + )} +
+
+
+
+ {renderCheckbox( + 'discloseToLegislator', + 'Legislator', + data.discloseToLegislator, + viewOnly, + )} + {renderConditionalField( + 'legislatorName', + 'discloseToLegislator', + 'Legislator Name', + data.legislatorName, + viewOnly, + true, + )} +
+
+ {renderCheckbox( + 'discloseToGovernorsStaff', + "Governor's Staff", + data.discloseToGovernorsStaff, + viewOnly, + )} +
+
+ + + setData('otherRecipientDetails', e.target.value) + } + /> +
+
+
+
-
- + + + Purpose of Disclosure +

+ The disclosure is being made for the following purpose(s): +

+
+ +
+ {renderPurposeCheckbox( + DisclosurePurposeType.ELIGIBILITY_DETERMINATION, + 'Eligibility Determination', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.LEGAL_CONSULTATION, + 'Legal Consultation', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.LEGAL_PROCEEDINGS, + 'Legal Proceedings', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.EMPLOYMENT, + 'Employment', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.COMPLAINT_INVESTIGATION, + 'Complaint/Investigation', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.TREATMENT_PLANNING, + 'Treatment Planning', + viewOnly, + )} +
+
+ {renderPurposeCheckbox( + DisclosurePurposeType.CONTINUITY_OF_SERVICES, + 'Continuity of Services', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.BACKGROUND_INVESTIGATION, + 'Background Investigation', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.CONSUMER_REQUEST, + 'Consumer Request', + viewOnly, + )} + {renderPurposeCheckbox( + DisclosurePurposeType.SHARE_AND_REFER, + 'Share and Refer Within the Organization', + viewOnly, + )} +
+ {renderPurposeCheckbox( + DisclosurePurposeType.OTHER, + 'Other', + viewOnly, + )} + {renderEnumConditionalField( + 'otherPurposeDetails', + DisclosurePurposeType.OTHER, + 'purposes', + 'Other Purpose Details', + data.otherPurposeDetails, + viewOnly, + true, + )} +
+
+
+
-
- - setData('accept_text_messages', Boolean(checked)) - } - disabled={viewOnly || processing} - /> - -
+ + + Information to be Disclosed +

+ The following information is to be disclosed: +

+
+ +
+ {renderContentTypeCheckbox( + DisclosureContentType.ENTIRE_FILE, + 'Entire File', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.LICENSURE_INFORMATION, + 'Licensure Information', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.MEDICAL_PSYCHIATRIC_RECORDS, + 'Medical/Psychiatric Records', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.HOTLINE_INVESTIGATIONS, + 'Hotline Investigations', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.HOME_STUDIES, + 'Home Studies', + viewOnly, + )} +
+
+ {renderContentTypeCheckbox( + DisclosureContentType.ELIGIBILITY_DETERMINATIONS, + 'Eligibility Determinations', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.SUBSTANCE_ABUSE_TREATMENT, + 'Substance Abuse Treatment', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.CLIENT_EMPLOYMENT_RECORDS, + 'Client Employment Records', + viewOnly, + )} + {renderContentTypeCheckbox( + DisclosureContentType.BENEFITS_RECEIVED, + 'Benefits Received', + viewOnly, + )} +
+ {renderContentTypeCheckbox( + DisclosureContentType.OTHER, + 'Other Information', + viewOnly, + )} + {renderEnumConditionalField( + 'otherDisclosureDetails', + DisclosureContentType.OTHER, + 'contentTypes', + 'Other Information Details', + data.otherDisclosureDetails, + viewOnly, + true, + )} +
+
+
+
-
- - setData('accept_text_messages', Boolean(!checked)) - } - disabled={viewOnly || processing} - /> - -
-
+ + + Communication Preferences + + +
+ {renderCheckbox( + 'acceptTextMessages', + 'I agree to receive text messages regarding this authorization', + data.acceptTextMessages, + viewOnly, + )} +
+
+
-
- -
-
-
+ + + Signatures + + +
+
+ + setData('consumerSignature', e.target.value)} + className={ + errors.consumerSignature ? 'border-red-500' : undefined + } + /> + {errors.consumerSignature && ( +

+ {errors.consumerSignature} +

+ )}
- -
-

Surveys

-

- Family Support Division would like to know what services - enrolled participants are seeking from our programs. In an - effort to capture this data Family Support Division is - administering a survey through Survey Monkey. Plase selet the - preferred mothod of survey delivery: -

-
-
- {renderCheckbox( - 'survey_by_email', - 'Email', - data.survey_by_email, - viewOnly || processing, - )} -
-
-
-
- {renderCheckbox( - 'survey_by_mail', - 'Address', - data.survey_by_mail, - viewOnly || processing, - )} -
-
-
-
- {renderCheckbox( - 'survey_by_online', - 'Online', - data.survey_by_online, - viewOnly || processing, - )} -
-
+
+ + setData('signatureDate', e.target.value)} + className={errors.signatureDate ? 'border-red-500' : undefined} + /> + {errors.signatureDate && ( +

+ {errors.signatureDate} +

+ )}
- -
- +
+
+
+ + setData('witnessSignature', e.target.value)} + /> +
+
+ + + setData('witnessSignatureDate', e.target.value) + } + /> +
+
+
+
+ + setData('guardianSignature', e.target.value)} + /> +
+
+ + + setData('guardianSignatureDate', e.target.value) + } + />
- +
-
+ + + + Survey Delivery Methods +

+ Please select how you would like to receive satisfaction surveys: +

+
+ + {renderCheckbox( + 'surveyByEmail', + 'Email', + data.surveyByEmail, + viewOnly, + )} + {renderCheckbox( + 'surveyByMail', + 'Mail', + data.surveyByMail, + viewOnly, + )} + {renderCheckbox( + 'surveyByOnline', + 'Online', + data.surveyByOnline, + viewOnly, + )} + +
+ + {!viewOnly && ( +
+ + +
+ )} + ) } diff --git a/resources/js/Components/ui/CurrencyInput.tsx b/resources/js/Components/ui/CurrencyInput.tsx new file mode 100644 index 00000000..0d892447 --- /dev/null +++ b/resources/js/Components/ui/CurrencyInput.tsx @@ -0,0 +1,21 @@ +import { Input, InputProps } from './Input' + +export const CurrencyInput = ({ props }: InputProps) => { + return ( +
+
+ $ +
+ +
+ ) +} diff --git a/resources/js/Components/ui/Label.tsx b/resources/js/Components/ui/Label.tsx index fda8225b..6a013f8d 100644 --- a/resources/js/Components/ui/Label.tsx +++ b/resources/js/Components/ui/Label.tsx @@ -1,7 +1,6 @@ import * as React from 'react' import * as LabelPrimitive from '@radix-ui/react-label' import { cva, type VariantProps } from 'class-variance-authority' - import { cn } from '@/lib/utils' const labelVariants = cva( @@ -11,14 +10,24 @@ const labelVariants = cva( const Label = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & - VariantProps ->(({ className, ...props }, ref) => ( - -)) + VariantProps & { + required?: boolean + error?: boolean + } +>(({ className, required, error, ...props }, ref) => { + return ( + + ) +}) Label.displayName = LabelPrimitive.Root.displayName export { Label } diff --git a/resources/js/Pages/Intake/Disclosure/Index.tsx b/resources/js/Pages/Intake/Disclosure/Index.tsx index b262aee2..43d91836 100644 --- a/resources/js/Pages/Intake/Disclosure/Index.tsx +++ b/resources/js/Pages/Intake/Disclosure/Index.tsx @@ -1,6 +1,5 @@ import React from 'react' -import { PageProps, ParticipantData } from '@/types' -import type { IntakeDisclosureAuthorizationForm } from '@/types/intake-disclosure-authorization-form' +import { PageProps, ParticipantData, ParticipantDisclosureAuthorizationForm } from '@/types' import { Button } from '@/Components/ui' import { router } from '@inertiajs/react' import IntakeLayout from '@/Layouts/IntakeLayout' @@ -8,7 +7,7 @@ import { cn } from '@/lib/utils' interface DisclosureProps extends PageProps { participant: ParticipantData - disclosureAuthorizations: IntakeDisclosureAuthorizationForm[] + disclosureAuthorizations: ParticipantDisclosureAuthorizationForm[] } export const Index = ({ diff --git a/resources/js/Pages/Intake/ParticipantRegister.tsx b/resources/js/Pages/Intake/ParticipantRegister.tsx index 3b1dcb19..4beb55f4 100644 --- a/resources/js/Pages/Intake/ParticipantRegister.tsx +++ b/resources/js/Pages/Intake/ParticipantRegister.tsx @@ -45,7 +45,6 @@ export default function ParticipantRegister() { return ( -
New Participant Registration
diff --git a/resources/js/Pages/Intake/Signup.tsx b/resources/js/Pages/Intake/Signup.tsx index bf2e033e..e55e4511 100644 --- a/resources/js/Pages/Intake/Signup.tsx +++ b/resources/js/Pages/Intake/Signup.tsx @@ -1,11 +1,28 @@ -import { PageProps, Child, User } from '@/types' +import { + PageProps, + ChildData, + MaritalStatus, + Ethnicity, + ParticipantData, +} from '@/types' import IntakeLayout from '@/Layouts/IntakeLayout' import { useForm } from '@inertiajs/react' -import { Button, Input, InputError, Label } from '@/Components/ui' +import { + Button, + Input, + InputError, + Label, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/Components/ui' import { ChildrenTable } from '@/Components/ChildrenTable' +import { useEffect } from 'react' +import { toast } from 'sonner' interface StartPageProps extends PageProps { - user: User maritalStatus: Record ethnicity: Record regions: { @@ -15,68 +32,70 @@ interface StartPageProps extends PageProps { } export default function StartPage({ - user, maritalStatus, ethnicity, regions, }: StartPageProps) { - const { data, setData, post, errors, processing } = useForm({ - address_line_1: '', - address_line_2: '', + const newChild: ChildData = { + id: null, + participantId: null, + firstName: '', + lastName: '', + dateOfBirth: '', + childSupport: 0, + custody: false, + visitation: false, + phoneContact: false, + contact: null, + createdAt: null, + updatedAt: null, + } + + const { data, setData, post, errors, processing } = useForm({ + id: null, + userId: null, + user: null, + name: '', + regionId: '', + region: null, + children: [newChild], + addressLine1: '', + addressLine2: null, city: '', state: '', zipcode: '', - employer: '', - t_shirt_size: '', - home_phone_number: user.phone_number?.toString(), - work_phone_number: '', - cell_phone_number: '', - alt_contact_number: '', - probation_parole_case_worker_name: '', - probation_parole_case_worker_phone: '', - marital_status: '', - ethnicity: '', - monthtly_child_support: '', - region_id: '', - children_info: [ - { - first_name: '', - last_name: '', - date_of_birth: '', - child_support: 0, - custody: false, - visitation: false, - phone_contact: false, - }, - ] as Child[], + employer: null, + cellPhoneNumber: null, + homePhoneNumber: null, + workPhoneNumber: null, + altContactNumber: null, + maritalStatus: null, + ethnicity: null, + monthlyChildSupport: null, + tShirtSize: null, + probationParoleCaseWorkerName: null, + probationParoleCaseWorkerPhone: null, + participantPhoto: null, + intakeDate: '', + createdAt: '', + updatedAt: '', }) const addChild = () => { - // Create a new empty child object with default values - const newChild: Child = { - first_name: '', - last_name: '', - date_of_birth: '', // Empty string, you can use an initial date string if needed - child_support: 0, - custody: false, - visitation: false, - phone_contact: false, - } - - // Update the children array with the new child setData((prevData) => ({ ...prevData, - children_info: [...prevData.children_info, newChild], + children: [...prevData.children, newChild], })) } + useEffect(() => { + if (Object.keys(errors).length > 0) { + toast.error('Please fix the errors and resubmit') + } + }, [errors]) + return ( - {Object.keys(errors).length > 0 && ( -
- Please fix the errors and resubmit -
- )} { e.preventDefault() @@ -85,15 +104,17 @@ export default function StartPage({ >
- + setData('address_line_1', e.target.value)} + value={data.addressLine1} + onChange={(e) => setData('addressLine1', e.target.value)} /> - +
@@ -101,14 +122,16 @@ export default function StartPage({ placeholder="Address Line 2" className="w-full" autoComplete="off" - value={data.address_line_2} - onChange={(e) => setData('address_line_2', e.target.value)} + value={data.addressLine2 ?? ''} + onChange={(e) => setData('addressLine2', e.target.value)} /> - +
- +
- - + State + +
- + setData('employer', e.target.value)} /> @@ -158,58 +239,60 @@ export default function StartPage({ placeholder="T-shirt Size" className="w-full" autoComplete="off" - value={data.t_shirt_size} - onChange={(e) => setData('t_shirt_size', e.target.value)} + value={data.tShirtSize ?? ''} + onChange={(e) => setData('tShirtSize', e.target.value)} /> - +
- + setData('home_phone_number', e.target.value)} + value={data.homePhoneNumber ?? ''} + onChange={(e) => setData('homePhoneNumber', e.target.value)} /> - +
- + setData('work_phone_number', e.target.value)} + value={data.workPhoneNumber ?? ''} + onChange={(e) => setData('workPhoneNumber', e.target.value)} /> - +
- + setData('cell_phone_number', e.target.value)} + value={data.cellPhoneNumber ?? ''} + onChange={(e) => setData('cellPhoneNumber', e.target.value)} /> - +
- + setData('alt_contact_number', e.target.value)} + value={data.altContactNumber ?? ''} + onChange={(e) => setData('altContactNumber', e.target.value)} /> - +
@@ -217,13 +300,13 @@ export default function StartPage({ placeholder="Probation Officer's Name" className="w-full" autoComplete="off" - value={data.probation_parole_case_worker_name} + value={data.probationParoleCaseWorkerName ?? ''} onChange={(e) => - setData('probation_parole_case_worker_name', e.target.value) + setData('probationParoleCaseWorkerName', e.target.value) } />
@@ -233,52 +316,64 @@ export default function StartPage({ placeholder="Probation Officer's Phone Number" className="w-full" autoComplete="off" - value={data.probation_parole_case_worker_phone} + value={data.probationParoleCaseWorkerPhone ?? ''} onChange={(e) => - setData('probation_parole_case_worker_phone', e.target.value) + setData('probationParoleCaseWorkerPhone', e.target.value) } />
- - + setData('maritalStatus', value) + } > - - {Object.entries(maritalStatus).map(([key, value]) => ( - - ))} - - + + + + + {Object.entries(maritalStatus).map(([key, value]) => ( + + {value} + + ))} + + +
- - setData('ethnicity', value)} > - - {Object.entries(ethnicity).map(([key, value]) => ( - - ))} - + + + + + {Object.entries(ethnicity).map(([key, value]) => ( + + {value} + + ))} + +
setData('children_info', children)} + childrenInfo={data.children} + setChildren={(children) => setData('children', children)} errors={errors} />
@@ -294,20 +389,25 @@ export default function StartPage({
- - setData('regionId', value)} > - - {regions?.map((region) => ( - - ))} - - + + + + + {regions?.map((region) => ( + + {region.description} + + ))} + + +
-
- - - ) -} diff --git a/resources/js/Pages/Auth/Register.tsx b/resources/js/Pages/Intake/Register.tsx similarity index 58% rename from resources/js/Pages/Auth/Register.tsx rename to resources/js/Pages/Intake/Register.tsx index b850e967..c72abcef 100644 --- a/resources/js/Pages/Auth/Register.tsx +++ b/resources/js/Pages/Intake/Register.tsx @@ -2,26 +2,29 @@ import { useEffect, FormEventHandler } from 'react' import GuestLayout from '@/Layouts/GuestLayout' import { Button, Label, Input, InputError } from '@/Components/ui' import { Head, Link, useForm } from '@inertiajs/react' +import { UserRegistrationForm } from '@/types' export default function Register() { - const { data, setData, post, processing, errors, reset } = useForm({ - first_name: '', - last_name: '', - email: '', - password: '', - password_confirmation: '', - }) + const { data, setData, post, processing, errors, reset } = + useForm({ + firstName: '', + lastName: '', + email: '', + phoneNumber: '', + password: '', + passwordConfirmation: '', + }) useEffect(() => { return () => { - reset('password', 'password_confirmation') + reset('password', 'passwordConfirmation') } }, []) const submit: FormEventHandler = (e) => { e.preventDefault() - post(route('register')) + post(route('intake.register')) } return ( @@ -30,35 +33,35 @@ export default function Register() {
- + setData('first_name', e.target.value)} + onChange={(e) => setData('firstName', e.target.value)} required /> - +
- + setData('last_name', e.target.value)} + onChange={(e) => setData('lastName', e.target.value)} required /> - +
@@ -78,6 +81,23 @@ export default function Register() {
+
+ + + setData('phoneNumber', e.target.value)} + required + /> + + +
+
@@ -96,20 +116,20 @@ export default function Register() {
- + setData('password_confirmation', e.target.value)} + onChange={(e) => setData('passwordConfirmation', e.target.value)} required /> - +
diff --git a/resources/js/types/generated.d.ts b/resources/js/types/generated.d.ts index c1575320..29f01162 100644 --- a/resources/js/types/generated.d.ts +++ b/resources/js/types/generated.d.ts @@ -45,12 +45,12 @@ export type Ethnicity = | 'no_answer' export type MaritalStatus = 'single' | 'married' | 'divorced' | 'widowed' export type ParticipantData = { - id: string - userId: string - user: UserData + id?: string + userId: string | null + user?: UserData | any regionId: string region: RegionData | null - children: Array + children: any addressLine1: string addressLine2: string | null city: string @@ -263,3 +263,11 @@ export type UserData = { updatedAt: string | null emailVerifiedAt: string | null } +export type UserRegistrationForm = { + firstName: string + lastName: string + email: string + phoneNumber: string + password: string + passwordConfirmation: string +} diff --git a/routes/auth.php b/routes/auth.php index a0094095..97671308 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -7,16 +7,11 @@ use App\Http\Controllers\Auth\NewPasswordController; use App\Http\Controllers\Auth\PasswordController; use App\Http\Controllers\Auth\PasswordResetLinkController; -use App\Http\Controllers\Auth\RegisteredUserController; +use App\Http\Controllers\Auth\UserRegistrationController; use App\Http\Controllers\Auth\VerifyEmailController; use Illuminate\Support\Facades\Route; Route::middleware('guest')->group(function () { - Route::get('register', [RegisteredUserController::class, 'create']) - ->name('register'); - - Route::post('register', [RegisteredUserController::class, 'store']); - Route::post('login', [AuthenticatedSessionController::class, 'store'])->name('login'); Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) diff --git a/routes/web.php b/routes/web.php index 36f4de48..ee07cf8d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ hasRole('intake') ? - Inertia::render('Intake/ParticipantRegister') : + Inertia::render('Intake/Register') : Inertia::render('Dashboard') : Inertia::render('Auth/Login'); })->name('home'); @@ -49,14 +49,12 @@ Route::middleware(['role:intake'])->group(function () { Route::get('/', [IntakeController::class, 'index'])->name('index'); - Route::get('register', [ParticipantRegistrationController::class, 'create'])->name('register'); - Route::post('register', [ParticipantRegistrationController::class, 'store']); + Route::get('register', [UserRegistrationController::class, 'create'])->name('register'); + Route::post('register', [UserRegistrationController::class, 'store'])->name('register'); }); - Route::middleware('role:participant')->group(function () { - Route::get('signup', [ParticipantSignupController::class, 'create'])->name('signup'); - Route::post('signup', [ParticipantSignupController::class, 'store']); - }); + Route::get('participantRegister', [ParticipantRegistrationController::class, 'create'])->name('participantRegister'); + Route::post('participantRegister', [ParticipantRegistrationController::class, 'store'])->name('participantRegister'); Route::middleware('role:participant')->group(function () { Route::get('disclosure', [ParticipantDisclosureController::class, 'create'])->name('disclosure'); @@ -95,3 +93,7 @@ }); require __DIR__.'/auth.php'; + +Route::get('/xdebug', function () { + xdebug_info(); +}); From e0431dd1701084d465d396aef446fb0582e23775 Mon Sep 17 00:00:00 2001 From: Nathan Toombs Date: Thu, 5 Jun 2025 20:22:15 -0500 Subject: [PATCH 28/40] Ignore docker directory in .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b59669c3..d3e2f046 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,6 @@ yarn-error.log /.editorconfig /.php-cs-fixer.cache dbml-error.log - +/docker *storybook.log .DS_Store From e2b4fb22f3f949af626e9ed1b17820210037da32 Mon Sep 17 00:00:00 2001 From: Nathan Toombs Date: Thu, 5 Jun 2025 20:22:34 -0500 Subject: [PATCH 29/40] Add user id to session after registration --- app/Http/Controllers/Auth/UserRegistrationController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Auth/UserRegistrationController.php b/app/Http/Controllers/Auth/UserRegistrationController.php index b9a35f4e..e957eb76 100644 --- a/app/Http/Controllers/Auth/UserRegistrationController.php +++ b/app/Http/Controllers/Auth/UserRegistrationController.php @@ -36,9 +36,10 @@ public function store(Request $request): RedirectResponse $user = UserRegistrationForm::from($request); $user = User::create($user->toArray()); - event(new Registered($user)); + // Store the user ID in session + session(['participant_user_id' => $user->id]); - Auth::login($user); + event(new Registered($user)); return redirect(route('intake.participantRegister')); } catch (Throwable $e) { From 61836e9cd1e5d833454b4a7cda180df9bb00c784 Mon Sep 17 00:00:00 2001 From: Nathan Toombs Date: Sun, 8 Jun 2025 17:02:53 -0500 Subject: [PATCH 30/40] Participant signup working --- app/Data/ChildData.php | 14 +++ app/Data/Forms/ChildForm.php | 47 ++++++++++ app/Data/Forms/ParticipantSignupForm.php | 51 +++-------- app/Data/Forms/UserRegistrationForm.php | 11 ++- app/Data/Props/AuthProp.php | 16 ++++ app/Data/Props/MiddlewareProps.php | 19 ++++ .../Props/ParticipantRegistrationProps.php | 27 ++++++ app/Data/Props/RegionProp.php | 13 +++ app/Data/Props/RequestProp.php | 20 +++++ app/Data/Props/ToastProp.php | 17 ++++ app/Enums/ToastType.php | 11 +++ .../Auth/UserRegistrationController.php | 13 ++- .../ParticipantRegistrationController.php | 41 +++++++-- app/Http/Middleware/HandleInertiaRequests.php | 39 +++++++-- resources/js/Components/ChildrenTable.tsx | 11 ++- resources/js/Components/ui/CurrencyInput.tsx | 2 +- resources/js/Components/ui/DataTable.tsx | 2 +- resources/js/Components/ui/TableFilter.tsx | 2 +- .../Pages/Intake/FatherhoodSurvey/Create.tsx | 2 +- resources/js/Pages/Intake/Index.tsx | 24 ----- resources/js/Pages/Intake/Signup.tsx | 87 +++++++------------ resources/js/hooks/permissions.ts | 2 +- resources/js/types/generated.d.ts | 63 ++++++++++++-- resources/js/types/index.d.ts | 33 ++----- routes/web.php | 7 +- 25 files changed, 387 insertions(+), 187 deletions(-) create mode 100644 app/Data/Forms/ChildForm.php create mode 100644 app/Data/Props/AuthProp.php create mode 100644 app/Data/Props/MiddlewareProps.php create mode 100644 app/Data/Props/ParticipantRegistrationProps.php create mode 100644 app/Data/Props/RegionProp.php create mode 100644 app/Data/Props/RequestProp.php create mode 100644 app/Data/Props/ToastProp.php create mode 100644 app/Enums/ToastType.php delete mode 100644 resources/js/Pages/Intake/Index.tsx diff --git a/app/Data/ChildData.php b/app/Data/ChildData.php index 0785f196..1822ff80 100644 --- a/app/Data/ChildData.php +++ b/app/Data/ChildData.php @@ -6,6 +6,7 @@ use Carbon\Carbon; use Carbon\CarbonImmutable; use Illuminate\Support\Str; +use Spatie\LaravelData\Attributes\MapOutputName; use Spatie\LaravelData\Attributes\Validation\BooleanType; use Spatie\LaravelData\Attributes\Validation\Max; use Spatie\LaravelData\Attributes\Validation\Nullable; @@ -14,34 +15,47 @@ use Spatie\LaravelData\Attributes\WithCast; use Spatie\LaravelData\Casts\DateTimeInterfaceCast; use Spatie\LaravelData\Data; +use Spatie\LaravelData\Mappers\SnakeCaseMapper; use Spatie\TypeScriptTransformer\Attributes\TypeScript; #[TypeScript] +#[MapOutputName(SnakeCaseMapper::class)] class ChildData extends Data { public function __construct( #[StringType, Max(36), Nullable] public string $id, + #[StringType, Max(36), Nullable] public string $participantId, + #[StringType, Max(255)] public string $firstName, + #[StringType, Max(255)] public string $lastName, + #[WithCast(DateTimeInterfaceCast::class)] public CarbonImmutable $dateOfBirth, + #[BooleanType, Nullable] public ?bool $phoneContact, + #[BooleanType, Nullable] public ?bool $custody, + #[BooleanType, Nullable] public ?bool $visitation, + #[StringType, Nullable] public ?string $contact, + #[Numeric] public float $childSupport, + #[WithCast(DateTimeInterfaceCast::class)] public string $createdAt, + #[WithCast(DateTimeInterfaceCast::class)] public string $updatedAt, ) { diff --git a/app/Data/Forms/ChildForm.php b/app/Data/Forms/ChildForm.php new file mode 100644 index 00000000..4d4764df --- /dev/null +++ b/app/Data/Forms/ChildForm.php @@ -0,0 +1,47 @@ +|null $childrenInfo */ - public ?DataCollection $childrenInfo, + #[ArrayType, Min(1)] + /** @var array $children */ + public array $children, ) { } - - /** - * Get the validation rules that apply to the request. - * - * @return array - */ - public static function rules(): array - { - return [ - 'homePhoneNumber' => ['nullable', 'string', 'max:12', new UsPhoneNumber()], - 'workPhoneNumber' => ['nullable', 'string', 'max:12', new UsPhoneNumber()], - 'cellPhoneNumber' => ['nullable', 'string', 'max:12', new UsPhoneNumber()], - 'altContactNumber' => ['nullable', 'string', 'max:12', new UsPhoneNumber()], - 'probationParoleCaseWorkerPhone' => ['nullable', 'string', 'max:12', new UsPhoneNumber()], - 'maritalStatus' => ['required', Rule::in(MaritalStatus::values())], - 'ethnicity' => ['required', Rule::in(Ethnicity::values())], - 'childrenInfo' => ['nullable', 'array'], - 'childrenInfo.*.firstName' => ['required_with:childrenInfo', 'string'], - 'childrenInfo.*.lastName' => ['required_with:childrenInfo', 'string'], - 'childrenInfo.*.dateOfBirth' => ['required_with:childrenInfo', 'date'], - 'childrenInfo.*.custody' => ['required_with:childrenInfo', 'boolean'], - 'childrenInfo.*.visitation' => ['required_with:childrenInfo', 'boolean'], - 'childrenInfo.*.phoneContact' => ['required_with:childrenInfo', 'boolean'], - 'childrenInfo.*.childSupport' => ['required_with:childrenInfo', 'numeric'], - ]; - } } diff --git a/app/Data/Forms/UserRegistrationForm.php b/app/Data/Forms/UserRegistrationForm.php index 1c93071e..4b605eea 100644 --- a/app/Data/Forms/UserRegistrationForm.php +++ b/app/Data/Forms/UserRegistrationForm.php @@ -2,6 +2,7 @@ namespace App\Data\Forms; +use App\Enums\Roles; use App\Rules\UsPhoneNumber; use Spatie\LaravelData\Attributes\MapInputName; use Spatie\LaravelData\Attributes\MapOutputName; @@ -11,8 +12,8 @@ use Spatie\LaravelData\Attributes\Validation\Password; use Spatie\LaravelData\Attributes\Validation\StringType; use Spatie\LaravelData\Data; -use Spatie\LaravelData\Mappers\CamelCaseMapper; use Spatie\LaravelData\Mappers\SnakeCaseMapper; +use Spatie\TypeScriptTransformer\Attributes\Optional; use Spatie\TypeScriptTransformer\Attributes\TypeScript; #[TypeScript] @@ -22,16 +23,24 @@ class UserRegistrationForm extends Data public function __construct( #[StringType, Max(191)] public string $firstName, + #[StringType, Max(191)] public string $lastName, + #[StringType, Max(191), Email] public string $email, + #[StringType, Max(12), UsPhoneNumber] public string $phoneNumber, + #[StringType, Password, Max(191)] public string $password, + #[StringType, Password, Max(191), AcceptedIf('password', 'equals_this')] public string $passwordConfirmation, + + #[MapInputName('role'), Optional] + public ?Roles $role ) { } } diff --git a/app/Data/Props/AuthProp.php b/app/Data/Props/AuthProp.php new file mode 100644 index 00000000..60ea9c6f --- /dev/null +++ b/app/Data/Props/AuthProp.php @@ -0,0 +1,16 @@ + $query */ + public array $query + ) { + } +} diff --git a/app/Data/Props/ToastProp.php b/app/Data/Props/ToastProp.php new file mode 100644 index 00000000..dd97511c --- /dev/null +++ b/app/Data/Props/ToastProp.php @@ -0,0 +1,17 @@ +toArray()); + if (! auth()->user()->hasRole('intake')) { + return response()->json($user); + } + + $user->assignRole('participant'); + // Store the user ID in session - session(['participant_user_id' => $user->id]); + session(['intake_user_id' => $user->id]); event(new Registered($user)); return redirect(route('intake.participantRegister')); + } catch (Throwable $e) { Log::error('Error processing participant signup form: '.$e->getMessage(), [ 'exception' => $e, diff --git a/app/Http/Controllers/Intake/ParticipantRegistrationController.php b/app/Http/Controllers/Intake/ParticipantRegistrationController.php index 7ad8e55e..95815320 100644 --- a/app/Http/Controllers/Intake/ParticipantRegistrationController.php +++ b/app/Http/Controllers/Intake/ParticipantRegistrationController.php @@ -2,13 +2,18 @@ namespace App\Http\Controllers\Intake; +use App\Data\Forms\ChildForm; use App\Data\Forms\ParticipantSignupForm; +use App\Data\Props\ParticipantRegistrationProps; use App\Enums\Ethnicity; use App\Enums\MaritalStatus; use App\Http\Controllers\Controller; +use App\Models\Child; +use App\Models\Participant; use App\Models\Region; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Auth; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; use Log; @@ -21,13 +26,11 @@ class ParticipantRegistrationController extends Controller */ public function create(): Response { - - return Inertia::render('Intake/Signup', [ - 'user' => Auth::user(), + return Inertia::render('Intake/Signup', ParticipantRegistrationProps::from([ 'ethnicity' => Ethnicity::displayArray(), 'maritalStatus' => MaritalStatus::displayArray(), 'regions' => Region::get(['id', 'description'])->toArray(), - ]); + ])->toArray()); } /** @@ -35,13 +38,33 @@ public function create(): Response * * @throws Throwable */ - public function store(ParticipantSignupForm $request): JsonResponse + public function store(Request $request): RedirectResponse|JsonResponse { try { - $participant = ParticipantSignupForm::from($request); - $participantData = $participant->toArray(); + $participant = ParticipantSignupForm::from($request->all()); + + $participantId = session('intake_user_id'); + unset($participant->children); + $participant = Participant::create([ + 'user_id' => $participantId, + ...$participant->toArray(), + ]); + $children = $request->children; + foreach ($children as $child) { + $child = ChildForm::from($child); + $child = $child->toArray(); + $child['participant_id'] = $participant->id; + Child::create($child); + } + + if (auth()->user()->hasRole('intake')) { + // Store the user ID in session + session(['intake_participant_id' => $participant->id]); + + return redirect(route('intake.disclosure')); + } - return response()->json($participantData); + return response()->json($participant); } catch (Throwable $e) { Log::error('Error processing participant signup form: '.$e->getMessage(), [ 'exception' => $e, diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index c41a9ca2..0372ea2a 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -2,9 +2,11 @@ namespace App\Http\Middleware; +use App\Data\Props\MiddlewareProps; use App\Data\UserData; use Illuminate\Http\Request; use Inertia\Middleware; +use Throwable; class HandleInertiaRequests extends Middleware { @@ -30,16 +32,35 @@ public function version(Request $request): ?string */ public function share(Request $request): array { - return [ - ...parent::share($request), - 'auth.user' => fn () => $request->user() - ? UserData::from($request->user()) - : null, - 'ziggy' => fn () => [ + try { + $userProp = $request->user() + ? UserData::from($request->user()) + : null; + + $requestProp = [ 'location' => $request->url(), 'query' => $request->query() ?? [], - ], - 'toast' => $request->session()->get('toast'), - ]; + ]; + + $middlewareProps = MiddlewareProps::from([ + 'auth' => [ + 'user' => $userProp, + ], + 'request' => $requestProp, + 'toast' => $request->session()->get('toast'), + ])->toArray(); + + return [ + ...parent::share($request), + ...$middlewareProps, + ]; + } catch (Throwable $e) { + return [ + 'error' => [ + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ], + ]; + } } } diff --git a/resources/js/Components/ChildrenTable.tsx b/resources/js/Components/ChildrenTable.tsx index 6e20763a..dfec6ec0 100644 --- a/resources/js/Components/ChildrenTable.tsx +++ b/resources/js/Components/ChildrenTable.tsx @@ -1,11 +1,11 @@ import React from 'react' import { Button, Checkbox, Input, InputError, Label } from '@/Components/ui' -import { ChildData } from '@/types' +import { ChildData, ChildForm } from '@/types' import { CurrencyInput } from '@/Components/ui/CurrencyInput' -export interface ChildrenTableProps { - childrenInfo: ChildData[] - setChildren: (children: ChildData[]) => void +export type ChildrenTableProps = { + childrenInfo: ChildForm[] + setChildren: (children: ChildForm[]) => void errors: Record } @@ -179,6 +179,9 @@ const ChildrenTable = React.forwardRef(
))}
+ {errors.children && ( + + )}
) }, diff --git a/resources/js/Components/ui/CurrencyInput.tsx b/resources/js/Components/ui/CurrencyInput.tsx index 0d892447..e1d87fcd 100644 --- a/resources/js/Components/ui/CurrencyInput.tsx +++ b/resources/js/Components/ui/CurrencyInput.tsx @@ -1,6 +1,6 @@ import { Input, InputProps } from './Input' -export const CurrencyInput = ({ props }: InputProps) => { +export const CurrencyInput = (props: InputProps) => { return (
diff --git a/resources/js/Components/ui/DataTable.tsx b/resources/js/Components/ui/DataTable.tsx index 413d26cf..6279e8cb 100644 --- a/resources/js/Components/ui/DataTable.tsx +++ b/resources/js/Components/ui/DataTable.tsx @@ -68,7 +68,7 @@ function DataTableComponent( tableActions, } = props const { - ziggy: { query }, + request: { query }, page, pageSize, totalPages, diff --git a/resources/js/Components/ui/TableFilter.tsx b/resources/js/Components/ui/TableFilter.tsx index e0e37002..32c48387 100644 --- a/resources/js/Components/ui/TableFilter.tsx +++ b/resources/js/Components/ui/TableFilter.tsx @@ -38,7 +38,7 @@ type FilterItemProps = { export const TableFilter = () => { const { fields } = useDataTableContext() - const query = usePage().props.ziggy.query + const query = usePage().props.request.query const queryFilters = () => { if (query.filters) { return Object.fromEntries( diff --git a/resources/js/Pages/Intake/FatherhoodSurvey/Create.tsx b/resources/js/Pages/Intake/FatherhoodSurvey/Create.tsx index 022a988f..bb986c82 100644 --- a/resources/js/Pages/Intake/FatherhoodSurvey/Create.tsx +++ b/resources/js/Pages/Intake/FatherhoodSurvey/Create.tsx @@ -8,7 +8,7 @@ interface SurveyPageProps extends PageProps { participant: ParticipantData } -export const Create: React.FC = ({ auth, participant }) => { +export const Create = ({ auth, participant }: SurveyPageProps) => { return ( diff --git a/resources/js/Pages/Intake/Index.tsx b/resources/js/Pages/Intake/Index.tsx deleted file mode 100644 index 1b9657c4..00000000 --- a/resources/js/Pages/Intake/Index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' -import { Button } from '@/Components/ui' -import { Head, Link } from '@inertiajs/react' -import { type PageProps } from '@/types' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' - -interface IntakeIndexProps extends PageProps {} - -export const IntakeIndex: React.FC = ({ auth }) => { - return ( - - -
-
- -
-
-
- ) -} - -export default IntakeIndex diff --git a/resources/js/Pages/Intake/Signup.tsx b/resources/js/Pages/Intake/Signup.tsx index e55e4511..919d5682 100644 --- a/resources/js/Pages/Intake/Signup.tsx +++ b/resources/js/Pages/Intake/Signup.tsx @@ -1,9 +1,9 @@ import { - PageProps, - ChildData, MaritalStatus, Ethnicity, - ParticipantData, + ChildForm, + ParticipantSignupForm, + ParticipantRegistrationProps, } from '@/types' import IntakeLayout from '@/Layouts/IntakeLayout' import { useForm } from '@inertiajs/react' @@ -19,26 +19,15 @@ import { SelectValue, } from '@/Components/ui' import { ChildrenTable } from '@/Components/ChildrenTable' -import { useEffect } from 'react' +import { FormEvent, useEffect } from 'react' import { toast } from 'sonner' -interface StartPageProps extends PageProps { - maritalStatus: Record - ethnicity: Record - regions: { - id: string - description: string - }[] -} - export default function StartPage({ maritalStatus, ethnicity, regions, -}: StartPageProps) { - const newChild: ChildData = { - id: null, - participantId: null, +}: ParticipantRegistrationProps) { + const newChild: ChildForm = { firstName: '', lastName: '', dateOfBirth: '', @@ -46,40 +35,28 @@ export default function StartPage({ custody: false, visitation: false, phoneContact: false, - contact: null, - createdAt: null, - updatedAt: null, } - const { data, setData, post, errors, processing } = useForm({ - id: null, - userId: null, - user: null, - name: '', - regionId: '', - region: null, - children: [newChild], - addressLine1: '', - addressLine2: null, - city: '', - state: '', - zipcode: '', - employer: null, - cellPhoneNumber: null, - homePhoneNumber: null, - workPhoneNumber: null, - altContactNumber: null, - maritalStatus: null, - ethnicity: null, - monthlyChildSupport: null, - tShirtSize: null, - probationParoleCaseWorkerName: null, - probationParoleCaseWorkerPhone: null, - participantPhoto: null, - intakeDate: '', - createdAt: '', - updatedAt: '', - }) + const { data, setData, post, errors, processing } = + useForm({ + addressLine1: '', + addressLine2: null, + city: '', + state: '', + zipcode: '', + employer: null, + tShirtSize: null, + homePhoneNumber: null, + workPhoneNumber: null, + cellPhoneNumber: null, + altContactNumber: null, + probationParoleCaseWorkerPhone: null, + probationParoleCaseWorkerName: null, + maritalStatus: 'single', + ethnicity: 'white', + regionId: '', + children: [newChild], + }) const addChild = () => { setData((prevData) => ({ @@ -94,14 +71,14 @@ export default function StartPage({ } }, [errors]) + const handleSubmit = (e: FormEvent) => { + e.preventDefault() + post(route('intake.participantRegister')) + } + return ( - { - e.preventDefault() - post(route('intake.signup')) - }} - > +