DEV Community

Navnit Rai
Navnit Rai

Posted on

Profile.jsx

import {
  Avatar,
  Button,
  Container,
  Heading,
  HStack,
  Image,
  Input,
  Modal,
  ModalBody,
  ModalCloseButton,
  ModalContent,
  ModalFooter,
  ModalHeader,
  ModalOverlay,
  Stack,
  Text,
  useDisclosure,
  VStack,
} from '@chakra-ui/react'; // Chakra UI components for UI layout
import React, { useEffect, useState } from 'react'; // React and hooks
import toast from 'react-hot-toast'; // For notifications
import { RiDeleteBin7Fill } from 'react-icons/ri'; // Delete icon
import { useDispatch, useSelector } from 'react-redux'; // For Redux state management
import { Link } from 'react-router-dom'; // For routing
import {
  removeFromPlaylist,
  updateProfilePicture,
} from '../../redux/actions/profile'; // Actions for profile management
import { cancelSubscription, loadUser } from '../../redux/actions/user'; // User actions
import { fileUploadCss } from '../Auth/Register'; // CSS for file upload

const Profile = ({ user }) => {
  const dispatch = useDispatch();
  const { loading, message, error } = useSelector(state => state.profile); // Profile state
  const {
    loading: subscriptionLoading,
    message: subscriptionMessage,
    error: subscriptionError,
  } = useSelector(state => state.subscription); // Subscription state

  // Handler to remove course from playlist
  const removeFromPlaylistHandler = async id => {
    await dispatch(removeFromPlaylist(id)); // Dispatch action to remove from playlist
    dispatch(loadUser()); // Reload user data
  };

  // Handler to change the profile picture
  const changeImageSubmitHandler = async (e, image) => {
    e.preventDefault(); // Prevent default form submission
    const myForm = new FormData(); // Create form data
    myForm.append('file', image); // Append the image file
    await dispatch(updateProfilePicture(myForm)); // Dispatch action to update profile picture
    dispatch(loadUser()); // Reload user data
  };

  // Handler to cancel the subscription
  const cancelSubscriptionHandler = () => {
    dispatch(cancelSubscription()); // Dispatch action to cancel subscription
  };

  // Effect to handle notifications and state changes
  useEffect(() => {
    if (error) {
      toast.error(error); // Show error toast
      dispatch({ type: 'clearError' }); // Clear error from state
    }
    if (message) {
      toast.success(message); // Show success toast
      dispatch({ type: 'clearMessage' }); // Clear message from state
    }
    if (subscriptionMessage) {
      toast.success(subscriptionMessage); // Show subscription success toast
      dispatch({ type: 'clearMessage' }); // Clear subscription message
      dispatch(loadUser()); // Reload user data
    }
    if (subscriptionError) {
      toast.error(subscriptionError); // Show subscription error toast
      dispatch({ type: 'clearError' }); // Clear subscription error
    }
  }, [dispatch, error, message, subscriptionError, subscriptionMessage]);

  // Modal control for changing photo
  const { isOpen, onClose, onOpen } = useDisclosure();

  return (
    <Container minH={'95vh'} maxW="container.lg" py="8">
      <Heading children="Profile" m="8" textTransform={'uppercase'} />

      <Stack
        justifyContent={'flex-start'}
        direction={['column', 'row']}
        alignItems={'center'}
        spacing={['8', '16']}
        padding="8"
      >
        <VStack>
          <Avatar boxSize={'48'} src={user.avatar.url} /> {/* User avatar */}
          <Button onClick={onOpen} colorScheme={'yellow'} variant="ghost">
            Change Photo
          </Button>
        </VStack>

        <VStack spacing={'4'} alignItems={['center', 'flex-start']}>
          <HStack>
            <Text children="Name" fontWeight={'bold'} />
            <Text children={user.name} />
          </HStack>
          <HStack>
            <Text children="Email" fontWeight={'bold'} />
            <Text children={user.email} />
          </HStack>
          <HStack>
            <Text children="CreatedAt" fontWeight={'bold'} />
            <Text children={user.createdAt.split('T')[0]} /> {/* Format created date */}
          </HStack>
          {user.role !== 'admin' && (
            <HStack>
              <Text children="Subscription" fontWeight={'bold'} />
              {user.subscription && user.subscription.status === 'active' ? (
                <Button
                  isLoading={subscriptionLoading}
                  onClick={cancelSubscriptionHandler}
                  color={'yellow.500'}
                  variant="unstyled"
                >
                  Cancel Subscription
                </Button>
              ) : (
                <Link to="/subscribe">
                  <Button colorScheme={'yellow'}>Subscribe</Button>
                </Link>
              )}
            </HStack>
          )}
          <Stack direction={['column', 'row']} alignItems={'center'}>
            <Link to="/updateprofile">
              <Button>Update Profile</Button>
            </Link>
            <Link to="/changepassword">
              <Button>Change Password</Button>
            </Link>
          </Stack>
        </VStack>
      </Stack>

      <Heading children="Playlist" size={'md'} my="8" />

      {user.playlist.length > 0 && (
        <Stack
          direction={['column', 'row']}
          alignItems={'center'}
          flexWrap="wrap"
          p="4"
        >
          {user.playlist.map(element => (
            <VStack w="48" m="2" key={element.course}>
              <Image
                boxSize={'full'}
                objectFit="contain"
                src={element.poster} // Course poster
              />

              <HStack>
                <Link to={`/course/${element.course}`}>
                  <Button variant={'ghost'} colorScheme="yellow">
                    Watch Now
                  </Button>
                </Link>

                <Button
                  isLoading={loading}
                  onClick={() => removeFromPlaylistHandler(element.course)} // Remove course from playlist
                >
                  <RiDeleteBin7Fill /> {/* Delete icon */}
                </Button>
              </HStack>
            </VStack>
          ))}
        </Stack>
      )}

      <ChangePhotoBox
        changeImageSubmitHandler={changeImageSubmitHandler}
        isOpen={isOpen}
        onClose={onClose}
        loading={loading}
      />
    </Container>
  );
};

