Last active
April 6, 2025 22:14
-
-
Save adrianhajdin/c9e83f0fb1dfcf238dae0cc68a90ba82 to your computer and use it in GitHub Desktop.
Build and Deploy a Full Stack MERN Dashboard App With CRUD, Auth, and Charts Using Refine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ApexOptions } from 'apexcharts'; | |
export const TotalRevenueSeries = [ | |
{ | |
name: 'Last Month', | |
data: [183, 124, 115, 85, 143, 143, 96], | |
}, | |
{ | |
name: 'Running Month', | |
data: [95, 84, 72, 44, 108, 108, 47], | |
}, | |
]; | |
export const TotalRevenueOptions: ApexOptions = { | |
chart: { | |
type: 'bar', | |
toolbar: { | |
show: false, | |
}, | |
}, | |
colors: ['#475BE8', '#CFC8FF'], | |
plotOptions: { | |
bar: { | |
borderRadius: 4, | |
horizontal: false, | |
columnWidth: '55%', | |
}, | |
}, | |
dataLabels: { | |
enabled: false, | |
}, | |
grid: { | |
show: false, | |
}, | |
stroke: { | |
colors: ['transparent'], | |
width: 4, | |
}, | |
xaxis: { | |
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'], | |
}, | |
yaxis: { | |
title: { | |
text: '$ (thousands)', | |
}, | |
}, | |
fill: { | |
opacity: 1, | |
}, | |
legend: { | |
position: 'top', | |
horizontalAlign: 'right', | |
}, | |
tooltip: { | |
y: { | |
formatter(val: number) { | |
return `$ ${val} thousands`; | |
}, | |
}, | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Email, Phone, Place } from '@mui/icons-material'; | |
import { Box, Stack, Typography } from '@pankod/refine-mui'; | |
import { ProfileProps, PropertyProps } from 'interfaces/common'; | |
import PropertyCard from './PropertyCard'; | |
function checkImage(url: any) { | |
let img = new Image(); | |
img.src = url; | |
return img.width !== 0 && img.height !== 0; | |
} | |
const Profile = ({ type, name, avatar, email, properties }: ProfileProps) => ( | |
<Box> | |
<Typography fontSize={25} fontWeight={700} color="#11142D">{type} Profile</Typography> | |
<Box | |
mt="20px" | |
borderRadius="15px" | |
padding="20px" | |
bgcolor="#FCFCFC" | |
> | |
<Box | |
sx={{ | |
display: 'flex', | |
flexDirection: { xs: 'column', md: 'row' }, | |
gap: 2.5, | |
}} | |
> | |
<img | |
src="https://images.unsplash.com/photo-1618005198919-d3d4b5a92ead?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1374&q=80" | |
width={340} | |
height={320} | |
alt="abstract" | |
className="my_profile-bg" | |
/> | |
<Box | |
flex={1} | |
sx={{ marginTop: { md: '58px' }, marginLeft: { xs: '20px', md: '0px' } }} | |
> | |
<Box flex={1} display="flex" flexDirection={{ xs: 'column', md: 'row' }} gap="20px"> | |
<img | |
src={checkImage(avatar) ? avatar : "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/User-avatar.svg/2048px-User-avatar.svg.png"} | |
width={78} | |
height={78} | |
alt="user_profile" | |
className="my_profile_user-img" | |
/> | |
<Box flex={1} display="flex" flexDirection="column" justifyContent="space-between" gap="30px"> | |
<Stack direction="column"> | |
<Typography fontSize={22} fontWeight={600} color="#11142D">{name}</Typography> | |
<Typography fontSize={16} color="#808191">Realestate Agent</Typography> | |
</Stack> | |
<Stack direction="column" gap="30px"> | |
<Stack gap="15px"> | |
<Typography fontSize={14} fontWeight={500} color="#808191">Address</Typography> | |
<Box display="flex" flexDirection="row" alignItems="center" gap="10px"> | |
<Place sx={{ color: '#11142D' }} /> | |
<Typography fontSize={14} color="#11142D">4517 Washington Ave. Manchaster, Kentucky 39495</Typography> | |
</Box> | |
</Stack> | |
<Stack direction="row" flexWrap="wrap" gap="20px" pb={4}> | |
<Stack flex={1} gap="15px"> | |
<Typography fontSize={14} fontWeight={500} color="#808191">Phone Number</Typography> | |
<Box display="flex" flexDirection="row" alignItems="center" gap="10px"> | |
<Phone sx={{ color: '#11142D' }} /> | |
<Typography fontSize={14} color="#11142D" noWrap>+0123 456 7890</Typography> | |
</Box> | |
</Stack> | |
<Stack flex={1} gap="15px"> | |
<Typography fontSize={14} fontWeight={500} color="#808191">Email</Typography> | |
<Box display="flex" flexDirection="row" alignItems="center" gap="10px"> | |
<Email sx={{ color: '#11142D' }} /> | |
<Typography fontSize={14} color="#11142D">{email}</Typography> | |
</Box> | |
</Stack> | |
</Stack> | |
</Stack> | |
</Box> | |
</Box> | |
</Box> | |
</Box> | |
</Box> | |
{properties.length > 0 && ( | |
<Box | |
mt={2.5} | |
borderRadius="15px" | |
padding="20px" | |
bgcolor="#FCFCFC" | |
> | |
<Typography fontSize={18} fontWeight={600} color="#11142D">{type} Properties</Typography> | |
<Box | |
mt={2.5} | |
sx={{ | |
display: 'flex', | |
flexWrap: 'wrap', | |
gap: 2.5, | |
}} | |
> | |
{properties?.map((property: PropertyProps) => ( | |
<PropertyCard key={property._id} id={property._id} | |
title={property.title} | |
location={property.location} | |
price={property.price} | |
photo={property.photo} | |
/> | |
))} | |
</Box> | |
</Box> | |
)} | |
</Box> | |
); | |
export default Profile; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// common | |
import Profile from './common/Profile'; | |
import PropertyCard from './common/PropertyCard'; | |
import CustomButton from './common/CustomButton'; | |
// charts | |
import PieChart from './charts/PieChart'; | |
import PropertyReferrals from './charts/PropertyReferrals'; | |
import TotalRevenue from './charts/TotalRevenue'; | |
// agent | |
import AgentCard from './agent/AgentCard'; | |
// home | |
import TopAgent from './home/TopAgent'; | |
export { | |
Profile, | |
PropertyCard, | |
CustomButton, | |
PieChart, | |
PropertyReferrals, | |
TotalRevenue, | |
AgentCard, | |
TopAgent, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const propertyReferralsInfo = [ | |
{ | |
title: 'Social Media', | |
percentage: 64, | |
color: '#6C5DD3', | |
}, | |
{ | |
title: 'Marketplace', | |
percentage: 40, | |
color: '#7FBA7A', | |
}, | |
{ | |
title: 'Websites', | |
percentage: 50, | |
color: '#FFCE73', | |
}, | |
{ | |
title: 'Digital Ads', | |
percentage: 80, | |
color: '#FFA2C0', | |
}, | |
{ | |
title: 'Others', | |
percentage: 15, | |
color: '#F45252', | |
}, | |
]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* { | |
font-family: 'Manrope', sans-serif !important; | |
} | |
a { | |
text-decoration: none !important; | |
} | |
.my_profile-bg { | |
width: 340px; | |
border-top-left-radius: 15px; | |
border-bottom-left-radius: 15px; | |
} | |
.my_profile_user-img { | |
border-radius: 100%; | |
margin-left: -64px; | |
} | |
.property_details-img { | |
width: 100%; | |
} | |
@media screen and (max-width: 900px) { | |
.my_profile-bg { | |
width: 100%; | |
border-radius: 15px; | |
} | |
.my_profile_user-img { | |
margin-left: 0px; | |
margin-top: -64px; | |
} | |
.property_details-img { | |
width: 100%; | |
height: auto; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState } from 'react'; | |
import { useGetIdentity } from '@pankod/refine-core'; | |
import { FieldValues, useForm } from '@pankod/refine-react-hook-form'; | |
import Form from 'components/common/Form'; | |
const CreateProperty = () => { | |
const { data: user } = useGetIdentity(); | |
const [propertyImage, setPropertyImage] = useState({ name: '', url: '' }); | |
const { refineCore: { onFinish, formLoading }, register, handleSubmit } = useForm(); | |
const handleImageChange = (file: File) => { | |
const reader = (readFile: File) => new Promise<string>((resolve, reject) => { | |
const fileReader = new FileReader(); | |
fileReader.onload = () => resolve(fileReader.result as string); | |
fileReader.readAsDataURL(readFile); | |
}); | |
reader(file).then((result: string) => setPropertyImage({ name: file?.name, url: result })); | |
}; | |
const onFinishHandler = async (data: FieldValues) => { | |
if(!propertyImage.name) return alert('Please select an image'); | |
await onFinish({ ...data, photo: propertyImage.url, email: user.email }) | |
}; | |
return ( | |
<Form | |
type="Create" | |
register={register} | |
onFinish={onFinish} | |
formLoading={formLoading} | |
handleSubmit={handleSubmit} | |
handleImageChange={handleImageChange} | |
onFinishHandler={onFinishHandler} | |
propertyImage={propertyImage} | |
/> | |
) | |
} | |
export default CreateProperty |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState } from 'react'; | |
import { useGetIdentity } from '@pankod/refine-core'; | |
import { FieldValues, useForm } from '@pankod/refine-react-hook-form'; | |
import Form from 'components/common/Form'; | |
const CreateProperty = () => { | |
const { data: user } = useGetIdentity(); | |
const [propertyImage, setPropertyImage] = useState({ name: '', url: '' }); | |
const { refineCore: { onFinish, formLoading }, register, handleSubmit } = useForm(); | |
const handleImageChange = (file: File) => { | |
const reader = (readFile: File) => new Promise<string>((resolve, reject) => { | |
const fileReader = new FileReader(); | |
fileReader.onload = () => resolve(fileReader.result as string); | |
fileReader.readAsDataURL(readFile); | |
}); | |
reader(file).then((result: string) => setPropertyImage({ name: file?.name, url: result })); | |
}; | |
const onFinishHandler = async (data: FieldValues) => { | |
if (!propertyImage.name) return alert('Please upload a property image'); | |
await onFinish({ ...data, photo: propertyImage.url, email: user.email }); | |
}; | |
return ( | |
<Form | |
type="Edit" | |
register={register} | |
onFinish={onFinish} | |
formLoading={formLoading} | |
handleSubmit={handleSubmit} | |
handleImageChange={handleImageChange} | |
onFinishHandler={onFinishHandler} | |
propertyImage={propertyImage} | |
/> | |
); | |
}; | |
export default CreateProperty; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AgentProfile from './agent-profile'; | |
import Agents from './agent'; | |
import AllProperties from './all-properties'; | |
import CreateProperty from './create-property'; | |
import Home from './home'; | |
import { Login } from './login'; | |
import MyProfile from './my-profile'; | |
import PropertyDetails from './property-details'; | |
import EditProperty from './edit-property'; | |
export { | |
AgentProfile, | |
Agents, | |
AllProperties, | |
CreateProperty, | |
Home, | |
Login, | |
MyProfile, | |
PropertyDetails, | |
EditProperty, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link rel="preconnect" href="https://fonts.googleapis.com"> | |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@200;300;400;500;600;700;800&display=swap" rel="stylesheet"> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-plusplus */ | |
import { FormValues } from 'interfaces/property'; | |
export const validateForm = (formValues: FormValues) => { | |
const errors: { message: string } = { message: '' }; | |
let hasError = false; | |
Object.keys(formValues).forEach((key) => { | |
switch (key) { | |
case 'title': | |
if (!formValues.title) { | |
errors.message = 'Title is required'; | |
hasError = true; | |
} | |
break; | |
case 'description': | |
if (!formValues.description) { | |
errors.message = 'Description is required'; | |
hasError = true; | |
} | |
break; | |
case 'propertyType': | |
if (!formValues.propertyType) { | |
errors.message = 'Property type is required'; | |
hasError = true; | |
} | |
break; | |
case 'location': | |
if (!formValues.location) { | |
errors.message = 'Location is required'; | |
hasError = true; | |
} | |
break; | |
case 'price': | |
if (!formValues.price) { | |
errors.message = 'Price is required'; | |
hasError = true; | |
} | |
break; | |
default: | |
hasError = false; | |
} | |
}); | |
return { hasError, errors }; | |
}; | |
export const hasChanged = (initialValues: FormValues, currentValues: FormValues) => { | |
const initialValuesArray = Object.values(initialValues); | |
const currentValuesArray = Object.values(currentValues); | |
for (let i = 0; i < initialValuesArray.length; i++) { | |
if (initialValuesArray[i] !== currentValuesArray[i]) { | |
return true; | |
} | |
} | |
return false; | |
}; |
import { useEffect, useRef } from "react"; import { useLogin } from "@refinedev/core"; import Container from "@mui/material/Container"; import Box from "@mui/material/Box"; // import { yariga } from "../assets";
import { CredentialResponse } from "../interfaces/google";
export const Login: React.FC = () => { const { mutate: login } = useLogin({ v3LegacyAuthProviderCompatible: true, });
const GoogleButton = (): JSX.Element => { const divRef = useRef<HTMLDivElement>(null); useEffect(() => { if ( typeof window === "undefined" || !window.google || !divRef.current ) { return; } try { console.log("Initializing Google Button"); window.google.accounts.id.initialize({ ux_mode: "popup", client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID, callback: async (res: CredentialResponse) => { console.log("Google callback response:", res); if (res.credential) { console.log("Login successful, calling login function."); login(res); } else { console.log("No credential received."); } }, }); window.google.accounts.id.renderButton(divRef.current, { theme: "filled_blue", size: "medium", type: "standard", }); } catch (error) { console.log("Error rendering Google Button:", error); } }, [login]); return <div ref={divRef} />;
};
return ( <Box component="div" sx={{ backgroundColor: "white" }}> <Container component="main" maxWidth="xs" sx={{ display: "flex", flexDirection: "column", justifyContent: "center", height: "100vh", }} > <Box sx={{ display: "flex", justifyContent: "center", flexDirection: "column", alignItems: "center", }} > <div> {/* <img src={yariga} alt="Yariga Logo" /> */} </div> <Box mt={4}> <GoogleButton /> </Box> </Box> </Container> </Box> );
};
after successfull login the page is redirecting to the same login page where my Google button in present why so? Please help
I got the same issue, have you resolve it yet?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this any one know how to change in light mode and i was working on the project once logout and then back in it turn into dark mode
