Skip to content

Commit 9dd240b

Browse files
authored
Phone verification frontend changes (#2106)
* Spruced up phone verification modals * Additional tweak to phone verification modal * Updated verify section of profile page to show both email and phone verification * Added verify account section to edit profile page and refactored * used feature flag to show new or legacy verify account section appropriately on profile page * fixed formatting * minor style adjustment
1 parent 5f3b092 commit 9dd240b

13 files changed

Lines changed: 280 additions & 83 deletions

File tree

.prettierignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ dist
1414
coverage
1515
storybook-static
1616
llm
17-
playwright-report
17+
playwright-report
18+
CLAUDE.md
19+
.cursor/

components/EditProfilePage/EditProfileHeader.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,19 @@ import { Role } from "../auth"
33
import { Col, Row } from "../bootstrap"
44
import { GearIcon, OutlineButton } from "../buttons"
55
import { ProfileEditToggle } from "components/ProfilePage/ProfileButtons"
6-
import { useFlags } from "components/featureFlags"
76

87
export const EditProfileHeader = ({
98
formUpdated,
109
onSettingsModalOpen,
11-
onGetVerifiedClick,
1210
uid,
13-
role,
14-
phoneVerified
11+
role
1512
}: {
1613
formUpdated: boolean
1714
onSettingsModalOpen: () => void
18-
onGetVerifiedClick?: () => void
1915
uid: string
2016
role: Role
21-
phoneVerified?: boolean
2217
}) => {
2318
const { t } = useTranslation("editProfile")
24-
const { phoneVerificationUI } = useFlags()
2519

2620
return (
2721
<Row className={`my-5`}>
@@ -36,25 +30,6 @@ export const EditProfileHeader = ({
3630
onClick={() => onSettingsModalOpen()}
3731
/>
3832
<ProfileEditToggle formUpdated={formUpdated} role={role} uid={uid} />
39-
{phoneVerificationUI &&
40-
(phoneVerified === true ? (
41-
<div className="d-flex align-items-center justify-content-center gap-1 py-1 col-12 text-capitalize text-nowrap">
42-
<span className="text-secondary">{t("verifiedUser")}</span>
43-
<img
44-
src="/images/verifiedUser.png"
45-
alt={t("verifiedUserBadgeAlt")}
46-
width={24}
47-
height={24}
48-
className="flex-shrink-0"
49-
/>
50-
</div>
51-
) : onGetVerifiedClick ? (
52-
<OutlineButton
53-
className={`py-1`}
54-
label={t("getVerified")}
55-
onClick={onGetVerifiedClick}
56-
/>
57-
) : null)}
5833
</Col>
5934
</Row>
6035
)

components/EditProfilePage/EditProfilePage.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import {
1717
import { EditProfileHeader } from "./EditProfileHeader"
1818
import { FollowingTab } from "./FollowingTab"
1919
import { PersonalInfoTab } from "./PersonalInfoTab"
20-
import PhoneVerificationModal from "./PhoneVerificationModal"
2120
import ProfileSettingsModal from "./ProfileSettingsModal"
21+
import { VerifyAccountSection } from "components/shared"
2222
import {
2323
StyledTabContent,
2424
TabNavItem,
@@ -88,8 +88,6 @@ export function EditProfileForm({
8888

8989
const [formUpdated, setFormUpdated] = useState(false)
9090
const [settingsModal, setSettingsModal] = useState<"show" | null>(null)
91-
const [showPhoneVerificationModal, setShowPhoneVerificationModal] =
92-
useState(false)
9391
const [notifications, setNotifications] = useState<Frequency>(
9492
notificationFrequency || "Weekly"
9593
)
@@ -114,7 +112,8 @@ export function EditProfileForm({
114112

115113
let isOrg = profile.role === "organization"
116114

117-
const isPendingUpgrade = useAuth().claims?.role === "pendingUpgrade"
115+
const { claims, user } = useAuth()
116+
const isPendingUpgrade = claims?.role === "pendingUpgrade"
118117

119118
isOrg = isOrg || isPendingUpgrade
120119

@@ -167,7 +166,7 @@ export function EditProfileForm({
167166
}
168167
]
169168

170-
const { followOrg } = useFlags()
169+
const { followOrg, phoneVerificationUI } = useFlags()
171170

172171
if (followOrg === false) {
173172
tabs.splice(2, 1)
@@ -181,11 +180,20 @@ export function EditProfileForm({
181180
<EditProfileHeader
182181
formUpdated={formUpdated}
183182
onSettingsModalOpen={onSettingsModalOpen}
184-
onGetVerifiedClick={() => setShowPhoneVerificationModal(true)}
185183
uid={uid}
186184
role={profile.role}
187-
phoneVerified={profile.phoneVerified}
188185
/>
186+
{phoneVerificationUI &&
187+
user &&
188+
(!user.emailVerified || !profile.phoneVerified) && (
189+
<div style={{ marginBottom: "3.5rem" }}>
190+
<VerifyAccountSection
191+
user={user}
192+
profile={profile}
193+
className=""
194+
/>
195+
</div>
196+
)}
189197
<TabContainer
190198
defaultActiveKey="about-you"
191199
activeKey={tabTitle}
@@ -216,10 +224,6 @@ export function EditProfileForm({
216224
onSettingsModalClose={() => setSettingsModal(null)}
217225
show={settingsModal === "show"}
218226
/>
219-
<PhoneVerificationModal
220-
show={showPhoneVerificationModal}
221-
onHide={() => setShowPhoneVerificationModal(false)}
222-
/>
223227
</>
224228
)
225229
}

components/ProfilePage/ProfilePage.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import { OrgContactInfo } from "./OrgContactInfo"
1414
import { ProfileAboutSection } from "./ProfileAboutSection"
1515
import { ProfileHeader } from "./ProfileHeader"
1616
import { ProfileLegislators } from "./ProfileLegislators"
17-
import { VerifyAccountSection } from "./VerifyAccountSection"
17+
import { VerifyAccountSection } from "components/shared"
18+
import { VerifyAccountSectionLegacy } from "./VerifyAccountSectionLegacy"
19+
import { useFlags } from "components/featureFlags"
1820

1921
export function ProfilePage(profileprops: {
2022
id: string
@@ -38,6 +40,8 @@ export function ProfilePage(profileprops: {
3840
number | undefined
3941
>()
4042

43+
const { phoneVerificationUI } = useFlags()
44+
4145
useEffect(() => {
4246
const countPublishedTestimony = async () => {
4347
const pubTestRef = query(
@@ -105,10 +109,24 @@ export function ProfilePage(profileprops: {
105109
profile={profile}
106110
/>
107111

108-
{isCurrentUser && !user.emailVerified ? (
112+
{!phoneVerificationUI && isCurrentUser && !user.emailVerified ? (
113+
<Row>
114+
<Col>
115+
<VerifyAccountSectionLegacy className="mb-4" user={user} />
116+
</Col>
117+
</Row>
118+
) : null}
119+
120+
{phoneVerificationUI &&
121+
isCurrentUser &&
122+
(!user.emailVerified || !profile.phoneVerified) ? (
109123
<Row>
110124
<Col>
111-
<VerifyAccountSection className="mb-4" user={user} />
125+
<VerifyAccountSection
126+
className="mb-4"
127+
user={user}
128+
profile={profile}
129+
/>
112130
</Col>
113131
</Row>
114132
) : null}

components/ProfilePage/VerifyAccountSection.tsx renamed to components/ProfilePage/VerifyAccountSectionLegacy.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { User } from "firebase/auth"
55
import { LoadingButton } from "components/buttons"
66
import { useTranslation } from "next-i18next"
77

8-
export const VerifyAccountSection = ({
8+
export const VerifyAccountSectionLegacy = ({
99
user,
1010
className
1111
}: {

components/EditProfilePage/PhoneVerificationModal.tsx renamed to components/shared/PhoneVerificationModal.tsx

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,16 @@ const AUTH_ERROR_CODE_TO_KEY: Record<string, string> = {
2828
"auth/operation-not-allowed": "phoneVerification.errors.operationNotAllowed"
2929
}
3030

31-
export default function PhoneVerificationModal({
31+
export function PhoneVerificationModal({
3232
show,
33-
onHide
34-
}: Pick<ModalProps, "show" | "onHide">) {
35-
const { t } = useTranslation("editProfile")
33+
onHide,
34+
onVerified
35+
}: Pick<ModalProps, "show" | "onHide"> & { onVerified?: () => void }) {
36+
const { t } = useTranslation("auth")
3637
const { user } = useAuth()
3738
const completePhoneVerification = useCompletePhoneVerification()
3839

39-
const [step, setStep] = useState<"phone" | "code">("phone")
40+
const [step, setStep] = useState<"phone" | "code" | "success">("phone")
4041
const [phone, setPhone] = useState("")
4142
const [code, setCode] = useState("")
4243
const [error, setError] = useState<string | null>(null)
@@ -137,7 +138,8 @@ export default function PhoneVerificationModal({
137138
if (completePhoneVerification.execute) {
138139
await completePhoneVerification.execute()
139140
}
140-
onHide?.()
141+
onVerified?.()
142+
setStep("success")
141143
} catch (err: unknown) {
142144
const code = (err as { code?: string })?.code
143145
setError(
@@ -165,6 +167,7 @@ export default function PhoneVerificationModal({
165167
onHide={onHide}
166168
aria-labelledby="phone-verification-modal"
167169
centered
170+
size="lg"
168171
>
169172
<Modal.Header closeButton>
170173
<Modal.Title id="phone-verification-modal">
@@ -178,7 +181,6 @@ export default function PhoneVerificationModal({
178181
<span style={{ whiteSpace: "pre-line" }}>{error}</span>
179182
</Alert>
180183
) : null}
181-
182184
{step === "phone" ? (
183185
<Form
184186
noValidate
@@ -187,49 +189,93 @@ export default function PhoneVerificationModal({
187189
handleSendCode()
188190
}}
189191
>
190-
<Input
192+
<p className="mb-4">
193+
<strong>{t("phoneVerification.stepOne")}</strong>{" "}
194+
{t("phoneVerification.stepOneDescription")}
195+
</p>
196+
<Form.Control
191197
ref={phoneInputRef}
192-
label={t("phoneVerification.phoneLabel")}
193198
type="tel"
194199
placeholder={t("phoneVerification.phonePlaceholder")}
195200
value={phone}
196201
onChange={e => setPhone(e.target.value)}
197-
className="mb-3"
202+
className="mb-4"
203+
style={{
204+
border: "none",
205+
borderRadius: 0,
206+
borderBottom: "1px solid",
207+
boxShadow: "none",
208+
background: "transparent",
209+
paddingLeft: 0
210+
}}
198211
/>
199212
<div id={RECAPTCHA_CONTAINER_ID} />
200213
<LoadingButton
201214
type="submit"
202-
className="w-100"
215+
className="w-100 mt-4 py-2"
203216
loading={sendingCode}
204217
>
205-
{t("phoneVerification.continue")}
218+
{t("phoneVerification.receiveCode")}
206219
</LoadingButton>
207220
</Form>
208-
) : (
221+
) : step === "code" ? (
209222
<Form
210223
noValidate
211224
onSubmit={e => {
212225
e.preventDefault()
213226
handleVerify()
214227
}}
215228
>
216-
<Input
229+
<p className="mb-4">
230+
<strong>{t("phoneVerification.stepTwo")}</strong>{" "}
231+
{t("phoneVerification.stepTwoDescription")}
232+
</p>
233+
<Form.Control
217234
ref={codeInputRef}
218-
label={t("phoneVerification.codeLabel")}
219235
type="text"
220236
placeholder={t("phoneVerification.codePlaceholder")}
221237
value={code}
222238
onChange={e => setCode(e.target.value)}
223-
className="mb-3"
239+
className="mb-5"
240+
style={{
241+
border: "none",
242+
borderRadius: 0,
243+
borderBottom: "1px solid",
244+
boxShadow: "none",
245+
background: "transparent",
246+
paddingLeft: 0
247+
}}
224248
/>
225249
<LoadingButton
226250
type="submit"
227-
className="w-100"
251+
className="w-100 mt-4 py-2"
228252
loading={verifying}
229253
>
230254
{t("phoneVerification.verify")}
231255
</LoadingButton>
232256
</Form>
257+
) : (
258+
<div className="text-center py-3">
259+
<div
260+
style={{
261+
width: 96,
262+
height: 96,
263+
borderRadius: "50%",
264+
backgroundColor: "#3D9922",
265+
display: "flex",
266+
alignItems: "center",
267+
justifyContent: "center",
268+
margin: "0 auto",
269+
fontSize: 48,
270+
color: "white"
271+
}}
272+
>
273+
{"\u2713" /* checkmark */}
274+
</div>
275+
<p className="mt-3 mb-0 py-3">
276+
{t("phoneVerification.successMessage")}
277+
</p>
278+
</div>
233279
)}
234280
</Col>
235281
</Modal.Body>

0 commit comments

Comments
 (0)