@@ -7,7 +7,14 @@ import * as ConnectionState from "../states/connection";
77import AnimatedModal from "../utils/animated-modal" ;
88import * as Profile from "../elements/profile" ;
99import { CharacterCounter } from "../elements/character-counter" ;
10- import { Badge , UserProfileDetails } from "@monkeytype/contracts/schemas/users" ;
10+ import {
11+ Badge ,
12+ GithubProfileSchema ,
13+ TwitterProfileSchema ,
14+ UserProfileDetails ,
15+ WebsiteSchema ,
16+ } from "@monkeytype/contracts/schemas/users" ;
17+ import { InputIndicator } from "../elements/input-indicator" ;
1118
1219export function show ( ) : void {
1320 if ( ! ConnectionState . get ( ) ) {
@@ -44,6 +51,12 @@ const githubInput = $("#editProfileModal .github");
4451const websiteInput = $ ( "#editProfileModal .website" ) ;
4552const badgeIdsSelect = $ ( "#editProfileModal .badgeSelectionContainer" ) ;
4653
54+ const indicators = [
55+ addValidation ( twitterInput , TwitterProfileSchema ) ,
56+ addValidation ( githubInput , GithubProfileSchema ) ,
57+ addValidation ( websiteInput , WebsiteSchema ) ,
58+ ] ;
59+
4760let currentSelectedBadgeId = - 1 ;
4861
4962function hydrateInputs ( ) : void {
@@ -90,6 +103,8 @@ function hydrateInputs(): void {
90103 badgeIdsSelect . find ( ".badgeSelectionItem" ) . removeClass ( "selected" ) ;
91104 $ ( currentTarget ) . addClass ( "selected" ) ;
92105 } ) ;
106+
107+ indicators . forEach ( ( it ) => it . hide ( ) ) ;
93108}
94109
95110function initializeCharacterCounters ( ) : void {
@@ -175,6 +190,45 @@ async function updateProfile(): Promise<void> {
175190 hide ( ) ;
176191}
177192
193+ function addValidation (
194+ element : JQuery < HTMLElement > ,
195+ schema : Zod . Schema
196+ ) : InputIndicator {
197+ const indicator = new InputIndicator ( element , {
198+ valid : {
199+ icon : "fa-check" ,
200+ level : 1 ,
201+ } ,
202+ invalid : {
203+ icon : "fa-times" ,
204+ level : - 1 ,
205+ } ,
206+ checking : {
207+ icon : "fa-circle-notch" ,
208+ spinIcon : true ,
209+ level : 0 ,
210+ } ,
211+ } ) ;
212+
213+ element . on ( "input" , ( event ) => {
214+ const value = ( event . target as HTMLInputElement ) . value ;
215+ if ( value === undefined || value === "" ) {
216+ indicator . hide ( ) ;
217+ return ;
218+ }
219+ const validationResult = schema . safeParse ( value ) ;
220+ if ( ! validationResult . success ) {
221+ indicator . show (
222+ "invalid" ,
223+ validationResult . error . errors . map ( ( err ) => err . message ) . join ( ", " )
224+ ) ;
225+ return ;
226+ }
227+ indicator . show ( "valid" ) ;
228+ } ) ;
229+ return indicator ;
230+ }
231+
178232const modal = new AnimatedModal ( {
179233 dialogId : "editProfileModal" ,
180234 setup : async ( modalEl ) : Promise < void > => {
0 commit comments