feat(dashboard-v2): add Settings/Account page
This commit is contained in:
parent
638825d8db
commit
8422a6770c
|
@ -0,0 +1,35 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useUser } from "../../contexts/user";
|
||||||
|
import { SimpleUploadIcon } from "../Icons";
|
||||||
|
|
||||||
|
const AVATAR_PLACEHOLDER = "/images/avatar-placeholder.svg";
|
||||||
|
|
||||||
|
export const AvatarUploader = (props) => {
|
||||||
|
const { user } = useUser();
|
||||||
|
const [imageUrl, setImageUrl] = useState(AVATAR_PLACEHOLDER);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setImageUrl(user.avatarUrl ?? AVATAR_PLACEHOLDER);
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...props}>
|
||||||
|
<div
|
||||||
|
className={`flex justify-center items-center xl:w-[245px] xl:h-[245px] bg-contain bg-none xl:bg-[url(/images/avatar-bg.svg)]`}
|
||||||
|
>
|
||||||
|
<img src={imageUrl} className="w-[160px]" alt="" />
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<button
|
||||||
|
className="flex items-center gap-4 hover:underline decoration-1 decoration-dashed underline-offset-2 decoration-gray-400"
|
||||||
|
type="button"
|
||||||
|
onClick={console.info.bind(console)}
|
||||||
|
>
|
||||||
|
<SimpleUploadIcon size={20} className="shrink-0" /> Upload profile picture
|
||||||
|
</button>
|
||||||
|
{/* TODO: actual uploading */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./AvatarUploader";
|
|
@ -1,15 +1,81 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { useMedia } from "react-use";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import theme from "../../lib/theme";
|
||||||
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
|
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
|
||||||
|
import { TextInputBasic } from "../../components/TextInputBasic/TextInputBasic";
|
||||||
|
import { Button } from "../../components/Button";
|
||||||
|
import { AvatarUploader } from "../../components/AvatarUploader";
|
||||||
|
|
||||||
const SettingsPage = () => {
|
const FormGroup = styled.div.attrs({
|
||||||
|
className: "grid sm:grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-end",
|
||||||
|
})``;
|
||||||
|
|
||||||
|
const AccountPage = () => {
|
||||||
|
const isLargeScreen = useMedia(`(min-width: ${theme.screens.xl})`);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<div className="flex flex-col xl:flex-row">
|
||||||
|
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px]">
|
||||||
|
<section>
|
||||||
<h4>Account</h4>
|
<h4>Account</h4>
|
||||||
|
<p>
|
||||||
|
Tum dicere exorsus est laborum et quasi involuta aperiri, altera prompta et expedita. Primum igitur,
|
||||||
|
inquit, modo ista sis aequitate.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
{!isLargeScreen && (
|
||||||
|
<section>
|
||||||
|
<AvatarUploader className="flex flex-col sm:flex-row gap-8 items-center" />
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
<section className="flex flex-col gap-8">
|
||||||
|
<FormGroup>
|
||||||
|
<TextInputBasic label="Display name" placeholder="John Doe" />
|
||||||
|
<div className="flex mt-2 sm:mt-0 justify-center">
|
||||||
|
<Button>Update</Button>
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<TextInputBasic label="Email" placeholder="john.doe@example.com" />
|
||||||
|
<div className="flex mt-2 sm:mt-0 justify-center">
|
||||||
|
<Button>Update</Button>
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<TextInputBasic type="password" label="Password" placeholder="dbf3htf*efh4pcy@PXB" />
|
||||||
|
<div className="flex mt-2 sm:mt-0 justify-center order-last sm:order-none">
|
||||||
|
<Button>Update</Button>
|
||||||
|
</div>
|
||||||
|
<small className="text-palette-400">
|
||||||
|
The password must be at least 6 characters long. Significantly different from the email and old
|
||||||
|
password.
|
||||||
|
</small>
|
||||||
|
</FormGroup>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section>
|
||||||
|
<h6 className="text-palette-400">Delete account</h6>
|
||||||
|
<p>This will completely delete your account. This process can't be undone.</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => window.confirm("TODO: confirmation modal")}
|
||||||
|
className="text-error underline decoration-1 hover:decoration-dashed"
|
||||||
|
>
|
||||||
|
Delete account
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full justify-start xl:justify-end">
|
||||||
|
{isLargeScreen && <AvatarUploader className="flex flex-col gap-4" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
SettingsPage.Layout = UserSettingsLayout;
|
AccountPage.Layout = UserSettingsLayout;
|
||||||
|
|
||||||
export default SettingsPage;
|
export default AccountPage;
|
||||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 160 160"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#f4f6f7;}.cls-3{fill:#231f20;}.cls-4{mask:url(#mask);}.cls-5,.cls-7{fill:none;stroke-width:2px;}.cls-5{stroke:#0f0f0f;stroke-linejoin:round;}.cls-6,.cls-8{fill:#40b75e;}.cls-6{fill-rule:evenodd;}.cls-7{stroke:#231f20;stroke-miterlimit:10;}</style><mask id="mask" x="15.52" y="37.16" width="128" height="146" maskUnits="userSpaceOnUse"><g id="gf1qkqaq7b"><circle id="asdc4b43va" class="cls-1" cx="79.52" cy="101.16" r="64"/></g></mask></defs><g id="Layer_2" data-name="Layer 2"><circle class="cls-2" cx="80" cy="80" r="79"/><path class="cls-3" d="M80,2A78,78,0,1,1,2,80,78.09,78.09,0,0,1,80,2m0-2a80,80,0,1,0,80,80A80,80,0,0,0,80,0Z"/></g><g id="Layer_1" data-name="Layer 1"><g class="cls-4"><path class="cls-5" d="M103.52,129.16l6,2c12,4,22,9.85,22,22v30m-40-70v10m-36,6-6,2c-12,4-22,9.85-22,22v30m40-70v10m-10-40,6-4h14l6-6,12,6,6,4v8l-2,2c0,10-6,24-20,24s-20-14-20-24l-2-2Z"/><path class="cls-5" d="M57.52,83.16l-2-2v-14l6-10,4,4,8-4,8,4h12a10,10,0,0,1,10,10v10l-2,2"/><path class="cls-6" d="M69.52,128.16l10,14,10-14h8l8,4-20,20h-12l-20-20,8-4Z"/><path class="cls-5" d="M69.52,123.16l10,14,10-14h8l8,4-20,20h-12l-20-20,8-4Z"/></g></g><g id="Layer_3" data-name="Layer 3"><circle class="cls-7" cx="95.3" cy="33.27" r="4.55"/><circle class="cls-8" cx="20.61" cy="86.7" r="2.29"/><circle class="cls-8" cx="128.65" cy="60.06" r="5.08"/></g></svg>
|
After Width: | Height: | Size: 1.5 KiB |
Reference in New Issue