diff --git a/app/css/modals/finish-student-mentor-discussion.css b/app/css/modals/finish-student-mentor-discussion.css index d150b56252..36699d43ed 100644 --- a/app/css/modals/finish-student-mentor-discussion.css +++ b/app/css/modals/finish-student-mentor-discussion.css @@ -209,17 +209,28 @@ @apply mt-24; } } + .successful-donation-step { + .badge-container { + @apply flex items-center rounded-5 shadow-xsZ1 py-6 px-24 mb-32 border-1 border-lightGold; + background: var(--backgroundColorCAlert); + } + .c-badge-medallion { + height: 40px; + width: 40px; + @apply mr-8; + } + } & section.celebrate-step { - @apply p-64; + @apply p-64 rounded-8; margin: -40px -48px; @apply flex flex-col items-center justify-start; &.neon-cat { - background: rgb(15, 68, 113); + background: rgb(17, 49, 97); & .gif { - height: 160px; + height: 80px; @apply mb-24; } & h2 { @@ -227,9 +238,9 @@ } & p { @apply text-20 leading-150 text-white text-center; - @apply mb-48; + @apply mb-20; & strong { - @apply block mb-8; + @apply block mb-4; @apply font-semibold; } } diff --git a/app/helpers/react_components/donations/footer_form.rb b/app/helpers/react_components/donations/footer_form.rb index 0c79267b3b..e7f8fcea30 100644 --- a/app/helpers/react_components/donations/footer_form.rb +++ b/app/helpers/react_components/donations/footer_form.rb @@ -17,7 +17,7 @@ def to_s recaptcha_site_key: ENV.fetch('RECAPTCHA_SITE_KEY', Exercism.secrets.recaptcha_site_key), links: { settings: Exercism::Routes.donations_settings_url, - donate: Exercism::Routes.donate_url + success: Exercism::Routes.donate_url } } ) diff --git a/app/helpers/react_components/donations/form_with_modal.rb b/app/helpers/react_components/donations/form_with_modal.rb index 86ca938d68..68b6147730 100644 --- a/app/helpers/react_components/donations/form_with_modal.rb +++ b/app/helpers/react_components/donations/form_with_modal.rb @@ -16,7 +16,7 @@ def to_s recaptcha_site_key: ENV.fetch('RECAPTCHA_SITE_KEY', Exercism.secrets.recaptcha_site_key), links: { settings: Exercism::Routes.donations_settings_url, - donate: Exercism::Routes.donate_url + success: Exercism::Routes.donate_url } } ) diff --git a/app/helpers/react_components/student/mentoring_session.rb b/app/helpers/react_components/student/mentoring_session.rb index c8b199d347..2022d4c680 100644 --- a/app/helpers/react_components/student/mentoring_session.rb +++ b/app/helpers/react_components/student/mentoring_session.rb @@ -6,26 +6,41 @@ class MentoringSession < ReactComponent def to_s super( "student-mentoring-session", - { - user_handle: student.handle, - request: SerializeMentorSessionRequest.(request, student), - discussion: discussion ? SerializeMentorDiscussionForStudent.(discussion) : nil, - track: SerializeMentorSessionTrack.(track), - exercise: SerializeMentorSessionExercise.(exercise), - iterations:, - mentor: mentor_data, - track_objectives: user_track&.objectives.to_s, - out_of_date: solution.out_of_date?, - videos:, - links: { - exercise: Exercism::Routes.track_exercise_mentor_discussions_url(track, exercise), - create_mentor_request: Exercism::Routes.api_solution_mentor_requests_path(solution.uuid), - learn_more_about_private_mentoring: Exercism::Routes.doc_path(:using, "feedback/private"), - private_mentoring: solution.external_mentoring_request_url, - mentoring_guide: Exercism::Routes.doc_path(:using, "feedback/guide-to-being-mentored") + to_h + ) + end + + def to_h + { + user_handle: student.handle, + request: SerializeMentorSessionRequest.(request, student), + discussion: discussion ? SerializeMentorDiscussionForStudent.(discussion) : nil, + track: SerializeMentorSessionTrack.(track), + exercise: SerializeMentorSessionExercise.(exercise), + iterations:, + mentor: mentor_data, + track_objectives: user_track&.objectives.to_s, + out_of_date: solution.out_of_date?, + videos:, + donation: { + show_donation_modal:, + request: { + endpoint: Exercism::Routes.current_api_payments_subscriptions_url, + options: { + initial_data: AssembleCurrentSubscription.(current_user) + } } + }, + links: { + exercise: Exercism::Routes.track_exercise_mentor_discussions_url(track, exercise), + create_mentor_request: Exercism::Routes.api_solution_mentor_requests_path(solution.uuid), + learn_more_about_private_mentoring: Exercism::Routes.doc_path(:using, "feedback/private"), + private_mentoring: solution.external_mentoring_request_url, + mentoring_guide: Exercism::Routes.doc_path(:using, "feedback/guide-to-being-mentored"), + donations_settings: Exercism::Routes.donations_settings_url, + donate: Exercism::Routes.donate_url } - ) + } end private @@ -95,6 +110,14 @@ def iterations SerializeIterations.(solution.iterations, comment_counts:) end + + def show_donation_modal + return false if current_user.insider? + return false if current_user.donated_in_last_35_days? + + num_testimonials = current_user.provided_testimonials.count + (num_testimonials % 3).zero? + end end end end diff --git a/app/helpers/view_components/site_footer.rb b/app/helpers/view_components/site_footer.rb index 196fa3856c..47931c387d 100644 --- a/app/helpers/view_components/site_footer.rb +++ b/app/helpers/view_components/site_footer.rb @@ -19,7 +19,7 @@ def cache_key parts << ::Track.active.count parts << user_part parts << stripe_version - parts << "v1" + parts << "v2" parts.join(':') end diff --git a/app/javascript/components/donations/FooterForm.tsx b/app/javascript/components/donations/FooterForm.tsx index b5006d70b9..7f677978cd 100644 --- a/app/javascript/components/donations/FooterForm.tsx +++ b/app/javascript/components/donations/FooterForm.tsx @@ -5,15 +5,14 @@ import { CustomAmountInput } from './donation-form/CustomAmountInput' import { FormModal } from './footer-form/FormModal' import { GraphicalIcon } from '../common' import { Request } from '../../hooks/request-query' - -type Links = Record<'settings' | 'donate', string> +import { StripeFormLinks } from './Form' const PRESET_AMOUNTS = [currency(16), currency(32), currency(64), currency(128)] const DEFAULT_AMOUNT = currency(16) export type FooterFormProps = { request: Request - links: Links + links: StripeFormLinks userSignedIn: boolean captchaRequired: boolean recaptchaSiteKey: string diff --git a/app/javascript/components/donations/Form.tsx b/app/javascript/components/donations/Form.tsx index 16ae999f00..16a26ca3ff 100644 --- a/app/javascript/components/donations/Form.tsx +++ b/app/javascript/components/donations/Form.tsx @@ -10,14 +10,18 @@ import currency from 'currency.js' import { Request, useRequestQuery } from '../../hooks/request-query' import { FetchingBoundary } from '../FetchingBoundary' import { useQueryCache } from 'react-query' -import { FormWithModalLinks } from './FormWithModal' + +export type StripeFormLinks = { + success: string + settings: string +} const TabsContext = createContext({ current: 'subscription', switchToTab: () => null, }) -type FormAmount = { +export type FormAmount = { subscription: currency payment: currency } @@ -38,10 +42,10 @@ type Props = { onSuccess: (type: PaymentIntentType, amount: currency) => void userSignedIn: boolean captchaRequired: boolean - recaptchaSiteKey: string + recaptchaSiteKey?: string onProcessing?: () => void onSettled?: () => void - links: FormWithModalLinks + links: StripeFormLinks id?: string } @@ -185,7 +189,7 @@ export const Form = ({ } > ) } + +export default Form diff --git a/app/javascript/components/donations/FormWithModal.tsx b/app/javascript/components/donations/FormWithModal.tsx index 83167feaf6..46ffcde4d4 100644 --- a/app/javascript/components/donations/FormWithModal.tsx +++ b/app/javascript/components/donations/FormWithModal.tsx @@ -1,21 +1,17 @@ import React, { useState, useCallback } from 'react' import currency from 'currency.js' import { Request } from '@/hooks/request-query' -import { Form } from './Form' +import { Form, FormAmount, StripeFormLinks } from './Form' import SuccessModal from './SuccessModal' import { PaymentIntentType } from './stripe-form/useStripeForm' -export type FormWithModalLinks = { - donate: string - settings: string -} - type FormWithModalProps = { request: Request userSignedIn: boolean - links: FormWithModalLinks + links: StripeFormLinks captchaRequired: boolean recaptchaSiteKey: string + defaultAmount?: Partial } export default function FormWithModal({ @@ -24,6 +20,7 @@ export default function FormWithModal({ links, captchaRequired, recaptchaSiteKey, + defaultAmount, }: FormWithModalProps): JSX.Element { const [paymentMade, setPaymentMade] = useState(false) @@ -49,11 +46,12 @@ export default function FormWithModal({ links={links} recaptchaSiteKey={recaptchaSiteKey} captchaRequired={captchaRequired} + defaultAmount={defaultAmount} /> ) diff --git a/app/javascript/components/donations/StripeForm.tsx b/app/javascript/components/donations/StripeForm.tsx index e31f1ab9ce..1f9c29cb7c 100644 --- a/app/javascript/components/donations/StripeForm.tsx +++ b/app/javascript/components/donations/StripeForm.tsx @@ -13,7 +13,7 @@ export type StripeFormProps = { onSettled?: () => void userSignedIn: boolean captchaRequired: boolean - recaptchaSiteKey: string + recaptchaSiteKey?: string amount: currency submitButtonDisabled?: boolean confirmParamsReturnUrl: string @@ -75,7 +75,7 @@ export function StripeForm({ /> ) : null} - {captchaRequired ? ( + {captchaRequired && recaptchaSiteKey ? (