diff --git a/.gitignore b/.gitignore index ac36ab6..8e1c9f8 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,6 @@ yarn-debug.log* /config/master.key .DS_Store + +/app/assets/builds/* +!/app/assets/builds/.keep diff --git a/Dockerfile.db b/Dockerfile.db new file mode 100644 index 0000000..750d682 --- /dev/null +++ b/Dockerfile.db @@ -0,0 +1,9 @@ +FROM mcr.microsoft.com/mssql/server:2017-latest +ENV ACCEPT_EULA=Y +ENV MSSQL_SA_PASSWORD=Br1tt0nPassw0rd +ENV MSSQL_PID=Developer + +COPY init.sql . +COPY ./bin/dbentrypoint.sh . +EXPOSE 1434 +CMD /bin/bash dbentrypoint.sh \ No newline at end of file diff --git a/Gemfile b/Gemfile index 8149b56..55936a9 100644 --- a/Gemfile +++ b/Gemfile @@ -12,9 +12,6 @@ gem "rails", "~> 7.2" # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] gem "sprockets-rails" -# Use postgresql as the database for Active Record -gem "pg", "~> 1.5" - # Use the Puma web server [https://github.com/puma/puma] gem "puma", "~> 6.5" @@ -31,7 +28,7 @@ gem "stimulus-rails" gem "jbuilder" # Use Redis adapter to run Action Cable in production -gem "redis", "~> 5.3" +# gem "redis", "~> 5.3" # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] # gem "kredis" @@ -40,17 +37,17 @@ gem "redis", "~> 5.3" # gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data", platforms: %i[ windows jruby ] +gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", require: false # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] -gem "image_processing", "~> 1.2" +# gem "image_processing", "~> 1.2" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem - gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" + # gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" # Static analysis for security vulnerabilities [https://brakemanscanner.org/] gem "brakeman", require: false @@ -70,6 +67,8 @@ group :development do # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] gem "rack-mini-profiler" + + gem "pry-rails" end group :test do @@ -77,3 +76,9 @@ group :test do gem "capybara" gem "selenium-webdriver" end + +gem 'activerecord-sqlserver-adapter' +gem 'tiny_tds' +gem 'devise' +gem 'pundit' +gem "tailwindcss-rails" \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 4c3c1b5..96aabc9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,6 +56,9 @@ GEM activemodel (= 7.2.3) activesupport (= 7.2.3) timeout (>= 0.4.0) + activerecord-sqlserver-adapter (7.2.8) + activerecord (~> 7.2.0) + tiny_tds activestorage (7.2.3) actionpack (= 7.2.3) activejob (= 7.2.3) @@ -78,6 +81,7 @@ GEM public_suffix (>= 2.0.2, < 7.0) ast (2.4.3) base64 (0.3.0) + bcrypt (3.1.20) benchmark (0.5.0) bigdecimal (3.3.1) bindex (0.8.1) @@ -99,32 +103,25 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) cgi (0.5.0) + coderay (1.1.3) concurrent-ruby (1.3.5) connection_pool (2.5.4) crass (1.0.6) date (3.5.0) - debug (1.11.0) - irb (~> 1.10) - reline (>= 0.3.8) + devise (4.9.4) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0) + responders + warden (~> 1.2.3) diff-lcs (1.6.2) drb (2.2.3) erb (5.1.3) erubi (1.13.1) - ffi (1.17.2-aarch64-linux-gnu) - ffi (1.17.2-aarch64-linux-musl) - ffi (1.17.2-arm-linux-gnu) - ffi (1.17.2-arm-linux-musl) - ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86_64-darwin) - ffi (1.17.2-x86_64-linux-gnu) - ffi (1.17.2-x86_64-linux-musl) globalid (1.3.0) activesupport (>= 6.1) i18n (1.14.7) concurrent-ruby (~> 1.0) - image_processing (1.14.0) - mini_magick (>= 4.9.5, < 6) - ruby-vips (>= 2.0.17, < 3) importmap-rails (2.2.2) actionpack (>= 6.0.0) activesupport (>= 6.0.0) @@ -152,8 +149,7 @@ GEM net-smtp marcel (1.1.0) matrix (0.4.3) - mini_magick (5.3.1) - logger + method_source (1.1.0) mini_mime (1.1.5) minitest (5.26.0) msgpack (1.8.0) @@ -183,27 +179,28 @@ GEM racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-musl) racc (~> 1.4) + orm_adapter (0.5.0) parallel (1.27.0) parser (3.3.10.0) ast (~> 2.4.1) racc - pg (1.6.2) - pg (1.6.2-aarch64-linux) - pg (1.6.2-aarch64-linux-musl) - pg (1.6.2-arm64-darwin) - pg (1.6.2-x86_64-darwin) - pg (1.6.2-x86_64-linux) - pg (1.6.2-x86_64-linux-musl) pp (0.6.3) prettyprint prettyprint (0.2.0) prism (1.6.0) + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-rails (0.3.11) + pry (>= 0.13.0) psych (5.2.6) date stringio public_suffix (6.0.2) puma (6.6.1) nio4r (~> 2.0) + pundit (2.5.2) + activesupport (>= 3.0.0) racc (1.8.1) rack (3.2.4) rack-mini-profiler (4.0.1) @@ -252,13 +249,12 @@ GEM erb psych (>= 4.0.0) tsort - redis (5.4.1) - redis-client (>= 0.22.0) - redis-client (0.26.1) - connection_pool regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) rexml (3.4.4) rspec-core (3.13.6) rspec-support (~> 3.13.0) @@ -309,9 +305,6 @@ GEM lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) - ruby-vips (2.2.5) - ffi (~> 1.12) - logger rubyzip (3.2.2) securerandom (0.4.1) selenium-webdriver (4.38.0) @@ -331,8 +324,28 @@ GEM stimulus-rails (1.3.4) railties (>= 6.0.0) stringio (3.1.7) + tailwindcss-rails (4.4.0) + railties (>= 7.0.0) + tailwindcss-ruby (~> 4.0) + tailwindcss-ruby (4.1.16) + tailwindcss-ruby (4.1.16-aarch64-linux-gnu) + tailwindcss-ruby (4.1.16-aarch64-linux-musl) + tailwindcss-ruby (4.1.16-arm64-darwin) + tailwindcss-ruby (4.1.16-x86_64-darwin) + tailwindcss-ruby (4.1.16-x86_64-linux-gnu) + tailwindcss-ruby (4.1.16-x86_64-linux-musl) thor (1.4.0) timeout (0.4.4) + tiny_tds (3.3.0) + bigdecimal (~> 3) + tiny_tds (3.3.0-aarch64-linux-gnu) + bigdecimal (~> 3) + tiny_tds (3.3.0-aarch64-linux-musl) + bigdecimal (~> 3) + tiny_tds (3.3.0-x86_64-linux-gnu) + bigdecimal (~> 3) + tiny_tds (3.3.0-x86_64-linux-musl) + bigdecimal (~> 3) tsort (0.2.0) turbo-rails (2.0.20) actionpack (>= 7.1.0) @@ -343,6 +356,8 @@ GEM unicode-emoji (~> 4.1) unicode-emoji (4.1.0) useragent (0.16.11) + warden (1.2.9) + rack (>= 2.0.9) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -368,19 +383,19 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + activerecord-sqlserver-adapter bootsnap brakeman bundler-audit capybara - debug - image_processing (~> 1.2) + devise importmap-rails jbuilder - pg (~> 1.5) + pry-rails puma (~> 6.5) + pundit rack-mini-profiler rails (~> 7.2) - redis (~> 5.3) rspec-rails rubocop-rails rubocop-rails-omakase @@ -388,6 +403,8 @@ DEPENDENCIES selenium-webdriver sprockets-rails stimulus-rails + tailwindcss-rails + tiny_tds turbo-rails tzinfo-data web-console diff --git a/Procfile.dev b/Procfile.dev index 852e6c7..94daa16 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,2 +1,2 @@ -web: bin/rails server -p 3000 -css: bin/rails dartsass:watch +web: bin/rails server -b 0.0.0.0 -p 3002 +css: bin/rails tailwindcss:watch[verbose] diff --git a/app/assets/builds/.keep b/app/assets/builds/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index ddd546a..b06fc42 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -2,3 +2,4 @@ //= link_directory ../stylesheets .css //= link_tree ../../javascript .js //= link_tree ../../../vendor/javascript .js +//= link_tree ../builds diff --git a/app/assets/tailwind/application.css b/app/assets/tailwind/application.css new file mode 100644 index 0000000..4ba672b --- /dev/null +++ b/app/assets/tailwind/application.css @@ -0,0 +1,18 @@ +@import "tailwindcss"; + +@theme { + /* dark blue */ + --color-deepcove: #04153E; + /* medium dark blue */ + --color-bluetang: #2A4B6F; + /* bright blue */ + --color-atmosphere: #0096E0; + /* light blue */ + --color-bluemana: #6AC8F1; + /* platinum */ + --color-platinum: #E0E0E0; + /* copper */ + --color-copper: #B06E30; + /* bronze */ + --color-bronze: #D38F4A; +} diff --git a/app/controllers/employer_setup_controller.rb b/app/controllers/employer_setup_controller.rb new file mode 100644 index 0000000..30fc010 --- /dev/null +++ b/app/controllers/employer_setup_controller.rb @@ -0,0 +1,191 @@ +class EmployerSetupController < ApplicationController + def new + @top_form = EmployerSetupForm.new(session[:employer_setup_data]) + case @top_form.current_step + when 'general_information' + @form = EmployerSetupGeneralInformationForm.new(session[:employer_setup_data]&.dig('general_information_data')) + when 'plans' + @form = EmployerSetupPlansForm.new(session[:employer_setup_data]&.dig('plans_data')) + when 'network_exceptions' + @form = EmployerSetupNetworkExceptionsForm.new(session[:employer_setup_data]&.dig('network_exceptions_data')) + when 'summary' + @form = @top_form + end + render @top_form.current_step_view + end + + def create + @top_form = EmployerSetupForm.new(session[:employer_setup_data]) + if @top_form.current_step != 'summary' + if process_step(@top_form.current_step) + session[:employer_setup_data]['current_step'] = @top_form.next_step + redirect_to new_employer_setup_path + else + render @top_form.current_step_view + end + else + if @top_form.save + session.delete(:employer_setup_data) + redirect_to root_path, notice: "Employer setup successfully!" + else + render @top_form.current_step_view + end + end + + case @top_form.current_step + when 'general_information' + @form = EmployerSetupGeneralInformationForm.new(general_information_params) + if @form.valid? + session[:employer_setup_data]['general_information_data'] = general_information_params + session[:employer_setup_data]['current_step'] = @top_form.next_step + redirect_to new_employer_setup_path + else + render @top_form.current_step_view + end + when 'plans' + @form = EmployerSetupPlansForm.new(plans_params) + if @form.valid? + session[:employer_setup_data]['plans_data'] = plans_params + session[:employer_setup_data]['current_step'] = @top_form.next_step + redirect_to new_employer_setup_path + else + render @top_form.current_step_view + end + when 'network_exceptions' + @form = EmployerSetupNetworkExceptionsForm.new(network_exceptions_params) + if @form.valid? + session[:employer_setup_data]['network_exceptions_data'] = network_exceptions_params + session[:employer_setup_data]['current_step'] = @top_form.next_step + redirect_to new_employer_setup_path + else + render @top_form.current_step_view + end + when 'summary' + @form = EmployerSetupForm.new(session[:employer_setup_data]) + if @form.save + session.delete(:employer_setup_data) + redirect_to root_path, notice: "Employer setup successfully!" + else + render @top_form.current_step_view + end + end + end + + def update + @form = RegistrationForm.new(registration_params) + + if params[:back_button] + @form.current_step = @form.previous_step + elsif params[:skip_newsletter] + @form.current_step = @form.next_step # Skip newsletter step + end + + render :new + end + + private + + def general_information_params + params.require(:employer_setup_general_information_form).permit( + :name, + :employer_logo, + :group_number, + :dental, + :pl_plan_key, + :effect_date, + :number_of_plans, + :network, + :number_of_additional_network_logos + ) + end + + def plans_params + params.require(:employer_setup_plans_form).permit( + plans: permited_plans_keys, + benefit_descs: benefit_sequence_keys + ) + end + + def network_exceptions_params + params.require(:employer_setup_network_exceptions_form).permit( + network_exceptions: [:network_logo, exceptions: [:type, :value]], + ) + end + + def process_step(step_name) + form_name = "employer_setup_#{step_name}_form".camelize.constantize + form_params_name = "#{step_name}_params".to_sym + allowed_params = [:general_information_params, :plans_params, :network_exceptions_params] + if allowed_params.include?(form_params_name) + form_params = send(form_params_name) + @form = form_name.new(form_params) + if @form.valid? + session[:employer_setup_data]["#{step_name}_data"] = form_params + true + else + false + end + end + false + end + + + + + + session[:employer_setup_data]['current_step'] = @top_form.next_step + redirect_to new_employer_setup_path + else + render @top_form.current_step_view + end + end + + when 'general_information' + @form = EmployerSetupGeneralInformationForm.new(general_information_params) + if @form.valid? + session[:employer_setup_data]['general_information_data'] = general_information_params + session[:employer_setup_data]['current_step'] = @top_form.next_step + redirect_to new_employer_setup_path + else + render @top_form.current_step_view + end + + # def employer_setup_params + # params.require(:employer_setup_form).permit( + # :current_step, + # :name, + # :employer_logo, + # :group_number, + # :pl_plan_key, + # :effect_date, + # :number_of_plans, + # :network, + # :number_of_additional_network_logos, + # network_exceptions: [:network_logo, exceptions: [:type, :value]], + # plans: permited_plans_keys, + # benefit_descs: benefit_sequence_keys + # ) + # end + + def benefit_sequence_keys + (1..14).map { |i| i.to_s.to_sym } + end + + def permited_plans_keys + benefit_sequence_keys.push(:plan_id) + end + + # def plans_params + # plans_keys = params[:plans]&.keys || [] + + # plans_keys.each_with_object({}) do |key, hash| + # if key == 'benefit_descs' || key.match?(/^plan_\d$/) + # hash[key.to_sym] = permited_plan_param_list + # end + # end + # end + + # def permited_plan_param_list + # (1..14).map { |i| i.to_s.to_sym }.push(:plan_id) + # end +end diff --git a/app/controllers/employer_setup_controller_old.rb b/app/controllers/employer_setup_controller_old.rb new file mode 100644 index 0000000..6837bc4 --- /dev/null +++ b/app/controllers/employer_setup_controller_old.rb @@ -0,0 +1,109 @@ +class EmployerSetupController < ApplicationController + def new + @employer_data = session[:employer_data] || {} + # @id_card_templates = IdCardTemplate.where.not(title: "BLANK") + # @id_card_template_benefits = IdCardTemplate.find_by(title: "BLANK").id_card_template_benefits.sort_by(&:sequence) + end + + def create_employer + @employer_data = {employer: {}} + @employer_data[:employer].merge!(params.require(:employer).permit( + :name, + :group_number, + :pl_plan_key, + :effect_date + )) + @employer_data[:employer].merge!(params.permit(:number_of_plans)) + + session[:employer_data] = @employer_data + puts session[:employer_data] + redirect_to action: :plans + end + + def plans + @employer_data = session[:employer_data] || {} + @id_card_templates = IdCardBenefitsTemplate.where.not(title: "BLANK") + @id_card_template_benefits = IdCardBenefitsTemplate.find_by(title: "BLANK").id_card_benefits.sort_by(&:sequence) + end + + def create_plans + @employer_data = session[:employer_data] || {} + @employer_data.merge!(params.require(:plans).permit(plans_params)) + session[:employer_data] = @employer_data + redirect_to action: :networks + end + + def networks + @employer_data = session[:employer_data] || {} + + end + + def create_provider_networks + @employer_data = session[:employer_data] || {} + @employer_data.merge!(params.require(:plans).permit(plans_params)) + session[:employer_data] = @employer_data + redirect_to action: :networks + end + + + def process_bad_name + @final_data = session[:employer_data] + + # Vhcs::HlPlanCode.create( + # group_number: @final_data['employer']['name'], + # medical_number: @final_data['employer']['group_number'], + # dental_number: ' ', + # plan_key: @final_data['employer']['pl_plan_key'], + # effect_date: @final_data['employer']['effect_date'] + # ) + + # default = Vhcs::HLRXCrosRef.find_by(pl_plan_key: 52) + + # Vhcs::HLRXCrosRef.create( + # group_no: @final_data['employer']['group_number'], + # rx_group_id: @final_data['employer']['group_number'], + # help_desk: default.help_desk, + # customer_service: default.customer_service, + # web_url: default.web_url, + # pl_plan_key: @final_data['employer']['pl_plan_key'] + # ) + + # plans_data = @final_data['plans'] + # benefit_descs = plans_data.delete('benefit_descs') + + # plans_data.each do |key, value| + # plan_id = value.delete('plan_id') + # value.each do |key2, value2| + # Vhcs::HLEgglestonCardBenefit.create( + # plan_id: plan_id, + # benefit_desc: benefit_descs[key2], + # benefit: value2, + # sequence: key2, + # plan_key: @final_data['employer']['pl_plan_key'] + # ) + # end + + # end + + # Create or update your model with @final_data + # Clear the session data after successful save + session[:employer_data] = nil + # Redirect to a success page + end + + private + + def plans_params + plans_keys = params[:plans]&.keys || [] + + plans_keys.each_with_object({}) do |key, hash| + if key == 'benefit_descs' || key.match?(/^plan_\d$/) + hash[key.to_sym] = permited_plan_param_list + end + end + end + + def permited_plan_param_list + (1..14).map { |i| i.to_s.to_sym }.push(:plan_id) + end +end diff --git a/app/controllers/employer_setup_controller_pre_split.rb b/app/controllers/employer_setup_controller_pre_split.rb new file mode 100644 index 0000000..964df31 --- /dev/null +++ b/app/controllers/employer_setup_controller_pre_split.rb @@ -0,0 +1,89 @@ +class EmployerSetupController < ApplicationController + def new + # session.delete(:employer_setup_data) + @form = EmployerSetupForm.new(session[:employer_setup_data]) + if @form.current_step == "plans" + @id_card_templates = IdCardBenefitsTemplate.where.not(title: "BLANK") + @id_card_template_benefits = IdCardBenefitsTemplate.find_by(title: "BLANK").id_card_benefits.sort_by(&:sequence) + end + puts session[:employer_setup_data] + render "employer_setup/#{@form.current_step}" # Renders the view for the current step + end + + def create + filtered_params = employer_setup_params.except(:validation_context) + @form = EmployerSetupForm.new(filtered_params) + + if @form.current_step == "general" && @form.valid?(:general_info) + # @form.current_step = "plans" + @form.current_step = "networks" # TESTING, Change Back + session[:employer_setup_data] = @form.attributes.slice("current_step", "name", "employer_logo", "group_number", "pl_plan_key", "effect_date", "number_of_plans", "network", "number_of_additional_network_logos") + redirect_to new_employer_setup_path # Redirect to the next step + elsif @form.current_step == "plans" && @form.valid?(:plan_info) + if @form.number_of_additional_network_logos == 0 + next_step = "summary" + else + next_step = "networks" + end + @form.current_step = next_step + session[:employer_setup_data].merge!(@form.attributes.slice("current_step", "plans", "benefit_descs")) + redirect_to new_employer_setup_path + # @form = UserOnboardingForm.new(session[:employer_setup_data]) # Re-initialize with all data + elsif @form.current_step == "networks" && @form.valid?(:network_info) + @form.current_step = "summary" + session[:employer_setup_data].merge!(@form.attributes.slice("current_step", "network_exceptions")) + redirect_to new_employer_setup_path + elsif @form.current_step == "summary" + puts @form.attributes + if @form.save + session.delete(:employer_setup_data) # Clear session data after successful save + redirect_to root_path, notice: "Employer setup successfully!" + else + render "employer_setup/summary" # Render step two again with errors + end + else + render "employer_setup/#{@form.current_step}" # Render the current step again with errors + end + end + + private + + def employer_setup_params + params.require(:employer_setup_form).permit( + :current_step, + :name, + :employer_logo, + :group_number, + :pl_plan_key, + :effect_date, + :number_of_plans, + :network, + :number_of_additional_network_logos, + network_exceptions: [:network_logo, exceptions: [:type, :value]], + plans: permited_plans_keys, + benefit_descs: benefit_sequence_keys + ) + end + + def benefit_sequence_keys + (1..14).map { |i| i.to_s.to_sym } + end + + def permited_plans_keys + benefit_sequence_keys.push(:plan_id) + end + + def plans_params + plans_keys = params[:plans]&.keys || [] + + plans_keys.each_with_object({}) do |key, hash| + if key == 'benefit_descs' || key.match?(/^plan_\d$/) + hash[key.to_sym] = permited_plan_param_list + end + end + end + + def permited_plan_param_list + (1..14).map { |i| i.to_s.to_sym }.push(:plan_id) + end +end diff --git a/app/controllers/id_card_benefits_templates_controller.rb b/app/controllers/id_card_benefits_templates_controller.rb new file mode 100644 index 0000000..5f4ec81 --- /dev/null +++ b/app/controllers/id_card_benefits_templates_controller.rb @@ -0,0 +1,89 @@ +class IdCardBenefitsTemplatesController < ApplicationController + skip_before_action :verify_authenticity_token + + def new_id_card_template + @id_card_templates = IdCardTemplate.where.not(title: "BLANK") + @id_card_template_benefits = IdCardTemplate.find_by(title: "BLANK").id_card_template_benefits.sort_by(&:sequence) + end + + def create_id_card_template + @id_card_template = IdCardTemplate.create(title: params[:title]) + + params[:benefits].each do |key, value| + IdCardTemplateBenefit.create( + sequence: key, + benefit_desc: value["desc"], + benefit: value["value"], + id_card_template: @id_card_template + ) + + end + + respond_to do |format| + if @id_card_template.save && @id_card_template.id_card_template_benefits.length == 14 + format.html { redirect_to '/dev_tools/new_id_card_setup', notice: "Template was successfully created." } + # format.json { render :show, status: :created, location: @employer } + else + format.html { render :new_id_card_template, status: :unprocessable_entity } + # format.json { render json: @employer.errors, status: :unprocessable_entity } + end + end + end + + def new_id_card_setup + @id_card_templates = IdCardTemplate.where.not(title: "BLANK") + @id_card_template_benefits = IdCardTemplate.find_by(title: "BLANK").id_card_template_benefits + end + + def get_templates_benefits + @id_card_benefits = IdCardBenefitsTemplate.find(params[:id]).id_card_benefits + render json: @id_card_benefits.as_json + end + + def create_id_card_setup + employer_general = params['general'] + hl_plan_code = Vhcs::HlPlanCode.new( + group_number: employer_general['group_number'], + medical_number: employer_general['group_number'], + dental_number: '', + plan_key: employer_general['pl_plan_key'], + effect_date: employer_general['effect_date'] + ) + + # Replace fairos_info with template like for benefits + fairos_info = Vhcs::HLRXCrosRef.where(pl_plan_key: 52).first + hlrx_cros_ref = Vhcs::HLRXCrosRef.new( + group_no: employer_general['group_number'], + rx_group_id: employer_general['group_number'], + help_desk: fairos_info.help_desk, + customer_service: fairos_info.customer_service, + web_url: fairos_info.web_url, + pl_plan_key: employer_general['pl_plan_key'] + ) + + number_of_plans = params[:number_of_plans].to_i + + number_of_plans.each do |i| + value['benefits'].each do |ben_key, ben_value| + Vhcs::HLEgglestonCardBenefit.create( + plan_id: value['plan_id'], + benefit_desc: ben_value["desc"], + benefit: ben_value["value"], + sequence: ben_key, + plan_key: employer_general['pl_plan_key'] + ) + end + end + + respond_to do |format| + if hl_plan_code.save && hlrx_cros_ref.save + format.html { redirect_to '/dev_tools/new_id_card_setup', notice: "Card setup was successfully created." } + # format.json { render :show, status: :created, location: @employer } + else + format.html { render :new_id_card_setup, status: :unprocessable_entity } + # format.json { render json: @employer.errors, status: :unprocessable_entity } + end + end + end + +end diff --git a/app/form_builders/tailwind_form_builder.rb b/app/form_builders/tailwind_form_builder.rb new file mode 100644 index 0000000..b680ce2 --- /dev/null +++ b/app/form_builders/tailwind_form_builder.rb @@ -0,0 +1,106 @@ +class TailwindFormBuilder < ActionView::Helpers::FormBuilder + class_attribute :text_field_helpers, default: field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field] + # leans on the FormBuilder class_attribute `field_helpers` + # you'll want to add a method for each of the specific helpers listed here if you want to style them + + TEXT_FIELD_STYLE = "bg-gray-200 rounded py-2 px-4 text-bluetang font-semibold leading-tight focus:outline-none focus:bg-white".freeze + SELECT_FIELD_STYLE = "block bg-gray-200 text-gray-700 py-2 px-4 rounded leading-tight focus:outline-none focus:bg-white".freeze + SUBMIT_BUTTON_STYLE = "shadow bg-bronze focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded hover:bg-copper".freeze + + text_field_helpers.each do |field_method| + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{field_method}(method, options = {}) + if options.delete(:tailwindified) + super + else + text_like_field(#{field_method.inspect}, method, options) + end + end + RUBY_EVAL + end + + def submit(value = nil, options = {}) + custom_opts, opts = partition_custom_opts(options) + classes = apply_style_classes(SUBMIT_BUTTON_STYLE, custom_opts) + + super(value, {class: classes}.merge(opts)) + end + + def select(method, choices = nil, options = {}, html_options = {}, &block) + custom_opts, opts = partition_custom_opts(options) + classes = apply_style_classes(SELECT_FIELD_STYLE, custom_opts, method) + + labels = labels(method, custom_opts[:label], options) + field = super(method, choices, opts, html_options.merge({class: classes}), &block) + + labels + field + end + + private + + def text_like_field(field_method, object_method, options = {}) + custom_opts, opts = partition_custom_opts(options) + + classes = apply_style_classes(TEXT_FIELD_STYLE, custom_opts, object_method) + + field = send(field_method, object_method, { + class: classes, + title: errors_for(object_method)&.join(" ") + }.compact.merge(opts).merge({tailwindified: true})) + + labels = labels(object_method, custom_opts[:label], options) + + labels + field + end + + def labels(object_method, label_options, field_options) + label = tailwind_label(object_method, label_options, field_options) + error_label = error_label(object_method, field_options) + + @template.content_tag("div", label + error_label, {class: "flex flex-col items-start"}) + end + + def tailwind_label(object_method, label_options, field_options) + text, label_opts = if label_options.present? + [label_options[:text], label_options.except(:text)] + else + [nil, {}] + end + + label_classes = label_opts[:class] || "block text-platinum font-bold md:text-right mb-1 md:mb-0 pr-4" + label_classes += " text-yellow-800 dark:text-yellow-400" if field_options[:disabled] + label(object_method, text, { + class: label_classes + }.merge(label_opts.except(:class))) + end + + def error_label(object_method, options) + if errors_for(object_method).present? + error_message = @object.errors[object_method].collect(&:titleize).join(", ") + tailwind_label(object_method, {text: error_message, class: " font-bold text-red-500"}, options) + end + end + + def border_color_classes(object_method) + if errors_for(object_method).present? + " border-2 border-red-400 focus:border-rose-200" + else + " border border-platinum focus:border-yellow-700" + end + end + + def apply_style_classes(classes, custom_opts, object_method = nil) + classes + border_color_classes(object_method) + " #{custom_opts[:class]}" + end + + CUSTOM_OPTS = [:label, :class].freeze + def partition_custom_opts(opts) + opts.partition { |k, v| CUSTOM_OPTS.include?(k) }.map(&:to_h) + end + + def errors_for(object_method) + return unless @object.present? && object_method.present? + + @object.errors[object_method] + end +end \ No newline at end of file diff --git a/app/forms/employer_setup_form.rb b/app/forms/employer_setup_form.rb new file mode 100644 index 0000000..d8cb4ee --- /dev/null +++ b/app/forms/employer_setup_form.rb @@ -0,0 +1,57 @@ +class EmployerSetupForm + include ActiveModel::Model + include ActiveModel::Attributes + + FIRST_STEP = "general_information" + + attribute :current_step, :string, default: FIRST_STEP + attribute :general_information_data + attribute :plans_data + attribute :network_exceptions_data + + def initialize(params = {}) + unless self.steps.first == FIRST_STEP + raise StepMisalignmentError, "FIRST_STEP does not match first entry in steps" + end + @general_information_data = EmployerSetupGeneralInformationForm.new(attributes[:general_information_data]) + @plans_data = EmployerSetupPlansForm.new(attributes[:plans_data]) + @network_exceptions_data = EmployerSetupNetworkExceptionsForm.new(attributes[:network_exceptions_data]) + end + + def steps + %w[general_information plans network_exceptions summary] + end + + def current_step_view + "employer_setup/#{self.current_step}" + end + + def next_step + index = steps.index(current_step) + if index && index < steps.length - 1 + if steps[index + 1] == 'network_exceptions' && general_information_data.number_of_additional_network_logos == 0 + steps[index + 2] + else + steps[index + 1] + end + end + end + + def previous_step + index = steps.index(current_step) + steps[index - 1] if index && index > 0 + end + + def save + if valid? + pl_plan_key = attributes[:general_information_data][:pl_plan_key] + EmployerSetupGeneralInformationForm.new(attributes[:general_information_data]).save + EmployerSetupPlansForm.new(attributes[:plans_data]).save(pl_plan_key) + EmployerSetupNetworkExceptionsForm.new(attributes[:network_exceptions_data]).save(pl_plan_key) + true + else + false + end + end + +end \ No newline at end of file diff --git a/app/forms/employer_setup_form_pre_split.rb b/app/forms/employer_setup_form_pre_split.rb new file mode 100644 index 0000000..dad07a2 --- /dev/null +++ b/app/forms/employer_setup_form_pre_split.rb @@ -0,0 +1,62 @@ +class EmployerSetupForm + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :current_step, :string, default: "general" + attribute :name, :string + attribute :employer_logo + attribute :group_number, :string + attribute :pl_plan_key, :string + attribute :effect_date, :string + attribute :number_of_plans, :integer + attribute :network, :string + attribute :number_of_additional_network_logos, :integer + attribute :plans, array: true, default: -> { [] } +# attribute :benefit_descs, :hash, default: -> { {} } + attribute :network_exceptions, array: true, default: -> { [] } + + attr_accessor :benefit_descs + + # Define validations based on the current step + with_options on: :general_info do + validates :name, presence: true + validates :employer_logo, presence: true + validates :group_number, presence: true + validates :pl_plan_key, presence: true + validates :effect_date, presence: true + validates :number_of_plans, presence: true + validates :network, presence: true + # validates :number_of_additional_network_logos, presence: true if network = "cigna+" + end + + with_options on: :plan_info do + validates :plans, presence: true + # validates :benefit_descs, presence: true + end + + with_options on: :network_info do + # validates :network_exceptions, presence: true if number_of_additional_network_logos > 0 + end + +# def initialize(params = {}) +# super(params) +# # Ensure the attribute is a hash after initialization +# @benefit_descs = params[:benefit_descs].to_h if params[:benefit_descs] +# end + + def benefit_descs + @benefit_descs ||= {} + end + + def save + # Implement logic to save data to models after all steps are complete + # For example, create a User record with the collected data + if valid? && step == total_steps + # User.create!(name: name, email: email, password: password) + true + else + false + end + end + +end \ No newline at end of file diff --git a/app/forms/employer_setup_general_information_form.rb b/app/forms/employer_setup_general_information_form.rb new file mode 100644 index 0000000..8182d46 --- /dev/null +++ b/app/forms/employer_setup_general_information_form.rb @@ -0,0 +1,72 @@ +class EmployerSetupGeneralInformationForm + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :name, :string + attribute :employer_logo + attribute :group_number, :string + attribute :dental, :boolean + attribute :pl_plan_key, :string + attribute :effect_date, :string + attribute :number_of_plans, :integer + attribute :network, :string + attribute :number_of_additional_network_logos, :integer + + validates :name, presence: true + validates :employer_logo, presence: true + validates :group_number, presence: true + validates :pl_plan_key, presence: true + validates :effect_date, presence: true + validates :number_of_plans, presence: true + validates :network, presence: true + # validates :number_of_additional_network_logos, presence: true if network = "cigna+" + +# def initialize(params = {}) +# super(params) +# # Ensure the attribute is a hash after initialization +# @benefit_descs = params[:benefit_descs].to_h if params[:benefit_descs] +# end + + def save + # Implement logic to save data to models after all steps are complete + # For example, create a User record with the collected data + if valid? + hl_plan_code = Vhcs::HlPlanCode.create!( + group_number: group_number, + medical_number: group_number, + dental_number: '', + plan_key: pl_plan_key, + effect_date: effect_date + ) + + # Replace fairos_info with template like for benefits + fairos_info = Vhcs::HLRXCrosRef.where(pl_plan_key: 52).first + hlrx_cros_ref = Vhcs::HLRXCrosRef.create!( + group_no: group_number, + rx_group_id: group_number, + help_desk: fairos_info.help_desk, + customer_service: fairos_info.customer_service, + web_url: fairos_info.web_url, + pl_plan_key: pl_plan_key + ) + + web_employer = BrittonWeb::Employers.create!( + pl_plan_key: pl_plan_key, + dental_plan: dental, + single_card_template: 'FairosRxIDCard', + logo: employer_logo.filename + ) + + BrittonWeb::NetworkLogos.create!( + employer: web_employer, + net_logo: network, + default: true + ) + + true + else + false + end + end + +end \ No newline at end of file diff --git a/app/forms/employer_setup_network_exceptions_form.rb b/app/forms/employer_setup_network_exceptions_form.rb new file mode 100644 index 0000000..fd87609 --- /dev/null +++ b/app/forms/employer_setup_network_exceptions_form.rb @@ -0,0 +1,46 @@ +class EmployerSetupNetworkExceptionsForm + include ActiveModel::Model + include ActiveModel::Attributes + + Network_exception = Struct.new(:network_logo, :exceptions) + attribute :network_exceptions, :array_of_items, default: -> { [] } + + # validates :network_exceptions, presence: true if number_of_additional_network_logos > 0 + +# def initialize(params = {}) +# super(params) +# # Ensure the attribute is a hash after initialization +# @benefit_descs = params[:benefit_descs].to_h if params[:benefit_descs] +# end + + def save(pl_plan_key) + # Implement logic to save data to models after all steps are complete + # For example, create a User record with the collected data + if valid? + employer = BrittonWeb::Employers.find_by(pl_plan_key: pl_plan_key) + + network_exceptions.each do |ne| + BrittonWeb::NetworkLogos.create!( + employer: employer, + net_logo: ne.network_logo, + exception_type: ne.type, + exception_value: ne.value, + default: false + ) + end + true + else + false + end + end + +end + + +class NetworkException + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :network_logo + attribute :exceptions, array: true, default: -> { [] } +end \ No newline at end of file diff --git a/app/forms/employer_setup_plans_form.rb b/app/forms/employer_setup_plans_form.rb new file mode 100644 index 0000000..1261e2b --- /dev/null +++ b/app/forms/employer_setup_plans_form.rb @@ -0,0 +1,49 @@ +class EmployerSetupPlansForm + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :plans, array: true, default: -> { [] } +# attribute :benefit_descs, :hash, default: -> { {} } + + attr_accessor :id_card_templates + attr_accessor :id_card_template_benefits + attr_accessor :benefit_descs + + validates :plans, presence: true + # validates :benefit_descs, presence: true + + + def initialize(params = {}) + super(params) + + @id_card_templates = IdCardBenefitsTemplate.where.not(title: "BLANK") + @id_card_template_benefits = IdCardBenefitsTemplate.find_by(title: "BLANK").id_card_benefits.sort_by(&:sequence) + end + + # def benefit_descs + # @benefit_descs ||= {} + # end + + def save(pl_plan_key) + # Implement logic to save data to models after all steps are complete + # For example, create a User record with the collected data + if valid? + plans.each do |plan| + plan_id = plan.delete(:plan_id) + plan.each do |key, value| + Vhcs::HLEgglestonCardBenefit.create( + plan_id: plan_id, + benefit_desc: benefit_descs["#{key}"], + benefit: value, + sequence: key, + plan_key: pl_plan_key + ) + end + end + true + else + false + end + end + +end \ No newline at end of file diff --git a/app/helpers/id_card_employers_helper.rb b/app/helpers/id_card_employers_helper.rb new file mode 100644 index 0000000..e5e3a5f --- /dev/null +++ b/app/helpers/id_card_employers_helper.rb @@ -0,0 +1,2 @@ +module IdCardEmployersHelper +end diff --git a/app/javascript/application.js b/app/javascript/application.js index 04d0ac4..a31a955 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -3,5 +3,3 @@ import "@hotwired/turbo-rails" import "controllers" import "trix" import "@rails/actiontext" - -console.log('Hello World from application.js'); diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js index d865bdd..1213e85 100644 --- a/app/javascript/controllers/application.js +++ b/app/javascript/controllers/application.js @@ -7,5 +7,3 @@ application.debug = false window.Stimulus = application export { application } - -console.log('Hello World from controllers/application.js'); diff --git a/app/javascript/controllers/benefits_template_picker_controller.js b/app/javascript/controllers/benefits_template_picker_controller.js new file mode 100644 index 0000000..02d5fb2 --- /dev/null +++ b/app/javascript/controllers/benefits_template_picker_controller.js @@ -0,0 +1,38 @@ +import { Controller } from "@hotwired/stimulus"; + +export default class extends Controller { + static values = { url: String }; + static targets = ["benefit_1", "benefit_2", "benefit_3", "benefit_4", "benefit_5", "benefit_6", "benefit_7", "benefit_8", "benefit_9", "benefit_10", "benefit_11", "benefit_12", "benefit_13", "benefit_14"]; + + async fetchData(event) { + const templateId = event.target.value; + if (!templateId) { + this.clearFields(); + return; + } + + const url = "/id_card_benefits_templates/get_templates_benefits/:id".replace(':id', templateId); + const response = await fetch(url); + const templateBenefitsData = await response.json(); + + + + this.nameTarget.value = templateData.name; + this.descriptionTarget.value = templateData.description; + } + + clearFields() { + this.nameTarget.value = ''; + this.descriptionTarget.value = ''; + } + + updateFields(templateBenefitsData) { + templateBenefitsData.forEach(function(benefit) { + const propertyName = `benefit_${benefit.sequence}` + this[propertyName].value = benefit.benefit + }); + } +} + +// this[propertyName] +// const propertyName = `${valueName}Value`; diff --git a/app/javascript/controllers/dynamic_plan_fields_controller.js b/app/javascript/controllers/dynamic_plan_fields_controller.js new file mode 100644 index 0000000..b935f54 --- /dev/null +++ b/app/javascript/controllers/dynamic_plan_fields_controller.js @@ -0,0 +1,37 @@ +import { Controller } from "@hotwired/stimulus"; + +export default class extends Controller { + static targets = ["numberInput", "planFieldsContainer", "template"]; + + connect() { + this.updatePlanFields(); + } + + updatePlanFields() { + const desiredCount = parseInt(this.numberInputTarget.value || 1, 10); + const currentCount = this.planFieldsContainerTarget.children.length; + + if (desiredCount > currentCount) { + this.addPlanFields(desiredCount - currentCount); + } else if (desiredCount < currentCount) { + this.removePlanFields(currentCount - desiredCount); + } + } + + addPlanFields(count) { + for (let i = 0; i < count; i++) { + const newPlanField = this.templateTarget.content.cloneNode(true); + // Replace '__INDEX__' with a unique value for nested attributes + // e.g., using Date.now() or a counter + const uniqueIndex = Date.now() + i; + newPlanField.innerHTML = newPlanField.innerHTML.replace(/__INDEX__/g, uniqueIndex); + this.planFieldsContainerTarget.appendChild(newPlanField); + } + } + + removePlanFields(count) { + for (let i = 0; i < count; i++) { + this.planFieldsContainerTarget.lastElementChild.remove(); + } + } +} \ No newline at end of file diff --git a/app/javascript/controllers/form_toggle_controller.js b/app/javascript/controllers/form_toggle_controller.js new file mode 100644 index 0000000..dda434d --- /dev/null +++ b/app/javascript/controllers/form_toggle_controller.js @@ -0,0 +1,22 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["divA"] // Add targets for all your divs + + connect() { + this.toggleDivs() // Call on connect to set initial state + } + + toggleDivs() { + const selectedValue = this.element.querySelector('select').value; + console.log("sv: ") + + // Hide all divs first + this.divATarget.classList.add("hidden"); + + // Show the relevant div based on selection + if (selectedValue === "cig+") { + this.divATarget.classList.remove("hidden"); + } + } +} \ No newline at end of file diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 08dc537..caaf180 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,4 +2,6 @@ class ApplicationRecord < ActiveRecord::Base primary_abstract_class + + establish_connection :dev_tools end diff --git a/app/models/article.rb b/app/models/article.rb deleted file mode 100644 index f245902..0000000 --- a/app/models/article.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Article < ApplicationRecord - has_many :comments, dependent: :destroy - has_rich_text :content - validates_presence_of :title -end diff --git a/app/models/comment.rb b/app/models/comment.rb deleted file mode 100644 index 48e21c4..0000000 --- a/app/models/comment.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Comment < ApplicationRecord - belongs_to :article - broadcasts_to :article -end diff --git a/app/models/id_card_benefit.rb b/app/models/id_card_benefit.rb new file mode 100644 index 0000000..f903ec7 --- /dev/null +++ b/app/models/id_card_benefit.rb @@ -0,0 +1,3 @@ +class IdCardBenefit < ApplicationRecord + belongs_to :id_card_benefits_template +end diff --git a/app/models/id_card_benefits_template.rb b/app/models/id_card_benefits_template.rb new file mode 100644 index 0000000..f8ffe76 --- /dev/null +++ b/app/models/id_card_benefits_template.rb @@ -0,0 +1,5 @@ +class IdCardBenefitsTemplate < ApplicationRecord + + has_many :id_card_benefits + +end diff --git a/app/models/vhcs/hl_eggleston_card_benefit.rb b/app/models/vhcs/hl_eggleston_card_benefit.rb new file mode 100644 index 0000000..dfa735f --- /dev/null +++ b/app/models/vhcs/hl_eggleston_card_benefit.rb @@ -0,0 +1,15 @@ +module Vhcs + class HlEgglestonCardBenefit < VhcsRecord + + self.table_name = 'HLEgglestonCardBenefit' + + alias_attribute :id, :Id + alias_attribute :plan_id, :PlanId + alias_attribute :benefit_desc, :BenefitDesc + alias_attribute :benefit, :Benefit + alias_attribute :sequence, :Sequence + alias_attribute :plan_key, :PlanKey + + + end +end \ No newline at end of file diff --git a/app/models/vhcs/hl_plan_code.rb b/app/models/vhcs/hl_plan_code.rb new file mode 100644 index 0000000..5a09301 --- /dev/null +++ b/app/models/vhcs/hl_plan_code.rb @@ -0,0 +1,14 @@ +module VHCS + class HlPlanCode < VhcsRecord + + self.table_name = 'HlPlanCode' + + alias_attribute :id, :ID + alias_attribute :group_number, :GroupNumber + alias_attribute :medical_number, :MedicalNumber + alias_attribute :dental_number, :DentalNumber + alias_attribute :plan_key, :PlanKey + alias_attribute :effect_date, :EffectDate + + end +end \ No newline at end of file diff --git a/app/models/vhcs/hlrx_cros_ref.rb b/app/models/vhcs/hlrx_cros_ref.rb new file mode 100644 index 0000000..a39c19f --- /dev/null +++ b/app/models/vhcs/hlrx_cros_ref.rb @@ -0,0 +1,15 @@ +module Vhcs + class HLRXCrosRef < VhcsRecord + + self.table_name = 'HLRXCrosRef' + + alias_attribute :group_no, :GroupNo + alias_attribute :rx_group_id, :RXGroupID + alias_attribute :help_desk, :HelpDesk + alias_attribute :customer_service, :CustomerService + alias_attribute :web_url, :WebUrl + alias_attribute :pl_plan_key, :PLPlanKey + + + end +end \ No newline at end of file diff --git a/app/models/vhcs_record.rb b/app/models/vhcs_record.rb new file mode 100644 index 0000000..2f37be3 --- /dev/null +++ b/app/models/vhcs_record.rb @@ -0,0 +1,5 @@ +class VhcsRecord < ActiveRecord::Base + self.abstract_class = true +# establish_connection :vhcs + connects_to database: { writing: :vhcs, reading: :vhcs } +end \ No newline at end of file diff --git a/app/types/array_of_items_type.rb b/app/types/array_of_items_type.rb new file mode 100644 index 0000000..64ce32e --- /dev/null +++ b/app/types/array_of_items_type.rb @@ -0,0 +1,23 @@ +class ArrayOfItemsType < ActiveModel::Type::Value + # The `cast` method is used to convert an incoming value into the desired type. + def cast(value) + return unless value.present? + + Array.wrap(value).map do |item_data| + # Assuming item_data is a hash, this creates an instance of the Item class. + # You can modify this part to match your object's initializer. + if item_data.is_a?(Hash) + Item.new(item_data) + else + item_data # Return the item as-is if it's already an object + end + end + end + + # This method is used when defining the type to convert it to a string for serialization. + def serialize(value) + value + end +end + +ActiveModel::Type.register(:array_of_items, ArrayOfItemsType) \ No newline at end of file diff --git a/app/views/employer_setup/general.html.erb b/app/views/employer_setup/general.html.erb new file mode 100644 index 0000000..4eb6c28 --- /dev/null +++ b/app/views/employer_setup/general.html.erb @@ -0,0 +1,56 @@ +