From 67fd15c75c39863a956d1cf3edfdd7f6d038d2b2 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Wed, 13 Aug 2025 13:51:54 +0530 Subject: [PATCH] wip after changing email show confirmation page within new website --- .../account/change-email-confirm-page.cy.js | 24 ++++++++++ .../components/public/JKConfirmEmailChange.js | 47 ++++++++++++++++++ jam-ui/src/helpers/rest.js | 48 ++++++++++++++----- jam-ui/src/layouts/JKPublicRoutes.js | 2 + ...1_add_profile_complete_columns_to_users.rb | 10 +--- ...0250322000000_affiliate_tracking_totals.rb | 24 +++++----- ...dd_gear_setup_reminder_columns_to_users.rb | 7 --- ruby/lib/jam_ruby/models/user_observer.rb | 2 +- web/app/controllers/api_users_controller.rb | 9 ++++ web/config/routes.rb | 1 + web/spec/requests/users_api_spec.rb | 14 +++++- 11 files changed, 147 insertions(+), 41 deletions(-) create mode 100644 jam-ui/cypress/e2e/account/change-email-confirm-page.cy.js create mode 100644 jam-ui/src/components/public/JKConfirmEmailChange.js diff --git a/jam-ui/cypress/e2e/account/change-email-confirm-page.cy.js b/jam-ui/cypress/e2e/account/change-email-confirm-page.cy.js new file mode 100644 index 000000000..c4751a10c --- /dev/null +++ b/jam-ui/cypress/e2e/account/change-email-confirm-page.cy.js @@ -0,0 +1,24 @@ +/// +import makeFakeUser from '../../factories/user'; +describe('Change Email Confirm Page', () => { + beforeEach(() => { + // Log in to the application or navigate to the account page + // where the change email feature is available + const currentUser = makeFakeUser({ + email: 'sam@example.com' + }); + cy.stubAuthenticate({ ...currentUser }); + }); + + it('should display the confirm page when visiting the confirm URL', () => { + // Replace with a realistic token for your app if needed + const token = 'dummy-confirm-token'; + + // Visit the confirm URL + cy.visit(`/public/confirm-email-change?token=${token}`); + + // Assert that the JKChangeEmailConfirm page is rendered + // Adjust selectors/texts as per your actual component + cy.contains('Change Email Confirmation').should('be.visible'); + }); +}); \ No newline at end of file diff --git a/jam-ui/src/components/public/JKConfirmEmailChange.js b/jam-ui/src/components/public/JKConfirmEmailChange.js new file mode 100644 index 000000000..f9d18730d --- /dev/null +++ b/jam-ui/src/components/public/JKConfirmEmailChange.js @@ -0,0 +1,47 @@ +import React from 'react' +import { useLocation } from "react-router-dom"; +import { Card, CardBody, CardText, CardTitle } from 'reactstrap'; +import PropTypes from 'prop-types'; +import { useState, useEffect } from 'react'; +import { updateEmail } from '../../helpers/rest'; + +const JKConfirmEmailChange = () => { + const location = useLocation(); + const params = new URLSearchParams(location.search); + const token = params.get('token'); + + const [success, setSuccess] = useState(false); + + useEffect(() => { + if (token) { + updateEmail(token) + .then(response => { + if (response.status === 200) { + setSuccess(true); + } else { + setSuccess(false); + } + }) + .catch(() => { + setSuccess(false); + }); + } + }, [token]); + + return ( + + + Unsubscribe from JamKazam emails + + { + success? + 'Your email has been successfully updated.' : + 'Loading...' + } + + + + ) +} + +export default JKConfirmEmailChange \ No newline at end of file diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js index a1ec75a58..c6bb87894 100644 --- a/jam-ui/src/helpers/rest.js +++ b/jam-ui/src/helpers/rest.js @@ -101,8 +101,8 @@ export const getInstruments = () => { export const getCurrentUser = () => { return new Promise((resolve, reject) => { apiFetch('/me') - .then(response => resolve(response)) - .catch(error => reject(error)) + .then(response => resolve(response)) + .catch(error => reject(error)) }) } @@ -289,7 +289,7 @@ export const getCities = (countryId, regionId) => { export const postUpdateAccountEmail = (userId, options) => { const { email, current_password } = options; return new Promise((resolve, reject) => { - apiFetch(`/users/${userId}/update_email`, { + apiFetch(`/users/${userId}/update_email_alt`, { method: 'POST', body: JSON.stringify({ update_email: email, current_password }) }) @@ -339,7 +339,7 @@ export const requstResetForgotPassword = email => { }) .then(response => resolve(response)) .catch(error => reject(error)); - }); + }); }; export const resetForgotPassword = (options = {}) => { @@ -491,9 +491,9 @@ export const getJamTrackPublic = options => { const { plan_code } = options; return new Promise((resolve, reject) => { // This does not make sense; historical reasons here - apiFetch(`/jamtracks/band/${plan_code}?${new URLSearchParams({plan_code})}`) - .then(response => resolve(response)) - .catch(error => reject(error)); + apiFetch(`/jamtracks/band/${plan_code}?${new URLSearchParams({ plan_code })}`) + .then(response => resolve(response)) + .catch(error => reject(error)); }); }; @@ -683,7 +683,7 @@ export const createAlert = (subject, data) => { return new Promise((resolve, reject) => { apiFetch(`/alerts`, { method: 'POST', - body: JSON.stringify({subject, data}) + body: JSON.stringify({ subject, data }) }) .then(response => resolve(response)) .catch(error => reject(error)); @@ -700,8 +700,8 @@ export const getClientDownloads = () => { export const getObsPluginDownloads = () => { return new Promise((resolve, reject) => { apiFetch(`/artifacts/OBSPlugin`) - .then(response => resolve(response)) - .catch(error => reject(error)); + .then(response => resolve(response)) + .catch(error => reject(error)); }); } @@ -719,7 +719,7 @@ export const paypalPlaceOrder = (options = {}) => { export const submitStripe = (options = {}) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { apiFetch(`/stripe`, { method: 'POST', body: JSON.stringify(options) @@ -749,4 +749,28 @@ export const updatePayment = (options = {}) => { .then(response => resolve(response)) .catch(error => reject(error)); }); -}; \ No newline at end of file +}; + +// function postUpdateEmail(email, current_password) { + +// var url = "/api/users/" + context.JK.currentUserId + "/update_email"; +// return $.ajax({ +// type: "POST", +// dataType: "json", +// contentType: 'application/json', +// url: url, +// data: JSON.stringify({ update_email: email, current_password: current_password }), +// processData: false +// }); +// } + +export const updateEmail = (userId, email, current_password) => { + return new Promise((resolve, reject) => { + apiFetch(`/users/${userId}/update_email`, { + method: 'POST', + body: JSON.stringify({ update_email: email, current_password }) + }) + .then(response => resolve(response)) + .catch(error => reject(error)); + }); +} \ No newline at end of file diff --git a/jam-ui/src/layouts/JKPublicRoutes.js b/jam-ui/src/layouts/JKPublicRoutes.js index f808dd81d..f4330eb0c 100644 --- a/jam-ui/src/layouts/JKPublicRoutes.js +++ b/jam-ui/src/layouts/JKPublicRoutes.js @@ -11,6 +11,7 @@ import JKUnsubscribe from '../components/public/JKUnsubscribe'; import JKDownloads from '../components/public/JKDownloads'; import JKDownloadsLegacy from '../components/public/JKDownloadsLegacy'; import JKObsDownloads from '../components/public/JKObsDownloads'; +import JKConfirmEmailChange from '../components/public/JKConfirmEmailChange'; import JKJamTracksLanding from '../components/jamtracks/JKJamTracksLandingDev'; import JKJamTracksArtistLanding from '../components/jamtracks/JKJamTracksArtistLandingDev'; @@ -23,6 +24,7 @@ const JKPublicRoutes = ({ match: { url } }) => ( + diff --git a/ruby/db/migrate/20250227125441_add_profile_complete_columns_to_users.rb b/ruby/db/migrate/20250227125441_add_profile_complete_columns_to_users.rb index 3d6a08c66..2fef976d3 100644 --- a/ruby/db/migrate/20250227125441_add_profile_complete_columns_to_users.rb +++ b/ruby/db/migrate/20250227125441_add_profile_complete_columns_to_users.rb @@ -19,11 +19,5 @@ execute "ALTER TABLE users DROP COLUMN profile_complete_reminder3_sent_at" end end -=begin - ALTER TABLE users ADD COLUMN profile_completed_at TIMESTAMP; - CREATE INDEX index_users_on_profile_completed_at ON users USING btree (profile_completed_at); - ALTER TABLE users ADD COLUMN profile_complete_reminder1_sent_at TIMESTAMP; - ALTER TABLE users ADD COLUMN profile_complete_reminder2_sent_at TIMESTAMP; - ALTER TABLE users ADD COLUMN profile_complete_reminder3_sent_at TIMESTAMP; - UPDATE users set profile_completed_at=NOW() WHERE users.id IN (SELECT player_id FROM musicians_instruments) OR users.id IN (SELECT player_id FROM genre_players); - end \ No newline at end of file + + diff --git a/ruby/db/migrate/20250322000000_affiliate_tracking_totals.rb b/ruby/db/migrate/20250322000000_affiliate_tracking_totals.rb index 6d3140c6f..5ee85575b 100644 --- a/ruby/db/migrate/20250322000000_affiliate_tracking_totals.rb +++ b/ruby/db/migrate/20250322000000_affiliate_tracking_totals.rb @@ -6,7 +6,7 @@ # affiliate_quarterly_payments.subscription_due_amount_in_cents # affiliate_monthly_payments.jamtrack_due_amount_in_cents # affiliate_monthly_payments.subscription_due_amount_in_cents -class AddTrackingTotalsToAffiliatePartners < ActiveRecord::Migration +class AffiliateTrackingTotals < ActiveRecord::Migration def self.up execute "ALTER TABLE affiliate_partners ADD COLUMN jamtrack_cumulative_earnings_in_cents INTEGER NOT NULL DEFAULT 0" @@ -16,16 +16,16 @@ class AddTrackingTotalsToAffiliatePartners < ActiveRecord::Migration execute "ALTER TABLE affiliate_partners ADD COLUMN subscriptions_current_quarter_in_cents INTEGER NOT NULL DEFAULT 0" execute "ALTER TABLE affiliate_partners ADD COLUMN jamtracks_sold INTEGER NOT NULL DEFAULT 0" execute "ALTER TABLE affiliate_partners ADD COLUMN subscriptions_count INTEGER NOT NULL DEFAULT 0" - execute "ALTER TABLE affiliate_quarterly_payments ADD COLUMN subscriptions_count INTEGER NOT NULL DEFAULT 0" - execute "ALTER TABLE affiliate_monthy_payments ADD COLUMN subscriptions_count INTEGER NOT NULL DEFAULT 0" - execute "ALTER TABLE affiliate_quarterly_payments ADD COLUMN jamtrack_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" - execute "ALTER TABLE affiliate_quarterly_payments ADD COLUMN subscription_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" - execute "ALTER TABLE affiliate_monthly_payments ADD COLUMN jamtrack_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" - execute "ALTER TABLE affiliate_monthly_payments ADD COLUMN subscription_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" + # execute "ALTER TABLE affiliate_quarterly_payments ADD COLUMN subscriptions_count INTEGER NOT NULL DEFAULT 0" + # execute "ALTER TABLE affiliate_monthy_payments ADD COLUMN subscriptions_count INTEGER NOT NULL DEFAULT 0" + # execute "ALTER TABLE affiliate_quarterly_payments ADD COLUMN jamtrack_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" + # execute "ALTER TABLE affiliate_quarterly_payments ADD COLUMN subscription_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" + #execute "ALTER TABLE affiliate_monthly_payments ADD COLUMN jamtrack_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" + #execute "ALTER TABLE affiliate_monthly_payments ADD COLUMN subscription_due_amount_in_cents INTEGER NOT NULL DEFAULT 0" execute "CREATE INDEX affiliate_partner_user_id_idx ON affiliate_partners USING btree (partner_user_id);" - execute "CREATE INDEX affiliate_quarterly_payments_closed_index ON affiliate_quarterly_payments USING btree (paid);" - execute "CREATE INDEX affiliate_quarterly_payments_paid_index ON affiliate_quarterly_payments USING btree (closed);" - execute "CREATE INDEX affiliate_monthly_payments_paid_index ON affiliate_monthly_payments USING btree (closed);" + # execute "CREATE INDEX affiliate_quarterly_payments_closed_index ON affiliate_quarterly_payments USING btree (paid);" + # execute "CREATE INDEX affiliate_quarterly_payments_paid_index ON affiliate_quarterly_payments USING btree (closed);" + # execute "CREATE INDEX affiliate_monthly_payments_paid_index ON affiliate_monthly_payments USING btree (closed);" end =begin @@ -54,7 +54,7 @@ class AddTrackingTotalsToAffiliatePartners < ActiveRecord::Migration execute "ALTER TABLE affiliate_partners DROP COLUMN jamtrack_cumulative_earnings_in_cents" execute "ALTER TABLE affiliate_partners DROP COLUMN subscriptions_cumulative_earnings_in_cents" execute "ALTER TABLE affiliate_partners DROP COLUMN subscriptions_count" - execute "ALTER TABLE affiliate_quarterly_payments DROP COLUMN jamtrack_due_amount_in_cents" - execute "ALTER TABLE affiliate_quarterly_payments DROP COLUMN subscription_due_amount_in_cents" + # execute "ALTER TABLE affiliate_quarterly_payments DROP COLUMN jamtrack_due_amount_in_cents" + # execute "ALTER TABLE affiliate_quarterly_payments DROP COLUMN subscription_due_amount_in_cents" end end diff --git a/ruby/db/migrate/20250511151844_add_gear_setup_reminder_columns_to_users.rb b/ruby/db/migrate/20250511151844_add_gear_setup_reminder_columns_to_users.rb index 2af9c834e..06185bbcf 100644 --- a/ruby/db/migrate/20250511151844_add_gear_setup_reminder_columns_to_users.rb +++ b/ruby/db/migrate/20250511151844_add_gear_setup_reminder_columns_to_users.rb @@ -11,10 +11,3 @@ execute "ALTER TABLE users DROP COLUMN gear_setup_reminder3_sent_at" end end - - -=begin - ALTER TABLE users ADD COLUMN gear_setup_reminder1_sent_at TIMESTAMP; -ALTER TABLE users ADD COLUMN gear_setup_reminder2_sent_at TIMESTAMP; -ALTER TABLE users ADD COLUMN gear_setup_reminder3_sent_at TIMESTAMP; - end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/user_observer.rb b/ruby/lib/jam_ruby/models/user_observer.rb index 94da9ebc7..7efb3b1f8 100644 --- a/ruby/lib/jam_ruby/models/user_observer.rb +++ b/ruby/lib/jam_ruby/models/user_observer.rb @@ -5,7 +5,7 @@ module JamRuby def after_save(user) if user.updating_email && !user.errors.any? - UserMailer.updating_email(user).deliver_now + UserMailer.begin_update_email(user).deliver_now elsif user.updated_email && !user.errors.any? UserMailer.updated_email(user).deliver_now elsif user.setting_password && !user.errors.any? diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index 1ef1e8d84..7b64b6863 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -615,6 +615,15 @@ class ApiUsersController < ApiController # NOTE: if you change confirm_email_link value below, you break outstanding email changes because links in user inboxes are broken confirm_email_link = confirm_email_url + "?token=" + do_bigin_complete_email(confirm_email_link) + end + + def begin_update_email_alt + confirm_email_link = ApplicationHelper.spa_base_uri + '/public/confirm-email-change' + "?token=" + do_bigin_complete_email(confirm_email_link) + end + + def do_bigin_complete_email(confirm_email_link) current_user.begin_update_email(params[:update_email], params[:current_password], confirm_email_link) if current_user.errors.any? diff --git a/web/config/routes.rb b/web/config/routes.rb index 3b174973c..a152b4561 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -511,6 +511,7 @@ Rails.application.routes.draw do # user account settings match '/users/:id/update_email' => 'api_users#begin_update_email', :via => :post, :as => 'begin_update_email' + match '/users/:id/update_email_alt' => 'api_users#begin_update_email_alt', :via => :post, :as => 'begin_update_email_alt' match '/users/update_email/:token' => 'api_users#finalize_update_email', :via => :post, :as => 'finalize_update_email' # user profile diff --git a/web/spec/requests/users_api_spec.rb b/web/spec/requests/users_api_spec.rb index f2630f149..cf2c68c11 100644 --- a/web/spec/requests/users_api_spec.rb +++ b/web/spec/requests/users_api_spec.rb @@ -248,6 +248,12 @@ describe "User API", :type => :api do return last_response end + def begin_update_email_alt(authenticated_user, update_email, validation_password, login = true) + login(authenticated_user.email, authenticated_user.password, 200, true) if login + post "/api/users/#{authenticated_user.id}/update_email_alt.json", { :update_email => update_email, :current_password => validation_password }.to_json, "CONTENT_TYPE" => 'application/json' + return last_response + end + def finalize_update_email(update_email_token) post "/api/users/update_email/#{update_email_token}.json", {}.to_json, "CONTENT_TYPE" => 'application/json' return last_response @@ -907,7 +913,7 @@ describe "User API", :type => :api do end ########## UPDATE EMAIL ######## - describe "update email" do + describe "update email", focus: true do describe "begin update email" do it "success" do last_response = begin_update_email(user, "not_taken_test@jamkazam.com", user.password) @@ -933,6 +939,12 @@ describe "User API", :type => :api do last_response.status.should == 403 UserMailer.deliveries.length.should == 0 end + + it "success alt" do + last_response = begin_update_email_alt(user, "not_taken_test_alt@jamkazam.com", user.password) + last_response.status.should == 200 + UserMailer.deliveries.length.should == 1 + end end describe "finalize update email" do