export default Profile;

// Component for changing the profile photo
function ChangePhotoBox({
  isOpen,
  onClose,
  changeImageSubmitHandler,
  loading,
}) {
  const [image, setImage] = useState(''); // State for the selected image
  const [imagePrev, setImagePrev] = useState(''); // State for the preview image

  // Handler to read the selected image
  const changeImage = e => {
    const file = e.target.files[0];
    const reader = new FileReader();

    reader.readAsDataURL(file);

    reader.onloadend = () => {
      setImagePrev(reader.result); // Set preview image
      setImage(file); // Set selected image
    };
  };

  // Close handler for modal
  const closeHandler = () => {
    onClose();
    setImagePrev(''); // Reset preview image
    setImage(''); // Reset selected image
  };

  return (
    <Modal isOpen={isOpen} onClose={closeHandler}>
      <ModalOverlay backdropFilter={'blur(10px)'} />
      <ModalContent>
        <ModalHeader>Change Photo</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Container>
            <form onSubmit={e => changeImageSubmitHandler(e, image)}>
              <VStack spacing={'8'}>
                {imagePrev && <Avatar src={imagePrev} boxSize={'48'} />} {/* Preview of the new image */}

                <Input
                  type={'file'}
                  css={{ '&::file-selector-button': fileUploadCss }} // Custom CSS for file input
                  onChange={changeImage} // Handler for file input change
                />

                <Button
                  isLoading={loading}
                  w="full"
                  colorScheme={'yellow'}
                  type="submit" // Submit button to change the photo
                >
                  Change
                </Button>
              </VStack>
            </form>
          </Container>
        </ModalBody>

        <ModalFooter>
          <Button mr="3" onClick={closeHandler}> // Cancel button
            Cancel
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  );
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)