Update profile page

pull/3813/head
Kamran Ahmed 2 years ago
parent 44ce3a3c98
commit e849eeeca5
  1. 2
      src/components/Navigation/navigation.ts
  2. 203
      src/components/Setting/UpdateProfileForm.tsx
  3. 2
      src/lib/http.ts
  4. 2
      src/pages/settings/profile.astro

@ -2,7 +2,7 @@ import Cookies from 'js-cookie';
import { handleAuthRequired } from '../Authenticator/authenticator'; import { handleAuthRequired } from '../Authenticator/authenticator';
import {TOKEN_COOKIE_NAME} from "../../lib/jwt"; import {TOKEN_COOKIE_NAME} from "../../lib/jwt";
function logout() { export function logout() {
Cookies.remove(TOKEN_COOKIE_NAME); Cookies.remove(TOKEN_COOKIE_NAME);
// Reloading will automatically redirect the user if required // Reloading will automatically redirect the user if required
window.location.reload(); window.location.reload();

@ -1,131 +1,87 @@
import { useCallback, useEffect, useState } from 'preact/hooks'; import { useEffect, useState } from 'preact/hooks';
import { httpGet, httpPost } from '../../lib/http';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Spinner from '../Spinner'; import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import {TOKEN_COOKIE_NAME} from "../../lib/jwt";
export default function UpdateProfileForm() { export function UpdateProfileForm() {
const [name, setName] = useState(''); const [name, setName] = useState('');
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [github, setGithub] = useState(''); const [github, setGithub] = useState('');
const [twitter, setTwitter] = useState('');
const [linkedin, setLinkedin] = useState(''); const [linkedin, setLinkedin] = useState('');
const [website, setWebsite] = useState(''); const [website, setWebsite] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(true);
const [message, setMessage] = useState<{ const [error, setError] = useState('');
type: 'error' | 'success' | 'info'; const [success, setSuccess] = useState('');
message: string;
}>();
const handleSubmit = (e: Event) => { const handleSubmit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true); setIsLoading(true);
setError('');
setSuccess('');
const headers = new Headers(); const { response, error } = await httpPost(
headers.append('Content-Type', 'application/json'); `${import.meta.env.PUBLIC_API_URL}/v1-update-profile`,
headers.append( {
'Cookie', name,
`${TOKEN_COOKIE_NAME}=${Cookies.get(TOKEN_COOKIE_NAME)}` github: github || undefined,
linkedin: linkedin || undefined,
twitter: twitter || undefined,
website: website || undefined,
}
); );
fetch('http://localhost:8080/v1-update-profile', { if (error || !response) {
method: 'POST', setIsLoading(false);
credentials: 'include', setError(error?.message || 'Something went wrong');
headers,
body: JSON.stringify({ return;
name, }
github: github === '' ? undefined : github,
linkedin: linkedin === '' ? undefined : linkedin, await loadProfile();
website: website === '' ? undefined : website, setSuccess('Profile updated successfully');
}),
})
.then(async (res) => {
const json = await res.json();
if (res.ok) {
return json;
} else {
throw new Error(json.message);
}
})
.then((data) => {
setIsLoading(false);
setName(data.name);
setEmail(data.email);
if (data.links) {
const { github, linkedin, website } = data.links;
setGithub(github || '');
setLinkedin(linkedin || '');
setWebsite(website || '');
}
// Check if the user has changed their email
fetchProfile();
setMessage({
type: 'success',
message: 'Profile updated successfully',
});
})
.catch((err) => {
setIsLoading(false);
setMessage({
type: 'error',
message: err.message || 'Something went wrong',
});
});
}; };
const fetchProfile = useCallback(async () => { const loadProfile = async () => {
// Set the loading state // Set the loading state
setIsLoading(true); setIsLoading(true);
// Create headers with the cookie const { error, response } = await httpGet(
const headers = new Headers(); `${import.meta.env.PUBLIC_API_URL}/v1-me`
headers.append('Content-Type', 'application/json');
headers.append(
'Cookie',
`${TOKEN_COOKIE_NAME}=${Cookies.get(TOKEN_COOKIE_NAME)}`
); );
try { if (error || !response) {
const res = await fetch('http://localhost:8080/v1-me', { if (error?.status === 401) {
method: 'POST',
credentials: 'include',
headers,
});
const json = await res.json();
if (json.status === 401) {
// If the user is not authenticated, redirect to the login page
// Clear the cookie
Cookies.remove(TOKEN_COOKIE_NAME); Cookies.remove(TOKEN_COOKIE_NAME);
window.location.href = '/login'; window.location.reload();
}
if (res.ok) { return;
setName(json.name);
setEmail(json.email);
if (json.links) {
const { github, linkedin, website } = json.links;
setGithub(github || '');
setLinkedin(linkedin || '');
setWebsite(website || '');
}
} else {
throw new Error(json.message);
} }
} catch (error: any) {
setMessage({ setIsLoading(false);
type: 'error', setError(error?.message || 'Something went wrong');
message: error?.message || 'Something went wrong',
}); return;
} }
const { name, email, links } = response;
setName(name);
setEmail(email);
setGithub(links?.github || '');
setLinkedin(links?.linkedin || '');
setTwitter(links?.twitter || '');
setWebsite(links?.website || '');
setIsLoading(false); setIsLoading(false);
}, []); };
// Make a request to the backend to fill in the form with the current values // Make a request to the backend to fill in the form with the current values
useEffect(() => { useEffect(() => {
fetchProfile(); loadProfile().finally(() => {
// hide the page level loading indicator
});
}, []); }, []);
return ( return (
@ -144,7 +100,7 @@ export default function UpdateProfileForm() {
type="text" type="text"
name="name" name="name"
id="name" id="name"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1" className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
required required
placeholder="John Doe" placeholder="John Doe"
value={name} value={name}
@ -162,7 +118,7 @@ export default function UpdateProfileForm() {
type="email" type="email"
name="email" name="email"
id="email" id="email"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1" className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
required required
disabled disabled
placeholder="john@example.com" placeholder="john@example.com"
@ -178,12 +134,27 @@ export default function UpdateProfileForm() {
type="text" type="text"
name="github" name="github"
id="github" id="github"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1" className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://github.com/username" placeholder="https://github.com/username"
value={github} value={github}
onInput={(e) => setGithub((e.target as HTMLInputElement).value)} onInput={(e) => setGithub((e.target as HTMLInputElement).value)}
/> />
</div> </div>
<div className="flex w-full flex-col">
<label for="twitter" className="text-sm leading-none text-slate-500">
Twitter
</label>
<input
type="text"
name="twitter"
id="twitter"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://twitter.com/username"
value={twitter}
onInput={(e) => setTwitter((e.target as HTMLInputElement).value)}
/>
</div>
<div className="flex w-full flex-col"> <div className="flex w-full flex-col">
<label for="linkedin" className="text-sm leading-none text-slate-500"> <label for="linkedin" className="text-sm leading-none text-slate-500">
LinkedIn LinkedIn
@ -192,7 +163,7 @@ export default function UpdateProfileForm() {
type="text" type="text"
name="linkedin" name="linkedin"
id="linkedin" id="linkedin"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1" className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://www.linkedin.com/in/username/" placeholder="https://www.linkedin.com/in/username/"
value={linkedin} value={linkedin}
onInput={(e) => setLinkedin((e.target as HTMLInputElement).value)} onInput={(e) => setLinkedin((e.target as HTMLInputElement).value)}
@ -207,33 +178,29 @@ export default function UpdateProfileForm() {
type="text" type="text"
name="website" name="website"
id="website" id="website"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1" className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://example.com" placeholder="https://example.com"
value={website} value={website}
onInput={(e) => setWebsite((e.target as HTMLInputElement).value)} onInput={(e) => setWebsite((e.target as HTMLInputElement).value)}
/> />
</div> </div>
{message && ( {error && (
<div <p className="mt-2 rounded-lg bg-red-100 p-2 text-red-700">{error}</p>
className={`mt-2 rounded-lg p-2 ${ )}
message.type === 'error'
? 'bg-red-100 text-red-700' {success && (
: message.type === 'success' <p className="mt-2 rounded-lg bg-green-100 p-2 text-green-700">
? 'bg-green-100 text-green-700' {success}
: 'bg-blue-100 text-blue-700' </p>
}`}
>
{message.message}
</div>
)} )}
<button <button
className="!mt-5 inline-flex h-10 min-w-[120px] items-center justify-center rounded-lg border border-slate-300 bg-black p-2 px-4 text-sm font-medium text-white outline-none transition duration-150 ease-in-out focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60"
type="submit" type="submit"
disabled={isLoading} disabled={isLoading}
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
> >
{isLoading ? <Spinner className="text-white" /> : 'Update'} {isLoading ? 'Please wait...' : 'Continue'}
</button> </button>
</div> </div>
</form> </form>

@ -1,5 +1,5 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import {TOKEN_COOKIE_NAME} from "./jwt"; import { TOKEN_COOKIE_NAME } from './jwt';
type AppResponse = Record<string, any>; type AppResponse = Record<string, any>;
type FetchError = { type FetchError = {

@ -1,6 +1,6 @@
--- ---
import SettingSidebar from '../../components/Setting/SettingSidebar.astro'; import SettingSidebar from '../../components/Setting/SettingSidebar.astro';
import UpdateProfileForm from '../../components/Setting/UpdateProfileForm'; import { UpdateProfileForm } from '../../components/Setting/UpdateProfileForm';
import SettingLayout from '../../layouts/SettingLayout.astro'; import SettingLayout from '../../layouts/SettingLayout.astro';
--- ---

Loading…
Cancel
Save