@@ -7,7 +7,14 @@ import * as ConnectionState from "../states/connection";
7
7
import AnimatedModal from "../utils/animated-modal" ;
8
8
import * as Profile from "../elements/profile" ;
9
9
import { 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" ;
11
18
12
19
export function show ( ) : void {
13
20
if ( ! ConnectionState . get ( ) ) {
@@ -44,6 +51,12 @@ const githubInput = $("#editProfileModal .github");
44
51
const websiteInput = $ ( "#editProfileModal .website" ) ;
45
52
const badgeIdsSelect = $ ( "#editProfileModal .badgeSelectionContainer" ) ;
46
53
54
+ const indicators = [
55
+ addValidation ( twitterInput , TwitterProfileSchema ) ,
56
+ addValidation ( githubInput , GithubProfileSchema ) ,
57
+ addValidation ( websiteInput , WebsiteSchema ) ,
58
+ ] ;
59
+
47
60
let currentSelectedBadgeId = - 1 ;
48
61
49
62
function hydrateInputs ( ) : void {
@@ -90,6 +103,8 @@ function hydrateInputs(): void {
90
103
badgeIdsSelect . find ( ".badgeSelectionItem" ) . removeClass ( "selected" ) ;
91
104
$ ( currentTarget ) . addClass ( "selected" ) ;
92
105
} ) ;
106
+
107
+ indicators . forEach ( ( it ) => it . hide ( ) ) ;
93
108
}
94
109
95
110
function initializeCharacterCounters ( ) : void {
@@ -175,6 +190,45 @@ async function updateProfile(): Promise<void> {
175
190
hide ( ) ;
176
191
}
177
192
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
+
178
232
const modal = new AnimatedModal ( {
179
233
dialogId : "editProfileModal" ,
180
234
setup : async ( modalEl ) : Promise < void > => {
0 commit comments