Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add profile page #155

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ const DashboardSidePanel = ({ selected, onClose, ...rest }) => {
isSelected={selected === 'support'}
onClick={() => navigate('/dashboard/support')}
/>
<SidebarItem
label="Profile"
isSelected={selected === 'profile'}
onClick={() => navigate('/dashboard/profile')}
/>
<SidebarItem
label="Logout"
isSelected={false}
Expand Down
97 changes: 97 additions & 0 deletions django_project/geohosting/src/components/Profile/Password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, { useState } from 'react';
import {
Box,
Button,
FormControl,
FormLabel,
Input,
IconButton,
SimpleGrid,
Text,
VStack,
InputGroup,
InputRightElement,
} from '@chakra-ui/react';
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons';

const PasswordResetTab: React.FC = () => {
const [passwords, setPasswords] = useState({
oldPassword: '',
newPassword: '',
repeatPassword: '',
});
const [showPassword, setShowPassword] = useState({
oldPassword: false,
newPassword: false,
repeatPassword: false,
});
const [passwordError, setPasswordError] = useState('');

const handlePasswordUpdate = () => {
// Implement password update logic
};

const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>, field: keyof typeof passwords) => {
const { value } = e.target;
setPasswords({ ...passwords, [field]: value });

if (field === 'repeatPassword') {
if (passwords.newPassword !== value) {
setPasswordError('Passwords do not match.');
} else {
setPasswordError('');
}
}
};

const renderInputField = (field: keyof typeof passwords, label: string) => (
<FormControl position="relative">
<FormLabel>{label}</FormLabel>
<InputGroup>
<Input
type={showPassword[field] ? 'text' : 'password'}
value={passwords[field]}
onChange={(e) => handlePasswordChange(e, field)}
borderWidth="0px"
bg="white"
width="400px"
/>
<InputRightElement>
<IconButton
aria-label={showPassword[field] ? 'Hide password' : 'Show password'}
icon={showPassword[field] ? <ViewOffIcon color="gray.500" /> : <ViewIcon color="gray.500" />}
onClick={() => setShowPassword({ ...showPassword, [field]: !showPassword[field] })}
variant="link"
color="gray.500"
size="sm"
/>
</InputRightElement>
</InputGroup>
</FormControl>
);

return (
<Box width={{ base: '100%', md: '71.5%' }} >
<VStack spacing={4} alignItems="flex-start" ml={{ base: 'auto', md: 44 }}>
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4} >
{renderInputField('oldPassword', 'Old Password')}
{renderInputField('newPassword', 'New Password')}
{renderInputField('repeatPassword', 'Repeat New Password')}
</SimpleGrid>

{passwordError && <Text color="red.500">{passwordError}</Text>}

<Button
colorScheme="blue"
onClick={handlePasswordUpdate}
alignSelf="flex-start"
isDisabled={!!passwordError || passwords.newPassword !== passwords.repeatPassword}
>
Change Password
</Button>
</VStack>
</Box>
);
};

export default PasswordResetTab;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import DashboardMainPage from "./DashboardMainPage";
import SupportPage from "./Support/SupportPage";
import OrdersList from './Orders/OrderList';
import OrderDetail from "./Orders/OrderDetail";
import ProfilePage from './Profile/ProfilePage';
import AgreementsTab from '../../components/Profile/Agreements';

