Prerequisites:
- Node version of 16.20.2
And now, let's go into the cooking step:
Initialize Node application
- Create a directory dedicated for this project. For me the name is front-end-loko-kai
- Open command prompt from that directory
-
Run this command:
npm create vite .
Choose the framework by using up and down arrow keys, I choose React. Then hit enter
Choose the programming language, I use plain JavaScript, then hit enter
Et voila, your application boilerplate is done.
Install npm packages
-
Installing
mui
. This package will be use as css framework
npm install @mui/material @emotion/react @emotion/styled
-
Installing
axios
npm install axios
-
Installing chart dependencies
npm install chart.js react-chartjs-2
Creating .env
This is how my .env
lookslike:
VITE_BASE_URL=http://localhost:8080
Creating project directory structure
As you get boilerplate, you might want to delete some of it because of its irrelevancy. Below is project structure that I use:
*src
- assets
- components
- services
- utils
- views
- App.jsx
- index.css
- main.jsx
- theme.js
*.env
*package.json
Creating global theme
I want to use Roboto font instead the built in one and make sure that my typography is responsive. So in theme.js
I put these lines:
import { createTheme, responsiveFontSizes } from '@mui/material/styles';
let theme = createTheme({
typography: {
fontFamily: [
'Roboto',
'sans-serif'
].join(',')
}
});
theme = responsiveFontSizes(theme);
export default theme;
To apply the theme in your application, you can update your App.jsx
like this:
import { ThemeProvider } from '@mui/material'
import theme from './theme'
import Dashboard from './views/Dashboard'
function App() {
return (
<ThemeProvider theme={theme}>
<Dashboard/>
</ThemeProvider>
)
}
export default App
Don't worry about Dashboard
component, we will be there soon. Then, if you want more independency of styling the application you might wanna update index.css
. Here's mine:
:root {
font-family: 'Roboto', sans-serif;
line-height: 1.5;
background: linear-gradient(90deg, rgba(253,202,198,1) 0%, rgba(252,248,223,1) 100%);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
Creating views
I only make a very simple dashboard. Hence, I only have one view which is dashboard.
- In
view
directory, create a new directory calledDashboard
-
Inside
Dasboard
directory, create a file calledindex.jsx
. This is the content of the file:
import { Container, Grid, Typography } from '@mui/material'; import React from 'react' import Header from '../../components/Header'; import ReportSummary from '../../components/ReportSummary'; import StatusDescription from '../../components/StatusDescription'; const centerContentStyles = { width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', }; function Dashboard() { return ( <div> <Container > <Grid spacing={3} container direction={"column"} alignItems={"center"} justifyContent={"center"} sx={{ height: '97vh' }}> <Grid item md={2} sx={ centerContentStyles }> <Header/> </Grid> <Grid item md={4} sx={ centerContentStyles }> <Grid spacing={4} container sx={{height: '100%'}}> <Grid item md={5}> <StatusDescription /> </Grid> <Grid item md={7}> <ReportSummary /> </Grid> </Grid> </Grid> </Grid> </Container> </div> ) } export default Dashboard;
Creating Header component
- In
components
directory, create a new directory calledHeader
-
Inside
Header
directory, create a file calledindex.jsx
. This is the content of the file:
import { Container, Grid, Typography } from '@mui/material'; import React from 'react' function Header() { return ( <div style={{ display:'flex', alignItems: 'center', width: '100%', height: '70%', backgroundColor: '#FDF2EF', borderRadius: '1rem' }}> <Container sx={{ textAlign: 'center' }}> <Typography variant='h4' fontWeight={"bold"} sx={{ color: '#ad917f' }}>Lokomotif Dashboard</Typography> </Container> </div> ) } export default Header;
Creating Status Description component
- In
components
directory, create a new directory calledStatusDescription
-
Inside
StatusDescription
directory, create a file calledindex.jsx
. This is the content of the file:
import { Grid, Typography } from '@mui/material'; import React from 'react'; import { statusDescription } from '../../utils/Constants'; function StatusDescription() { const detailedStatusDescription = statusDescription; return ( <Grid container gap={2} sx={{ display:'flex', flexDirection: 'column', alignItems: 'left', width: '100%', height: '100%', backgroundColor: '#FDF2EF', borderRadius: '1rem', padding: '1rem' }} > <Grid item> <Typography variant='h6' fontWeight={"bold"} sx={{ color: '#855ef5' }}>Lokomotif Status Description</Typography> </Grid> <Grid item> { detailedStatusDescription.map((status) => { return ( <Typography sx={{ marginBottom: '0.25rem' }}> Status of <span style={{ color: '#855ef5', fontWeight:'bold' }}>{status.status}</span>: {status.description} </Typography> ) }) } </Grid> </Grid> ) } export default StatusDescription;
-
Create a JSON containing status locomotive and its description by adding new file called
Constants.js
insideutils
folder
export const statusDescription = [ { status: 1, description: "New Lokomotif" }, { status: 2, description: "In Use Lokomotif" }, { status: 3, description: "Scheduled Maintenance" }, { status: 4, description: "Under Repair" }, { status: 5, description: "Idle Lokomotif" }, { status: 6, description: "Out of Service - Major Repairs" }, { status: 7, description: "Testing Phase" }, { status: 8, description: "Retired Lokomotif" }, { status: 9, description: "Pending Inspection" }, { status: 10, description: "Emergency Standby" } ];
Creating Report Summary component
This component will be causing error since we are not yet defining API calls service and a helper function. But we will got into it soon.
- In
components
directory, create a new directory calledReportSummary
-
Inside
ReportSummary
directory, create a file calledindex.jsx
. This is the content of the file:
import { Grid, Typography } from '@mui/material' import React, { useEffect, useState } from 'react' import { Chart } from 'chart.js/auto' import { CategoryScale } from 'chart.js/auto' import { Bar } from 'react-chartjs-2' import Grid2 from '@mui/material/Unstable_Grid2/Grid2' import { getAllLokomotifFromYesterday } from '../../services/lokomotif' import { groupLokomotifStatusandTotal } from '../../utils/HelperFunctions' Chart.register(CategoryScale); function ReportSummary() { const [chartData, setChartData] = useState({}); const [isFetched, setIsFetched] = useState(false); useEffect(() => { getAllLokomotifFromYesterday() .then((res) => { const data = groupLokomotifStatusandTotal(res); const labels = data.map((lokomotif => lokomotif.status + 1)); const datasets = { label: 'Total lokomotif in database', data: data.map((lokomotif => lokomotif.total)), backgroundColor: ["#ad917f"] } setChartData({ labels: labels, datasets: [datasets], }) setIsFetched(true); }).catch((err) => { console.log(err); }); }, []); return ( <Grid container gap={2} sx={{ display:'flex', flexDirection: 'column', alignItems: 'left', width: '100%', height: '100%', backgroundColor: '#FDF2EF', borderRadius: '1rem', padding: '1rem' }} > <Grid item> <Typography variant='h6' fontWeight={"bold"} sx={{ color: '#855ef5' }}>Lokomotifs Status Statistic in Database</Typography> </Grid> <Grid2 item> { isFetched && ( <Bar data={chartData} options={{ plugins: { legend: { display: false } }, scales: { x: { title: { text: 'Lokomotif status', display: true } }, y: { title: { text: 'Total lokomotif in database', display: true }, ticks: { stepSize: 1 } }, } }} /> ) } </Grid2> </Grid> ) } export default ReportSummary
Creating a helper function
Since the summary will only contain locomotive status and its total number in database, we need some function to create an object as what we need since API return will be a whole different object in term of structure.
-
Inside
utils
directory, create a file calledHelperFunction.js
export const groupLokomotifStatusandTotal = (lokomotifs) => { const totalStatus = 10; let grouppedLokomotif = []; for(var i = 0; i < totalStatus; i++) { let totalLokomotif = lokomotifs.filter(lokomotif => lokomotif.status === i.toString()).length; let temp = { status: i, total: totalLokomotif } grouppedLokomotif.push(temp); } return grouppedLokomotif; }
Creating API call service
- Inside
services
directory, create a directory calledconfig
-
Inside
config
directory, create a file calledaxios.js
import axios from "axios"; const BASE_URL = import.meta.env.VITE_BASE_URL; const api = axios.create({ baseURL: BASE_URL, }); export default api;
Inside
services
directory, create a directory calledlokomotif
-
Inside
lokomotif
directory, create a file calledindex.js
import api from "../config/axios"; export const getAllLokomotifFromYesterday = async () => { try { const res = await api.get('/lokomotifs'); const pageSize = res.data.page.totalPages; let allLokomotifData = []; for(var i = 0; i < pageSize; i++) { const resOnPage = await getLokomotifDataByPage(i) allLokomotifData = [...allLokomotifData, ...resOnPage];; } return allLokomotifData; } catch (error) { console.log(error); } }; const getLokomotifDataByPage = async (page) => { try { const res = await api.get(`/lokomotifs?page=${page}`); return res.data._embedded.lokomotifs; } catch (error) { console.log(error); } }
Top comments (0)