Open jjfantini opened 7 months ago
Here is some code to build the profile settings page:
"use client";
import type {CardProps} from "@nextui-org/react";
import React from "react";
import {Card, Tabs, Tab} from "@nextui-org/react";
import {Icon} from "@iconify/react";
import AccountDetails from "./account-details";
import NotificationsSettings from "./notifications-settings";
import SecuritySettings from "./security-settings";
export default function Component(props: CardProps) {
return (
<Card {...props}>
<Tabs
classNames={{
tabList: "mx-4 mt-6 text-medium",
tabContent: "text-small",
}}
size="lg"
>
<Tab
key="account-settings"
textValue="Account Settings"
title={
<div className="flex items-center gap-1.5">
<Icon icon="solar:user-id-bold" width={20} />
<p>Account</p>
</div>
}
>
<AccountDetails className="p-2 shadow-none" />
</Tab>
<Tab
key="notifications-settings"
textValue="Notification Settings"
title={
<div className="flex items-center gap-1.5">
<Icon icon="solar:bell-bold" width={20} />
<p>Notifications</p>
</div>
}
>
<NotificationsSettings className="p-2 shadow-none" />
</Tab>
<Tab
key="security-settings"
textValue="Security Settings"
title={
<div className="flex items-center gap-1.5">
<Icon icon="solar:shield-keyhole-bold" width={20} />
<p>Security</p>
</div>
}
>
<SecuritySettings className="p-2 shadow-none" />
</Tab>
</Tabs>
</Card>
);
}
"use client";
import type {CardProps} from "@nextui-org/react";
import React from "react";
import {
Card,
CardHeader,
CardBody,
Button,
Avatar,
Badge,
Input,
Autocomplete,
AutocompleteItem,
CardFooter,
} from "@nextui-org/react";
import {Icon} from "@iconify/react";
import countries from "./countries";
export default function Component(props: CardProps) {
return (
<Card className="max-w-xl p-2" {...props}>
<CardHeader className="flex flex-col items-start px-4 pb-0 pt-4">
<p className="text-large">Account Details</p>
<div className="flex gap-4 py-4">
<Badge
disableOutline
classNames={{
badge: "w-5 h-5",
}}
color="primary"
content={
<Button
isIconOnly
className="p-0 text-primary-foreground"
radius="full"
size="sm"
variant="light"
>
<Icon icon="solar:pen-2-linear" />
</Button>
}
placement="bottom-right"
shape="circle"
>
<Avatar className="h-14 w-14" src="https://i.pravatar.cc/150?u=a04258114e29026708c" />
</Badge>
<div className="flex flex-col items-start justify-center">
<p className="font-medium">Tony Reichert</p>
<span className="text-small text-default-500">Professional Designer</span>
</div>
</div>
<p className="text-small text-default-400">
The photo will be used for your profile, and will be visible to other users of the
platform.
</p>
</CardHeader>
<CardBody className="grid grid-cols-1 gap-4 md:grid-cols-2">
{/* Username */}
<Input label="Username" labelPlacement="outside" placeholder="Enter username" />
{/* Email */}
<Input label="Email" labelPlacement="outside" placeholder="Enter email" />
{/* First Name */}
<Input label="First Name" labelPlacement="outside" placeholder="Enter first name" />
{/* Last Name */}
<Input label="Last Name" labelPlacement="outside" placeholder="Enter last name" />
{/* Phone Number */}
<Input label="Phone Number" labelPlacement="outside" placeholder="Enter phone number" />
{/* Country */}
<Autocomplete
defaultItems={countries}
label="Country"
labelPlacement="outside"
placeholder="Select country"
showScrollIndicators={false}
>
{(item) => (
<AutocompleteItem
key={item.code}
startContent={
<Avatar
alt="Country Flag"
className="h-6 w-6"
src={`https://flagcdn.com/${item.code.toLowerCase()}.svg`}
/>
}
value={item.code}
>
{item.name}
</AutocompleteItem>
)}
</Autocomplete>
{/* State */}
<Input label="State" labelPlacement="outside" placeholder="Enter state" />
{/* Address */}
<Input label="Address" labelPlacement="outside" placeholder="Enter address" />
{/* Zip Code */}
<Input label="Zip Code" labelPlacement="outside" placeholder="Enter zip code" />
</CardBody>
<CardFooter className="mt-4 justify-end gap-2">
<Button radius="full" variant="bordered">
Cancel
</Button>
<Button color="primary" radius="full">
Save Changes
</Button>
</CardFooter>
</Card>
);
}
"use client";
import type {CardProps} from "@nextui-org/react";
import React from "react";
import {Card, CardHeader, CardBody, Button} from "@nextui-org/react";
import SwitchCell from "./switch-cell";
export default function Component(props: CardProps) {
return (
<Card className="w-full max-w-lg p-2" {...props}>
<CardHeader className="flex flex-col items-start px-4 pb-0 pt-4">
<p className="text-large">Notification Settings</p>
<p className="text-small text-default-500">Manage your notification preferences</p>
</CardHeader>
<CardBody>
<form className="flex flex-col gap-2" onSubmit={(e) => e.preventDefault()}>
<SwitchCell description="Temporarily pause all notifications" label="Pause all" />
<SwitchCell
defaultSelected
description="Get notified when someone follows you"
label="Followers"
/>
<SwitchCell
defaultSelected
description="Get notified when someone likes your post"
label="Likes"
/>
<SwitchCell
description="Get notified when someone comments on your post"
label="Comments"
/>
<SwitchCell
defaultSelected
description="Get notified when someone mentions you in a post"
label="Mentions"
/>
<SwitchCell
defaultSelected
description="Get notified when someone sends you a message"
label="Messages"
/>
<SwitchCell
description="Get notified when someone sends you a friend request"
label="Friend Requests"
/>
<div className="flex w-full justify-end gap-2 pt-4">
<Button variant="bordered">Reset to Default</Button>
<Button color="primary" type="submit">
Save Changes
</Button>
</div>
</form>
</CardBody>
</Card>
);
}
"use client";
import type {CardProps} from "@nextui-org/react";
import React from "react";
import {Card, CardHeader, CardBody, Button} from "@nextui-org/react";
import {Icon} from "@iconify/react";
import SwitchCell from "./switch-cell";
import CellWrapper from "./cell-wrapper";
export default function Component(props: CardProps) {
return (
<Card className="w-full max-w-lg p-2" {...props}>
<CardHeader className="flex flex-col items-start px-4 pb-0 pt-4">
<p className="text-large">Security Settings</p>
<p className="text-small text-default-500">Manage your security preferences</p>
</CardHeader>
<CardBody className="space-y-2">
{/* Email */}
<CellWrapper>
<div>
<p>Email Address</p>
<p className="text-small text-default-500">
The email address associated with your account.
</p>
</div>
<div className="flex w-full flex-wrap items-center justify-end gap-6 sm:w-auto sm:flex-nowrap">
<div className="flex flex-col items-end">
<p>john.doe@mail.com</p>
<p className="text-small text-success">Verified</p>
</div>
<Button
endContent={<Icon icon="solar:pen-2-linear" />}
radius="full"
variant="bordered"
>
Edit
</Button>
</div>
</CellWrapper>
{/* Password */}
<CellWrapper>
<div>
<p>Password</p>
<p className="text-small text-default-500">
Set a unique password to protect your account.
</p>
</div>
<Button radius="full" variant="bordered">
Change
</Button>
</CellWrapper>
{/* Two-Factor Authentication */}
<SwitchCell
defaultSelected
description="Add an extra layer of security to your account."
label="Two-Factor Authentication"
/>
{/* Password Reset Protection */}
<SwitchCell
description="Require additional information to reset your password."
label="Password Reset Protection"
/>
{/* Require Pin */}
<SwitchCell
defaultSelected
description="Require a pin to access your account."
label="Require Pin"
/>
{/* Deactivate Account */}
<CellWrapper>
<div>
<p>Deactivate Account</p>
<p className="text-small text-default-500">
Deactivate your account and delete all your data.
</p>
</div>
<Button radius="full" variant="bordered">
Deactivate
</Button>
</CellWrapper>
{/* Delete Account */}
<CellWrapper>
<div>
<p>Delete Account</p>
<p className="text-small text-default-500">Delete your account and all your data.</p>
</div>
<Button color="danger" radius="full" variant="flat">
Delete
</Button>
</CellWrapper>
</CardBody>
</Card>
);
}
const countries = [
{name: "Argentina", code: "AR"},
{name: "land Islands", code: "AX"},
{name: "Albania", code: "AL"},
{name: "Algeria", code: "DZ"},
{name: "American Samoa", code: "AS"},
{name: "Cameroon", code: "CM"},
{name: "Canada", code: "CA"},
{name: "Cape Verde", code: "CV"},
{name: "Cayman Islands", code: "KY"},
{name: "Central African Republic", code: "CF"},
{name: "Chad", code: "TD"},
{name: "Chile", code: "CL"},
{name: "China", code: "CN"},
{name: "Christmas Island", code: "CX"},
{name: "Cocos (Keeling) Islands", code: "CC"},
{name: "Colombia", code: "CO"},
{name: "Comoros", code: "KM"},
{name: "Congo", code: "CG"},
{name: "Congo, The Democratic Republic of the", code: "CD"},
{name: "Cook Islands", code: "CK"},
{name: "Costa Rica", code: "CR"},
{name: "Macao", code: "MO"},
{name: "Macedonia, The Former Yugoslav Republic of", code: "MK"},
{name: "Madagascar", code: "MG"},
{name: "Malawi", code: "MW"},
{name: "Malaysia", code: "MY"},
{name: "Northern Mariana Islands", code: "MP"},
{name: "Norway", code: "NO"},
{name: "Oman", code: "OM"},
{name: "Pakistan", code: "PK"},
{name: "Palau", code: "PW"},
{name: "Palestinian Territory, Occupied", code: "PS"},
{name: "Panama", code: "PA"},
{name: "Papua New Guinea", code: "PG"},
{name: "Paraguay", code: "PY"},
{name: "Peru", code: "PE"},
{name: "Philippines", code: "PH"},
{name: "Pitcairn", code: "PN"},
{name: "Poland", code: "PL"},
{name: "Portugal", code: "PT"},
{name: "Puerto Rico", code: "PR"},
{name: "Qatar", code: "QA"},
{name: "Switzerland", code: "CH"},
{name: "Syrian Arab Republic", code: "SY"},
{name: "Taiwan, Province of China", code: "TW"},
{name: "Tajikistan", code: "TJ"},
{name: "Tanzania, United Republic of", code: "TZ"},
{name: "Thailand", code: "TH"},
{name: "Timor-Leste", code: "TL"},
{name: "Togo", code: "TG"},
{name: "Tokelau", code: "TK"},
{name: "Tonga", code: "TO"},
{name: "Trinidad and Tobago", code: "TT"},
{name: "Tunisia", code: "TN"},
{name: "Turkey", code: "TR"},
{name: "Turkmenistan", code: "TM"},
{name: "Turks and Caicos Islands", code: "TC"},
{name: "Tuvalu", code: "TV"},
{name: "Uganda", code: "UG"},
{name: "Ukraine", code: "UA"},
{name: "United Arab Emirates", code: "AE"},
{name: "United Kingdom", code: "GB"},
{name: "United States", code: "US"},
{name: "United States Minor Outlying Islands", code: "UM"},
{name: "Uruguay", code: "UY"},
{name: "Uzbekistan", code: "UZ"},
{name: "Vanuatu", code: "VU"},
{name: "Venezuela", code: "VE"},
{name: "Viet Nam", code: "VN"},
{name: "Virgin Islands, British", code: "VG"},
{name: "Virgin Islands, U.S.", code: "VI"},
{name: "Wallis and Futuna", code: "WF"},
{name: "Western Sahara", code: "EH"},
{name: "Yemen", code: "YE"},
{name: "Zambia", code: "ZM"},
{name: "Zimbabwe", code: "ZW"},
];
export default countries;
"use client";
import type {SwitchProps} from "@nextui-org/react";
import React from "react";
import {extendVariants, Switch} from "@nextui-org/react";
import {cn} from "./cn";
const CustomSwitch = extendVariants(Switch, {
variants: {
color: {
foreground: {
wrapper: [
"group-data-[selected=true]:bg-foreground",
"group-data-[selected=true]:text-background",
],
},
},
},
});
export type SwitchCellProps = Omit<SwitchProps, "color"> & {
label: string;
description: string;
color?: SwitchProps["color"] | "foreground";
};
const SwitchCell = React.forwardRef<HTMLInputElement, SwitchCellProps>(
({label, description, classNames, ...props}, ref) => (
<CustomSwitch
ref={ref}
classNames={{
...classNames,
base: cn(
"inline-flex bg-content2 flex-row-reverse w-full max-w-full items-center",
"justify-between cursor-pointer rounded-medium gap-2 p-4",
classNames?.base,
),
}}
{...props}
>
<div className="flex flex-col">
<p className="text-medium">{label}</p>
<p className="text-small text-default-500">{description}</p>
</div>
</CustomSwitch>
),
);
SwitchCell.displayName = "SwitchCell";
export default SwitchCell;
import React from "react";
import {cn} from "./cn";
const CellWrapper = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({children, className, ...props}, ref) => (
<div
ref={ref}
className={cn(
"flex items-center justify-between gap-2 rounded-medium bg-content2 p-4",
className,
)}
{...props}
>
{children}
</div>
),
);
CellWrapper.displayName = "CellWrapper";
export default CellWrapper;
Here is an example dashboard to showcase UserDropdown functionality:
Main Goals
UserDropdown
with the following items:LogoutModal.tsx
Tasks
[x] Create UserDropdown Component:
UserDropdown.tsx
to include:[x] Integrate UserDropdown in Dashboard Header:
UserDropdown
(client component) intoDashboardSidebar
(server component)DashboardSidebar.tsx
to avoid conflicts[x] Profile Settings Page:
Notes
DashboardSidebar
is a client component, whileUserDropdown
is a server component. Efforts have been made to convertUserDropdown
to a client component usingutils/supabase/client#createClient()
, but issues persist.