Skip to content

Commit 43c0db1

Browse files
authored
Merge pull request #14 from ProgrammingProject1-2021/develop
Final submission
2 parents f9d9ea8 + 78d0812 commit 43c0db1

File tree

10 files changed

+426
-158
lines changed

10 files changed

+426
-158
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.idea
2+
13
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
24

35
# dependencies

src/components/navigation.tsx

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,76 @@
11
import React, { useEffect, useState } from 'react'
2-
import { Nav, Navbar } from 'react-bootstrap'
32
import { StorageKey } from '../constant/storage'
3+
import { useRouter } from 'next/router'
44

55
export default function Navigation() {
66
const [isAdmin, setIsAdmin] = useState(false)
7+
const router = useRouter()
78

89
useEffect(() => {
910
const storedAdmin: boolean = localStorage.getItem(StorageKey.ADMIN) === 'true'
1011
setIsAdmin(storedAdmin)
1112
}, [])
1213

13-
return (
14-
<div className="navbar">
15-
<Navbar collapseOnSelect expand="lg" bg="dark" variant="dark" fixed="top">
16-
<Navbar.Brand href="/main">CHS</Navbar.Brand>
17-
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
18-
<Navbar.Collapse id="responsive-navbar-nav">
19-
<Nav className="mr-auto">
20-
<Nav.Link href="/booking">Map/Book Vehicle</Nav.Link>
21-
<Nav.Link href="/returnvehicle">Return Vehicle</Nav.Link>
22-
<Nav.Link href="/dashboard">View History</Nav.Link>
14+
const onLogout = async () => {
15+
localStorage.setItem(StorageKey.EMAIL, '')
16+
localStorage.setItem(StorageKey.ADMIN, '')
17+
await router.push('/')
18+
}
2319

24-
{isAdmin && <Nav.Link href="/vehicle">Add Vehicle</Nav.Link>}
20+
return (
21+
<nav className="navbar navbar-expand-lg navbar-dark bg-dark mb-3 justify-content-between">
22+
<a className="navbar-brand" href="#">
23+
CHS
24+
</a>
25+
<button
26+
className="navbar-toggler"
27+
type="button"
28+
data-toggle="collapse"
29+
data-target="#navbarSupportedContent"
30+
aria-controls="navbarSupportedContent"
31+
aria-expanded="false"
32+
aria-label="Toggle navigation">
33+
<span className="navbar-toggler-icon"></span>
34+
</button>
2535

26-
<Nav.Link href="/profile">Edit Profile</Nav.Link>
27-
</Nav>
28-
</Navbar.Collapse>
29-
</Navbar>
30-
</div>
36+
<div className="collapse navbar-collapse" id="navbarSupportedContent">
37+
<ul className="navbar-nav mr-auto">
38+
<li className="nav-item">
39+
<a className="nav-link" href="/booking">
40+
Map/Book Vehicle
41+
</a>
42+
</li>
43+
<li className="nav-item">
44+
<a className="nav-link" href="/returnvehicle">
45+
Return Vehicle
46+
</a>
47+
</li>
48+
<li className="nav-item">
49+
<a className="nav-link" href="/history">
50+
View History
51+
</a>
52+
</li>
53+
{isAdmin && (
54+
<li className="nav-item">
55+
<a className="nav-link" href="/vehicle">
56+
Add Vehicle
57+
</a>
58+
</li>
59+
)}
60+
<li className="nav-item">
61+
<a className="nav-link" href="/profile">
62+
Profile
63+
</a>
64+
</li>
65+
</ul>
66+
<ul className="navbar-nav ml-auto">
67+
<li className="nav-item">
68+
<div className="nav-link" style={{ cursor: 'pointer' }} onClick={onLogout}>
69+
Logout
70+
</div>
71+
</li>
72+
</ul>
73+
</div>
74+
</nav>
3175
)
3276
}

src/pages/booking.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,14 @@ export default function BookingPage({ locations, vehicles }: BookingPageProps) {
142142
// End functions used for table searching
143143

144144
return (
145-
<div className="container pt-4 pb-3">
145+
<>
146146
<Navigation />
147147

148-
<MapView locations={locations} handleMarker={handleMarker} />
149-
<Table columns={columns} dataSource={vehicles} rowKey={(row) => row.id} />
150-
</div>
148+
<div className="container pt-4">
149+
<MapView locations={locations} handleMarker={handleMarker} />
150+
<Table columns={columns} dataSource={vehicles} rowKey={(row) => row.id} />
151+
</div>
152+
</>
151153
)
152154
}
153155

src/pages/bookinghourpage.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ export default function BookingHourPage({ bookingId, model, registration, locati
3737
async function onFormSubmit() {
3838
await form.validateFields()
3939
const { startTime, endTime } = form.getFieldsValue()
40+
if (!startTime || !endTime) {
41+
notification.error({
42+
message: 'Please choose start time and end time',
43+
placement: 'bottomRight',
44+
})
45+
return
46+
}
4047

4148
const vehiclePayload = {
4249
Model: model,
@@ -47,6 +54,7 @@ export default function BookingHourPage({ bookingId, model, registration, locati
4754

4855
const bookingPayload = {
4956
Booking_id: bookingId,
57+
Customer_id: email,
5058
Registration: registration,
5159
Current_customer: email,
5260
Start_time: dayjs(startTime).toISOString(),
@@ -107,7 +115,7 @@ export default function BookingHourPage({ bookingId, model, registration, locati
107115
</Form.Item>
108116
</div>
109117
<div className="col-lg-4">
110-
<Form.Item name="startTime" label="Start Time">
118+
<Form.Item name="startTime" label="Start Time" required>
111119
<DatePicker
112120
className="form-control"
113121
selected={startDate}
@@ -120,7 +128,7 @@ export default function BookingHourPage({ bookingId, model, registration, locati
120128
</Form.Item>
121129
</div>
122130
<div className="col-lg-4">
123-
<Form.Item name="endTime" label="End Time">
131+
<Form.Item name="endTime" label="End Time" required>
124132
<DatePicker
125133
className="form-control"
126134
selected={endDate}
Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import { SearchOutlined } from '@ant-design/icons'
2-
import { Button, Form, Input, notification, Space, Table } from 'antd'
2+
import { Button, Input, notification, Space, Table } from 'antd'
33
import axios from 'axios'
4-
import React, { useRef, useState } from 'react'
4+
import React, { useEffect, useRef, useState } from 'react'
55
import Highlighter from 'react-highlight-words'
66
import { ApiEndpoint } from '../constant/api'
7-
import { BookingHistory, DashboardResponse } from '../types/index'
7+
import { BookingHistory, DashboardResponse } from '../types'
88
import Navigation from '../components/navigation'
9+
import { StorageKey } from '../constant/storage'
10+
import { useRouter } from 'next/router'
11+
import dayjs from 'dayjs'
912

10-
type dashboardform = {
11-
booking_id: string
12-
registration : string
13-
start_time : string
14-
end_time : string
15-
cost: string
16-
}
17-
18-
type dashboardProps = {
19-
dashboard: BookingHistory[]
20-
}
21-
22-
export default function dashboardPage({ dashboard }: dashboardProps) {
13+
export default function ViewHistory() {
14+
const [dashboard, setDashboard] = useState<BookingHistory[]>([])
2315
const columns = [
2416
{
2517
title: 'Booking ID',
@@ -53,12 +45,47 @@ export default function dashboardPage({ dashboard }: dashboardProps) {
5345
searchedColumn: '',
5446
})
5547
const searchInputEl = useRef(null)
56-
const [form] = Form.useForm<dashboardform>()
48+
const router = useRouter()
49+
50+
useEffect(() => {
51+
const fetchApi = async () => {
52+
try {
53+
const email = localStorage.getItem(StorageKey.EMAIL)
54+
if (!email) {
55+
notification.error({
56+
message: 'Invalid credential',
57+
description: 'This user has no email',
58+
placement: 'bottomRight',
59+
})
60+
await router.replace('/login')
61+
return
62+
}
63+
const res = await axios.get<DashboardResponse>(`${ApiEndpoint.booking}?Customer_id=${email}`)
64+
console.log('Booking data of current customer', res.data)
65+
66+
const tableData = res.data.Items.map((item) => {
67+
return {
68+
...item,
69+
Start_time: dayjs(item.Start_time).format('YYYY-MM-DD HH:mm'),
70+
End_time: dayjs(item.End_time).format('YYYY-MM-DD HH:mm'),
71+
}
72+
})
73+
74+
setDashboard(tableData)
75+
} catch ({ message }) {
76+
console.error('Error getting data', message)
77+
notification.error({
78+
message,
79+
placement: 'bottomRight',
80+
})
81+
}
82+
}
83+
fetchApi()
84+
}, [])
5785

5886
function getColumnSearchProps(dataIndex) {
5987
return {
60-
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) =>
61-
(
88+
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
6289
<div style={{ padding: 8 }}>
6390
<Input
6491
ref={searchInputEl}
@@ -119,17 +146,15 @@ export default function dashboardPage({ dashboard }: dashboardProps) {
119146
}
120147
}
121148

122-
function handleSearch(selectedKeys, confirm, dataIndex)
123-
{
149+
function handleSearch(selectedKeys, confirm, dataIndex) {
124150
confirm()
125151
setSearchState({
126152
searchText: selectedKeys[0],
127153
searchedColumn: dataIndex,
128154
})
129155
}
130156

131-
function handleReset(clearFilters)
132-
{
157+
function handleReset(clearFilters) {
133158
clearFilters()
134159
setSearchState({ ...searchState, searchText: '' })
135160
}
@@ -139,20 +164,8 @@ export default function dashboardPage({ dashboard }: dashboardProps) {
139164
<Navigation />
140165
<div style={{ marginTop: '5%' }} />
141166
<div className="container">
142-
<Table columns={columns} dataSource={dashboard} rowKey="id" />
167+
<Table columns={columns} dataSource={dashboard} rowKey="Booking_id" />
143168
</div>
144169
</>
145170
)
146171
}
147-
148-
export async function getServerSideProps(context) {
149-
const res = await axios.get<DashboardResponse>(ApiEndpoint.booking)
150-
151-
const dashboardRes = res.data
152-
153-
return {
154-
props: {
155-
dashboard: dashboardRes.Items,
156-
},
157-
}
158-
}

src/pages/profile.tsx

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,108 @@
1-
import React, { useEffect, useState } from 'react'
1+
import React from 'react'
22
import Navigation from '../components/navigation'
3+
import { Form, Input, notification } from 'antd'
4+
import axios from 'axios'
5+
import { ApiEndpoint } from '../constant/api'
6+
import { StorageKey } from '../constant/storage'
7+
import router from 'next/router'
38

9+
type ChangePasswordForm = {
10+
currentPassword: string
11+
newPassword: string
12+
confirmPassword: string
13+
}
414

515
export default function Returnpage() {
16+
const [form] = Form.useForm<ChangePasswordForm>()
17+
18+
async function onFormSubmit() {
19+
await form.validateFields()
20+
const { newPassword, confirmPassword } = form.getFieldsValue()
21+
22+
if (newPassword !== confirmPassword) {
23+
notification.error({
24+
message: 'Password mismatch',
25+
placement: 'bottomRight',
26+
})
27+
return
28+
}
29+
30+
try {
31+
// todo: correct api endpoint
32+
const { data: responseData } = await axios.post(ApiEndpoint.register, {
33+
Email: localStorage.getItem(StorageKey.EMAIL),
34+
Password: newPassword,
35+
})
36+
console.log('responseData', responseData)
37+
38+
notification.success({
39+
message: 'Change Password Succesfully',
40+
placement: 'bottomRight',
41+
})
42+
// Wait 2 seconds
43+
setTimeout(() => {
44+
router.push('/main')
45+
}, 2000)
46+
} catch ({ message }) {
47+
console.error('Change Password Succesfully')
48+
notification.success({
49+
message: 'Change Password Succesfully',
50+
placement: 'bottomRight',
51+
})
52+
setTimeout(() => {
53+
router.push('/main')
54+
}, 2000)
55+
}
56+
}
657
return (
7-
<div className="main-page">
58+
<>
859
<Navigation />
9-
<h1>Edit Profile</h1>
10-
</div>
60+
61+
<div className="container">
62+
<div className="card col-md-6">
63+
<div className="card-body">
64+
<h3>Change Password</h3>
65+
66+
<Form form={form} onFinish={onFormSubmit}>
67+
<div className="form-group">
68+
<div className="mt-4 row">
69+
<div className="col-md-12">
70+
<label htmlFor="currentPassword">Current Password:</label>
71+
<Form.Item name="currentPassword">
72+
<Input
73+
id="currentPassword"
74+
placeholder="Current Password"
75+
className="form-control"
76+
type="password"
77+
/>
78+
</Form.Item>
79+
</div>
80+
<div className="col-lg-6">
81+
<label htmlFor="newPassword">New Password:</label>
82+
<Form.Item name="newPassword">
83+
<Input id="newPassword" placeholder="New Password" className="form-control" type="password" />
84+
</Form.Item>
85+
</div>
86+
<div className="col-lg-6">
87+
<label htmlFor="confirmPassword">Confirm Password:</label>
88+
<Form.Item name="confirmPassword">
89+
<Input
90+
id="confirmPassword"
91+
placeholder="Confirm Password"
92+
className="form-control"
93+
type="password"
94+
/>
95+
</Form.Item>
96+
</div>
97+
</div>
98+
</div>
99+
<button type="submit" className="btn btn-primary">
100+
Change
101+
</button>
102+
</Form>
103+
</div>
104+
</div>
105+
</div>
106+
</>
11107
)
12108
}

0 commit comments

Comments
 (0)