@@ -8,6 +8,15 @@ const logoutBtn = document.getElementById("logout-btn");
88const badge = document . getElementById ( "mode-badge" ) ;
99const queryInput = document . getElementById ( "query" ) ;
1010const sendJsonCheckbox = document . getElementById ( "send-json" ) ;
11+ const profileForm = document . getElementById ( "profile-form" ) ;
12+ const profileRefreshBtn = document . getElementById ( "profile-refresh-btn" ) ;
13+ const profileEmailInput = document . getElementById ( "profile-email" ) ;
14+ const profileAgeInput = document . getElementById ( "profile-age" ) ;
15+ const profilePhoneInput = document . getElementById ( "profile-phone" ) ;
16+ const profileDepartmentInput = document . getElementById ( "profile-department" ) ;
17+ const profileBioInput = document . getElementById ( "profile-bio" ) ;
18+ const profileRawModeCheckbox = document . getElementById ( "profile-raw-mode" ) ;
19+ const profileRawPayloadInput = document . getElementById ( "profile-raw-payload" ) ;
1120const materialIcons = {
1221 pdf : "/assets/pdf.png" ,
1322 video : "/assets/video.png" ,
@@ -21,6 +30,65 @@ function showResponse(payload) {
2130 responseOutput . textContent = JSON . stringify ( payload , null , 2 ) ;
2231}
2332
33+ function clearProfileForm ( ) {
34+ profileEmailInput . value = "" ;
35+ profileAgeInput . value = "" ;
36+ profilePhoneInput . value = "" ;
37+ profileDepartmentInput . value = "" ;
38+ profileBioInput . value = "" ;
39+ }
40+
41+ function fillProfileForm ( profile ) {
42+ if ( ! profile || typeof profile !== "object" ) {
43+ clearProfileForm ( ) ;
44+ return ;
45+ }
46+
47+ profileEmailInput . value = typeof profile . email === "string" ? profile . email : "" ;
48+ profileAgeInput . value = Number . isFinite ( profile . age ) ? String ( profile . age ) : "" ;
49+ profilePhoneInput . value = typeof profile . phoneNumber === "string" ? profile . phoneNumber : "" ;
50+ profileDepartmentInput . value =
51+ typeof profile . department === "string" ? profile . department : "" ;
52+ profileBioInput . value = typeof profile . bio === "string" ? profile . bio : "" ;
53+
54+ const exampleEmail = typeof profile . email === "string" ? profile . email : "user@example.com" ;
55+ profileRawPayloadInput . placeholder = JSON . stringify (
56+ {
57+ email : { $eq : exampleEmail } ,
58+ role : "admin" ,
59+ bio : "Injected write demo" ,
60+ } ,
61+ null ,
62+ 2
63+ ) ;
64+ }
65+
66+ function buildProfileFormPayload ( ) {
67+ const payload = { } ;
68+
69+ if ( profileEmailInput . value . trim ( ) !== "" ) {
70+ payload . email = profileEmailInput . value . trim ( ) ;
71+ }
72+
73+ if ( profileAgeInput . value . trim ( ) !== "" ) {
74+ payload . age = Number ( profileAgeInput . value ) ;
75+ }
76+
77+ if ( profilePhoneInput . value . trim ( ) !== "" ) {
78+ payload . phoneNumber = profilePhoneInput . value . trim ( ) ;
79+ }
80+
81+ if ( profileDepartmentInput . value . trim ( ) !== "" ) {
82+ payload . department = profileDepartmentInput . value . trim ( ) ;
83+ }
84+
85+ if ( profileBioInput . value . trim ( ) !== "" ) {
86+ payload . bio = profileBioInput . value . trim ( ) ;
87+ }
88+
89+ return payload ;
90+ }
91+
2492function escapeHtml ( value ) {
2593 return String ( value )
2694 . replace ( / & / g, "&" )
@@ -165,6 +233,50 @@ async function fetchJson(url, options = {}) {
165233 return { response, payload } ;
166234}
167235
236+ function getProfileQueryStringFromPageUrl ( ) {
237+ const params = new URLSearchParams ( window . location . search ) ;
238+ const profileParams = new URLSearchParams ( ) ;
239+
240+ for ( const [ key , value ] of params . entries ( ) ) {
241+ if ( key === "mode" ) {
242+ continue ;
243+ }
244+
245+ const isProfileFilterKey =
246+ key === "username" ||
247+ key === "email" ||
248+ key . startsWith ( "username[" ) ||
249+ key . startsWith ( "email[" ) ;
250+
251+ if ( isProfileFilterKey ) {
252+ profileParams . append ( key , value ) ;
253+ }
254+ }
255+
256+ return profileParams . toString ( ) ;
257+ }
258+
259+ async function loadProfilesFromPageUrlQuery ( ) {
260+ const rawQueryString = getProfileQueryStringFromPageUrl ( ) ;
261+ if ( ! rawQueryString ) {
262+ return ;
263+ }
264+
265+ const url = `/api/${ mode } /profile?${ rawQueryString } ` ;
266+ const { payload } = await fetchJson ( url , { method : "GET" } ) ;
267+ showResponse ( payload ) ;
268+ }
269+
270+ async function loadCurrentProfile ( showPayload = false ) {
271+ const { payload } = await fetchJson ( `/api/${ mode } /profile` , { method : "GET" } ) ;
272+ if ( showPayload ) {
273+ showResponse ( payload ) ;
274+ }
275+
276+ const profile = Array . isArray ( payload . results ) && payload . results . length > 0 ? payload . results [ 0 ] : null ;
277+ fillProfileForm ( profile ) ;
278+ }
279+
168280async function ensureAuthenticated ( ) {
169281 try {
170282 const { response, payload } = await fetchJson ( `/api/${ mode } /me` ) ;
@@ -245,9 +357,58 @@ document.getElementById("search-form").addEventListener("submit", async (event)
245357 }
246358} ) ;
247359
360+ profileRawModeCheckbox . addEventListener ( "change" , ( ) => {
361+ profileRawPayloadInput . classList . toggle ( "hidden" , ! profileRawModeCheckbox . checked ) ;
362+ } ) ;
363+
364+ profileRefreshBtn . addEventListener ( "click" , async ( ) => {
365+ try {
366+ await loadCurrentProfile ( true ) ;
367+ } catch ( error ) {
368+ showResponse ( { ok : false , mode, message : error . message } ) ;
369+ }
370+ } ) ;
371+
372+ profileForm . addEventListener ( "submit" , async ( event ) => {
373+ event . preventDefault ( ) ;
374+
375+ let payload ;
376+ if ( profileRawModeCheckbox . checked ) {
377+ try {
378+ payload = JSON . parse ( profileRawPayloadInput . value ) ;
379+ } catch ( error ) {
380+ showResponse ( {
381+ ok : false ,
382+ mode,
383+ message : `Invalid JSON payload: ${ error . message } ` ,
384+ } ) ;
385+ return ;
386+ }
387+ } else {
388+ payload = buildProfileFormPayload ( ) ;
389+ }
390+
391+ try {
392+ const { payload : responsePayload } = await fetchJson ( `/api/${ mode } /profile` , {
393+ method : "PUT" ,
394+ headers : { "Content-Type" : "application/json" } ,
395+ body : JSON . stringify ( payload ) ,
396+ } ) ;
397+
398+ showResponse ( responsePayload ) ;
399+ if ( responsePayload && responsePayload . ok ) {
400+ await loadCurrentProfile ( false ) ;
401+ }
402+ } catch ( error ) {
403+ showResponse ( { ok : false , mode, message : error . message } ) ;
404+ }
405+ } ) ;
406+
248407( async ( ) => {
249408 const authenticated = await ensureAuthenticated ( ) ;
250409 if ( authenticated ) {
251410 await loadItems ( ) ;
411+ await loadCurrentProfile ( false ) ;
412+ await loadProfilesFromPageUrlQuery ( ) ;
252413 }
253414} ) ( ) ;
0 commit comments