const DashboardPage = ({ title="Dashboard" }) => {
Expand Down Expand Up @@ -59,6 +60,7 @@ const DashboardPage = ({ title="Dashboard" }) => {
<Route path="/support" element={<SupportPage />} />
<Route path="/orders" element={<OrdersList />} />
<Route path="/orders/:id" element={<OrderDetail />} />
<Route path='/profile' element={<ProfilePage />} />
</Routes>
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import React, { useEffect, useState } from 'react';
import {
Box,
Flex,
Button,
Input,
FormControl,
FormLabel,
Avatar,
VStack,
Text,
SimpleGrid,
} from '@chakra-ui/react';
import { useDispatch, useSelector } from 'react-redux';
import { AppDispatch, RootState } from '../../../redux/store';
import { fetchUserProfile, updateUserProfile } from '../../../redux/reducers/profileSlice';
import PasswordResetTab from '../../../components/Profile/Password';

const ProfilePage: React.FC = () => {
const dispatch: AppDispatch = useDispatch();
const { user } = useSelector((state: RootState) => state.profile);

const [profileImage, setProfileImage] = useState<File | null>(null);
const [personalInfo, setPersonalInfo] = useState({
name: '',
surname: '',
email: '',
});
const [showPasswordUpdate, setShowPasswordUpdate] = useState(false);
const [billingInfo, setBillingInfo] = useState({
billingName: '',
address: '',
postalCode: '',
country: '',
city: '',
region: '',
});

useEffect(() => {
dispatch(fetchUserProfile());
}, [dispatch]);

useEffect(() => {
if (user) {
setPersonalInfo({
name: user.name,
surname: user.surname,
email: user.email,
});
setBillingInfo({
billingName: user.billingName,
address: user.address,
postalCode: user.postalCode,
country: user.country,
city: user.city,
region: user.region,
});
}
}, [user]);

const handleProfileImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setProfileImage(e.target.files[0]);
}
};

const handleProfileUpdate = () => {
dispatch(updateUserProfile({
...personalInfo,
profileImage,
billingInfo,
}));
};

return (
<Box p={0} mx="auto" >
<Text fontSize="2xl" fontWeight="bold" mb={2} color={'#3e3e3e'}>Profile</Text>
<Box height="2px" bg="blue.500" width="100%" mb={8} />

<Flex
direction={{ base: 'column', md: 'row' }}
align="flex-start"
gap={10}
>
<VStack spacing={4} alignItems="center" mb={{ base: 6, md: 0 }} mt={{ base: 'auto', md: 12}}>
<Avatar size="2xl" src={user?.profileImageUrl} />
<Button colorScheme="orange" onClick={() => console.log("Update Avatar")}>
Update Avatar
</Button>
</VStack>

<VStack spacing={4} alignItems="flex-start" width={{ base: '100%', md: '60%' }}>
<Text fontSize="lg" fontWeight="bold">User Information</Text>
<FormControl>
<FormLabel>Name</FormLabel>
<Input
value={personalInfo.name}
onChange={(e) => setPersonalInfo({ ...personalInfo, name: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>Surname</FormLabel>
<Input
value={personalInfo.surname}
onChange={(e) => setPersonalInfo({ ...personalInfo, surname: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>Email</FormLabel>
<Input
value={personalInfo.email}
onChange={(e) => setPersonalInfo({ ...personalInfo, email: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<Button colorScheme="blue" onClick={() => setShowPasswordUpdate(!showPasswordUpdate)}>
{showPasswordUpdate ? 'Close' : 'Update Password'}
</Button>
</VStack>
</Flex>

{showPasswordUpdate && (
<Box mt={6}>
<PasswordResetTab />
</Box>
)}

<Box mt={14} width={{ base: '100%', md: '60%' }} ml={{base: 'auto', md: 44}}>
<Text fontSize="lg" fontWeight="bold" mb={4}>Billing Information</Text>
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={6}>
<FormControl>
<FormLabel>Institution Name</FormLabel>
<Input
value={billingInfo.billingName}
onChange={(e) => setBillingInfo({ ...billingInfo, billingName: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>Billing Address</FormLabel>
<Input
value={billingInfo.address}
onChange={(e) => setBillingInfo({ ...billingInfo, address: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>Postal Code</FormLabel>
<Input
value={billingInfo.postalCode}
onChange={(e) => setBillingInfo({ ...billingInfo, postalCode: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>Country</FormLabel>
<Input
value={billingInfo.country}
onChange={(e) => setBillingInfo({ ...billingInfo, country: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>City</FormLabel>
<Input
value={billingInfo.city}
onChange={(e) => setBillingInfo({ ...billingInfo, city: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>Region</FormLabel>
<Input
value={billingInfo.region}
onChange={(e) => setBillingInfo({ ...billingInfo, region: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
<FormControl>
<FormLabel>VAT/Tax number</FormLabel>
<Input
value={billingInfo.region}
onChange={(e) => setBillingInfo({ ...billingInfo, region: e.target.value })}
borderWidth="0px"
borderColor="gray.400"
bg="white"
width={{ base: '100%', md: '400px' }}
/>
</FormControl>
</SimpleGrid>
<Button colorScheme="orange" onClick={handleProfileUpdate} mt={4}>
Update Profile
</Button>
</Box>

</Box>
);
};

export default ProfilePage;
Loading
Loading