Skip to content

Commit 37dfc43

Browse files
authored
Merge pull request #19 from OzPol/dev-buppgard-thrusday2
Dev buppgard thrusday2
2 parents 0608160 + b429cfb commit 37dfc43

36 files changed

+765
-100
lines changed

app/favicon.ico

25.3 KB
Binary file not shown.

app/layout.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Metadata } from "next";
2+
import { Inter } from "next/font/google";
3+
import "./globals.css";
4+
5+
const inter = Inter({ subsets: ["latin"] });
6+
7+
export const metadata: Metadata = {
8+
title: "ProBooker",
9+
description: "Connect with the pros, book with confidence",
10+
};
11+
12+
export default function RootLayout({
13+
children,
14+
}: Readonly<{
15+
children: React.ReactNode;
16+
}>) {
17+
return (
18+
<html lang="en">
19+
<body className={inter.className}>{children}</body>
20+
</html>
21+
);
22+
}
File renamed without changes.

components/CustomerAccountDetails.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const CustomerAccountDetails = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">Account Details</h2>
5+
<p>This is a placeholder for the customer account details section.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default CustomerAccountDetails;
11+
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const CustomerProfileOverview = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">Profile Overview</h2>
5+
<p>This is a placeholder for the customer profile overview section.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default CustomerProfileOverview;
11+

components/CustomerSearchServices.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const CustomerSearchServices = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">Search Services</h2>
5+
<input
6+
type="text"
7+
placeholder="Search for services..."
8+
className="w-full p-2 border rounded mb-4"
9+
/>
10+
<p>This is a placeholder for the customer search services section.</p>
11+
</div>
12+
);
13+
};
14+
15+
export default CustomerSearchServices;
16+

components/CustomerViewBookings.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const CustomerViewBookings = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">View Bookings</h2>
5+
<p>This is a placeholder for the customer bookings section.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default CustomerViewBookings;
11+
File renamed without changes.
File renamed without changes.
File renamed without changes.

components/LoginForm.tsx

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use client'
2+
3+
import { useState, ChangeEvent, FormEvent } from 'react';
4+
5+
interface LoginFormProps {
6+
onSwitchToRegister: () => void;
7+
}
8+
9+
const LoginForm: React.FC<LoginFormProps> = ({ onSwitchToRegister }) => {
10+
const [formData, setFormData] = useState({ email: '', password: '' });
11+
12+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
13+
const { name, value } = e.target;
14+
setFormData((prevData) => ({ ...prevData, [name]: value }));
15+
};
16+
17+
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
18+
e.preventDefault();
19+
// Handle login logic
20+
console.log('Login Form Data:', formData);
21+
};
22+
23+
return (
24+
<form onSubmit={handleSubmit} className="w-full max-w-sm">
25+
<input
26+
type="email"
27+
name="email"
28+
placeholder="Email"
29+
className="w-full p-2 mb-4 border border-gray-300 rounded"
30+
value={formData.email}
31+
onChange={handleChange}
32+
required
33+
aria-label="Email"
34+
/>
35+
<input
36+
type="password"
37+
name="password"
38+
placeholder="Password"
39+
className="w-full p-2 mb-4 border border-gray-300 rounded"
40+
value={formData.password}
41+
onChange={handleChange}
42+
required
43+
aria-label="Password"
44+
/>
45+
<button type="submit" className="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600">
46+
Log In
47+
</button>
48+
<button
49+
type="button"
50+
className="w-full mt-2 text-blue-500 underline"
51+
onClick={onSwitchToRegister}
52+
>
53+
Register
54+
</button>
55+
</form>
56+
);
57+
};
58+
59+
export default LoginForm;

components/RegisterForm.tsx

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
'use client'
2+
3+
import { useState, ChangeEvent, FormEvent } from 'react';
4+
5+
interface RegisterFormProps {
6+
onSwitchToLogin: () => void;
7+
}
8+
9+
const RegisterForm: React.FC<RegisterFormProps> = ({ onSwitchToLogin }) => {
10+
const [formData, setFormData] = useState({ email: '', password: '', confirmPassword: '' });
11+
12+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
13+
const { name, value } = e.target;
14+
setFormData((prevData) => ({ ...prevData, [name]: value }));
15+
};
16+
17+
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
18+
e.preventDefault();
19+
// Handle registration logic
20+
console.log('Register Form Data:', formData);
21+
};
22+
23+
return (
24+
<form onSubmit={handleSubmit} className="w-full max-w-sm">
25+
<input
26+
type="email"
27+
name="email"
28+
placeholder="Email"
29+
className="w-full p-2 mb-4 border border-gray-300 rounded"
30+
value={formData.email}
31+
onChange={handleChange}
32+
required
33+
aria-label="Email"
34+
/>
35+
<input
36+
type="password"
37+
name="password"
38+
placeholder="Password"
39+
className="w-full p-2 mb-4 border border-gray-300 rounded"
40+
value={formData.password}
41+
onChange={handleChange}
42+
required
43+
aria-label="Password"
44+
/>
45+
<input
46+
type="password"
47+
name="confirmPassword"
48+
placeholder="Choose a Password"
49+
className="w-full p-2 mb-4 border border-gray-300 rounded"
50+
value={formData.confirmPassword}
51+
onChange={handleChange}
52+
required
53+
aria-label="Confirm Password"
54+
/>
55+
<button type="submit" className="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600">
56+
Register
57+
</button>
58+
<button
59+
type="button"
60+
className="w-full mt-2 text-blue-500 underline"
61+
onClick={onSwitchToLogin}
62+
>
63+
Log In
64+
</button>
65+
</form>
66+
);
67+
};
68+
69+
export default RegisterForm;

