Skip to content

Instantly share code, notes, and snippets.

@adrianhajdin
Last active April 22, 2024 15:29
Show Gist options
  • Save adrianhajdin/c9e83f0fb1dfcf238dae0cc68a90ba82 to your computer and use it in GitHub Desktop.
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
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`;
},
},
},
};
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;
// 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,
};
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',
},
];
* {
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;
}
}
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
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;
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,
};
<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">
/* 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;
};
@ArfaniAsra
Copy link

when I install refine, the component only has a header, no siders, what's the solution
same issue, it seem refine was update neweast version so we have to refactor the code.

Run the swizzle command in the project directory i fund in the doc npm run refine swizzle

when I install refine, the component only has a header, no siders, what's the solution
same issue, it seem refine was update neweast version so we have to refactor the code.

Run the swizzle command in the project directory i fund in the doc npm run refine swizzle

Hi, I want to ask. after running this command, what should we choose between 3 of these following items:
Data Provider
@refinedev/simple-rest
UI Framework
@refinedev/core
@refinedev/mui

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment