Skip to content

Commit

Permalink
Start settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
bombies committed Oct 28, 2023
1 parent 7a63574 commit dd051f3
Show file tree
Hide file tree
Showing 25 changed files with 396 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import StatisticsIcon from "@/app/(site)/components/icons/StatisticsIcon";
const DashboardSidebar: FC = () => {
return (
<SidebarProvider>
<Sidebar>
<Sidebar headerText="Your Dashboard">
<SidebarItem
startContent={<CloudIcon />}
title="Your Dreams"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,21 @@ const AddCharacterForm: FC<Props> = ({onSuccess}) => {
if (!session?.user)
return;

await toast.promise(characters.optimisticData
.addOptimisticData(() => handleCreation(data), {
id: '',
...data,
createdAt: new Date(),
updatedAt: new Date(),
userId: session.user.id
}),
{
loading: "Adding new character...",
success: "Successfully added that character!",
error: "Could not add that character!"
}
)

if (characters.optimisticData.addOptimisticData)
await toast.promise(characters.optimisticData
.addOptimisticData(() => handleCreation(data), {
id: '',
...data,
createdAt: new Date(),
updatedAt: new Date(),
userId: session.user.id
}),
{
loading: "Adding new character...",
success: "Successfully added that character!",
error: "Could not add that character!"
}
)
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const useDreams = (): DreamsState => {
const addOptimisticDream = useCallback<OptimisticWorker<Dream>>(async (work, optimisticDream) => {
if (!dreams)
return
const mutate = mutateDreams as KeyedMutator<Dream[]>
const mutate = mutateDreams
const doWork = async (): Promise<Dream[]> => {
const dream = await work()
if (!dream)
Expand All @@ -29,7 +29,7 @@ const useDreams = (): DreamsState => {
const removeOptimisticDream = useCallback<OptimisticWorker<Dream>>(async (work, removedOptimisticDream) => {
if (!dreams)
return
const mutate = mutateDreams as KeyedMutator<Dream[]>
const mutate = mutateDreams
const doWork = async (): Promise<Dream[]> => {
const removedDream = await work()
if (!removedDream)
Expand All @@ -47,7 +47,7 @@ const useDreams = (): DreamsState => {
if (!dreams)
return

const mutate = mutateDreams as KeyedMutator<Dream[]>
const mutate = mutateDreams

const doUpdate = (editedDream: Dream): Dream[] => {
const newArr = dreams.filter(dream => dream.id !== editedDream.id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client"

import {FC} from "react";
import {Button} from "@nextui-org/button";

const ChangePasswordButton: FC = () => {
return (
<Button
size="lg"
color="danger"
variant="shadow"
>
Change Password
</Button>
)
}

export default ChangePasswordButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client"

import { Button } from "@nextui-org/react";
import {FC} from "react";

const DeleteAccountButton: FC = () => {
return (
<Button
color="danger"
variant="bordered"
size="lg"
>
Delete Account
</Button>
)
}

export default DeleteAccountButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"use client"

import {FC, Fragment} from "react";
import {useMemberData} from "@/app/(site)/components/providers/user-data/UserProvider";
import {Avatar, Spacer, Spinner} from "@nextui-org/react";
import Card from "@/app/(site)/components/Card";
import {CardBody} from "@nextui-org/card";
import EditableInput from "@/app/(site)/components/inputs/editable/EditableInput";
import {USERNAME_REGEX} from "@/app/api/auth/register/register.dto";
import {EditIcon} from "@nextui-org/shared-icons";

const EditableUserProfile: FC = () => {
const {
memberData: {
data: member,
loading: memberDataLoading,
optimisticData: {
editOptimisticData: editMemberData
}
}
} = useMemberData()

return (
<Card
className="w-1/2 laptop:w-5/6"
classNames={{
body: "p-12"
}}
>
<CardBody>
{
memberDataLoading ?
<Spinner/>
:
(
<Fragment>
<div className="flex gap-8">
<Avatar
src={member?.image ?? undefined}
isBordered
className="w-24 h-24"
/>
<div className="flex flex-col justify-center">
<h3 className="capitalize font-semibold text-2xl">{member?.firstName} {member?.lastName}</h3>
<h3 className="text-primary brightness-200">@{member?.username}</h3>
</div>
</div>
<Spacer y={6}/>
<Card classNames={{
body: "space-y-6 bg-[#0C0015] p-12"
}}>
<CardBody>
<div>
<h4 className="text-subtext font-semibold mb-2">USERNAME</h4>
<EditableInput
isEditable
isRequired
value={member?.username}
minLength={2}
maxLength={32}
size="sm"
validate={{
predicate(value) {
if (!value)
return true
return USERNAME_REGEX.test(value)
},
errorMsg: "Invalid username!"
}}
onEdit={(newValue) => {

}}
>
<p className="flex gap-2">{member?.username} <EditIcon className="self-center" /></p>
</EditableInput>
</div>
<div className="flex phone:flex-col gap-24 phone:gap-4">
<div>
<h4 className="text-subtext font-semibold mb-2">FIRST NAME</h4>
<EditableInput
isEditable
isRequired
value={member?.firstName}
minLength={1}
maxLength={60}
size="sm"
onEdit={(newValue) => {

}}
>
<p className="capitalize flex gap-2">{member?.firstName} <EditIcon className="self-center" /></p>
</EditableInput>
</div>
<div>
<h4 className="text-subtext font-semibold mb-2">LAST NAME</h4>
<EditableInput
isEditable
isRequired
value={member?.lastName}
minLength={1}
maxLength={60}
size="sm"
onEdit={(newValue) => {

}}
>
<p className="capitalize flex gap-2">{member?.lastName} <EditIcon className="self-center" /></p>
</EditableInput>
</div>
</div>
<div>
<h4 className="text-subtext font-semibold mb-2">EMAIL ADDRESS</h4>
<p>{member?.email}</p>
</div>
</CardBody>
</Card>
</Fragment>
)
}
</CardBody>
</Card>
)
}

export default EditableUserProfile
32 changes: 32 additions & 0 deletions src/app/(site)/(internal)/settings/account/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {FC, Fragment} from "react";
import Title from "@/app/(site)/components/Title";
import {Spacer} from "@nextui-org/react";
import SubTitle from "@/app/(site)/components/SubTitle";
import EditableUserProfile from "@/app/(site)/(internal)/settings/account/components/EditableUserProfile";
import {Divider} from "@nextui-org/divider";
import ChangePasswordButton from "@/app/(site)/(internal)/settings/account/components/ChangePasswordButton";
import DeleteAccountButton from "@/app/(site)/(internal)/settings/account/components/DeleteAccountButton";

const AccountSettingsPage: FC = () => {
return (
<Fragment>
<Title>Account Settings</Title>
<SubTitle>Your Profile</SubTitle>
<Spacer y={4} />
<EditableUserProfile />
<Divider className="my-6 w-3/4" />
<SubTitle>Password</SubTitle>
<div>
<ChangePasswordButton />
</div>
<Divider className="my-6 w-3/4" />
<SubTitle>Account Removal</SubTitle>
<p className="font-bold p-6 bg-danger/30 rounded-3xl w-fit phone:w-3/4 mb-6">Deleting your account is a permanent action and cannot be undone!</p>
<div>
<DeleteAccountButton />
</div>
</Fragment>
)
}

export default AccountSettingsPage
23 changes: 23 additions & 0 deletions src/app/(site)/(internal)/settings/components/SettingsSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client"

import {FC} from "react";
import SidebarProvider from "@/app/(site)/components/sidebar/SidebarProvider";
import Sidebar from "@/app/(site)/components/sidebar/Sidebar";
import SidebarItem from "@/app/(site)/components/sidebar/SidebarItem";
import {AvatarIcon} from "@nextui-org/shared-icons";

const SettingsSidebar: FC = () => {
return (
<SidebarProvider>
<Sidebar headerText="Your Settings">
<SidebarItem
title="Account"
startContent={<AvatarIcon className="self-center" />}
href={"/settings/account"}
/>
</Sidebar>
</SidebarProvider>
)
}

export default SettingsSidebar
22 changes: 22 additions & 0 deletions src/app/(site)/(internal)/settings/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {Metadata} from "next";
import {FC, PropsWithChildren} from "react";
import SettingsSidebar from "@/app/(site)/(internal)/settings/components/SettingsSidebar";

export const metadata: Metadata = {
title: 'Dream Logger - Settings',
description: 'Your Dream Logger settings'
}

const SettingsLayout: FC<PropsWithChildren> = ({children}) => {
return (
<main className="relative flex min-h-screen">
<SettingsSidebar />
<div
className="flex-grow py-32 w-screen overflow-hidden phone:pt-32 flex flex-col phone:items-center phone-min:ml-16 tablet-min:ml-32">
{children}
</div>
</main>
)
}

export default SettingsLayout
10 changes: 5 additions & 5 deletions src/app/(site)/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {FC} from "react";
import {Card as NextCard, CardBody, CardProps} from "@nextui-org/card";
import clsx from "clsx";

const Card: FC<CardProps> = ({classNames, ...props}) => {
return (
<NextCard
classNames={{
base: "rounded-3xl",
header: "bg-secondary",
body: "bg-secondary",
footer: "bg-secondary",
...classNames,
base: clsx("rounded-3xl", classNames?.base),
header: clsx("bg-secondary", classNames?.header),
body: clsx("bg-secondary", classNames?.body),
footer: clsx("bg-secondary", classNames?.footer),
}}
{...props}
>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(site)/components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const NavBar: FC = () => {

return (
<Navbar
className={clsx((pathName.includes("/dashboard") || pathName.includes("/signin")) && "hidden")}
className={clsx((["/dashboard", "/signin", "/settings"].some(name => pathName.includes(name))) && "hidden")}
classNames={{
base: 'bg-[#0C0015]'
}}
Expand Down
17 changes: 17 additions & 0 deletions src/app/(site)/components/SubTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {FC, PropsWithChildren} from "react";
import clsx from "clsx";

type Props = {
className?: string
} & PropsWithChildren

const SubTitle: FC<Props> = ({className, children}) => {
return (
<h2 className={clsx(
"text-4xl phone:text-2xl font-semibold mb-8 phone:mb-4 tablet:text-center",
className
)}>{children}</h2>
)
}

export default SubTitle
9 changes: 9 additions & 0 deletions src/app/(site)/components/Title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {FC, PropsWithChildren} from "react";

const Title: FC<PropsWithChildren> = ({children}) => {
return (
<h1 className="font-bold text-7xl phone:text-4xl mb-20 phone:mb-10">{children}</h1>
)
}

export default Title
Loading

0 comments on commit dd051f3

Please sign in to comment.