humblFINANCE / humblFINANCE-frontend

the official codebase for the humblFINANCE web app
https://humbl-finance-frontend.vercel.app
Other
2 stars 1 forks source link

[FEAT]: UserDropdown in Dashboard Header #3

Open jjfantini opened 7 months ago

jjfantini commented 7 months ago

Main Goals

Tasks

Notes

jjfantini commented 7 months ago

Here is some code to build the profile settings page:

Image

Image

Image

jjfantini commented 7 months ago

Component:

"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>
  );
}
jjfantini commented 7 months ago

account-details.tsx

"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>
  );
}
jjfantini commented 7 months ago

notifications-settings.tsx

"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>
  );
}
jjfantini commented 7 months ago

security-settings.tsx

"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>
  );
}
jjfantini commented 7 months ago

contries.ts

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;
jjfantini commented 7 months ago

switch-cell.tsx

"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;
jjfantini commented 7 months ago

cell-wrapper.tsx


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;
jjfantini commented 4 months ago

Here is an example dashboard to showcase UserDropdown functionality:

https://nextui-dashboard-template.vercel.app