components/ServiceAccountDetails.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const ServiceAccountDetails = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">Account Details</h2>
5+
<p>This is a placeholder for the service provider account details section.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default ServiceAccountDetails;
11+

components/ServiceCard.tsx

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// inspired by AdrianHajdin's CardCard component
2+
// https://github.com/adrianhajdin/project_next13_car_showcase/blob/main/components/CarCard.tsx
3+
// a card component to display each service,
4+
// which will be used in the services page to display services in a grid format
5+
6+
import React from 'react';
7+
8+
interface ServiceCardProps {
9+
name: string;
10+
description: string;
11+
price: number;
12+
providerName: string;
13+
}
14+
15+
const ServiceCard: React.FC<ServiceCardProps> = ({ name, description, price, providerName }) => {
16+
return (
17+
<div className="bg-white shadow-lg rounded-lg overflow-hidden m-4">
18+
<div className="p-4">
19+
<h2 className="text-xl font-bold mb-2">{name}</h2>
20+
<p className="text-gray-700 mb-4">{description}</p>
21+
<div className="text-right mb-4">
22+
<span className="text-blue-500 font-bold">${price}</span>
23+
</div>
24+
<p className="text-gray-600 italic">Provider: {providerName}</p>
25+
</div>
26+
</div>
27+
);
28+
};
29+
30+
export default ServiceCard;
31+

components/ServiceProfileOverview.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const ServiceProfileOverview = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">Profile Overview</h2>
5+
<p>This is a placeholder for the service provider profile overview section.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default ServiceProfileOverview;
11+

components/ServiceServices.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const ServiceServices = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">Search Services</h2>
5+
<p>This is a placeholder for the service providers to see their services.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default ServiceServices;
11+

components/ServiceViewBookings.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const ServiceViewBookings = () => {
2+
return (
3+
<div>
4+
<h2 className="text-2xl font-bold mb-4">View Bookings</h2>
5+
<p>This is a placeholder for the service provider to view their bookings.</p>
6+
</div>
7+
);
8+
};
9+
10+
export default ServiceViewBookings;
11+

jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports = {
77
moduleNameMapper: {
88
'^@app/(.*)$': '<rootDir>/app/$1',
99
'^@components/(.*)$': '<rootDir>/app/components/$1',
10-
'^@lib/(.*)$': '<rootDir>/app/lib/$1',
10+
'^@lib/(.*)$': '<rootDir>/lib/$1',
1111
'^@pages/(.*)$': '<rootDir>/pages/$1',
1212
'^@services/(.*)$': '<rootDir>/services/$1',
1313
'^@tests/(.*)$': '<rootDir>/tests/$1',
File renamed without changes.

app/lib/db.js renamed to lib/db.js

File renamed without changes.

lib/utils.ts

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { type ClassValue, clsx } from "clsx";
2+
import { twMerge } from "tailwind-merge";
3+
4+
export function cn(...inputs: ClassValue[]) {
5+
return twMerge(clsx(inputs));
6+
}
7+
8+
export const parseStringify = (value: any) => JSON.parse(JSON.stringify(value));
9+
10+
export const convertFileToUrl = (file: File) => URL.createObjectURL(file);
11+
12+
// FORMAT DATE TIME
13+
export const formatDateTime = (dateString: Date | string) => {
14+
const dateTimeOptions: Intl.DateTimeFormatOptions = {
15+
// weekday: "short", // abbreviated weekday name (e.g., 'Mon')
16+
month: "short", // abbreviated month name (e.g., 'Oct')
17+
day: "numeric", // numeric day of the month (e.g., '25')
18+
year: "numeric", // numeric year (e.g., '2023')
19+
hour: "numeric", // numeric hour (e.g., '8')
20+
minute: "numeric", // numeric minute (e.g., '30')
21+
hour12: true, // use 12-hour clock (true) or 24-hour clock (false)
22+
};
23+
24+
const dateDayOptions: Intl.DateTimeFormatOptions = {
25+
weekday: "short", // abbreviated weekday name (e.g., 'Mon')
26+
year: "numeric", // numeric year (e.g., '2023')
27+
month: "2-digit", // abbreviated month name (e.g., 'Oct')
28+
day: "2-digit", // numeric day of the month (e.g., '25')
29+
};
30+
31+
const dateOptions: Intl.DateTimeFormatOptions = {
32+
month: "short", // abbreviated month name (e.g., 'Oct')
33+
year: "numeric", // numeric year (e.g., '2023')
34+
day: "numeric", // numeric day of the month (e.g., '25')
35+
};
36+
37+
const timeOptions: Intl.DateTimeFormatOptions = {
38+
hour: "numeric", // numeric hour (e.g., '8')
39+
minute: "numeric", // numeric minute (e.g., '30')
40+
hour12: true, // use 12-hour clock (true) or 24-hour clock (false)
41+
};
42+
43+
const formattedDateTime: string = new Date(dateString).toLocaleString(
44+
"en-US",
45+
dateTimeOptions
46+
);
47+
48+
const formattedDateDay: string = new Date(dateString).toLocaleString(
49+
"en-US",
50+
dateDayOptions
51+
);
52+
53+
const formattedDate: string = new Date(dateString).toLocaleString(
54+
"en-US",
55+
dateOptions
56+
);
57+
58+
const formattedTime: string = new Date(dateString).toLocaleString(
59+
"en-US",
60+
timeOptions
61+
);
62+
63+
return {
64+
dateTime: formattedDateTime,
65+
dateDay: formattedDateDay,
66+
dateOnly: formattedDate,
67+
timeOnly: formattedTime,
68+
};
69+
};
70+
71+
export function encryptKey(passkey: string) {
72+
return btoa(passkey);
73+
}
74+
75+
export function decryptKey(passkey: string) {
76+
return atob(passkey);
77+
}

0 commit comments

Comments
 (0)