Skip to content

Commit

Permalink
feat(#5): integrate frontend and backend for driver registration, add…
Browse files Browse the repository at this point in the history
… backend fixes, and enable Docker hot-reload
  • Loading branch information
chriscoderdr committed Oct 30, 2024
1 parent cdc5a84 commit a58e1a7
Show file tree
Hide file tree
Showing 18 changed files with 156 additions and 49 deletions.
4 changes: 3 additions & 1 deletion apps/api/.dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/node_modules
node_modules
dist
.git
2 changes: 1 addition & 1 deletion apps/api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ COPY . .

EXPOSE 3000

CMD yarn install && yarn build && node dist/start.js
CMD yarn install && yarn dev
1 change: 0 additions & 1 deletion apps/api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ services:
DATABASE_URL: "postgres://morro_user:morro_pass@db:5432/morrotaxi"
volumes:
- .:/app
- /app/node_modules

db:
image: postgres:15
Expand Down
6 changes: 3 additions & 3 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "api",
"version": "1.0.0",
"main": "index.js",
"main": "dist/src/start.js",
"scripts": {
"test": "mocha -r ts-node/register test/**/*.test.ts",
"build": "tsc",
"start": "node dist/start.js",
"dev": "concurrently \"yarn build\" \"nodemon dist/start.js\""
"start": "node --es-module-specifier-resolution=node",
"dev": "concurrently \"yarn build\" \"nodemon --es-module-specifier-resolution=node\""
},
"keywords": [],
"author": "",
Expand Down
16 changes: 13 additions & 3 deletions apps/api/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import sequelize from './config/database';
import driverRoutes from './routes/driver-routes';
import driverRoutes from './routes';

const app = new Koa();

sequelize.sync().then(() => console.log("Connected to PostgreSQL"));
// sequelize.sync().then(() => console.log("Connected to PostgreSQL"));

app.use(bodyParser());

// Temporary test route
app.use(async (ctx, next) => {
if (ctx.path === '/') {
ctx.body = 'Server is up!';
} else {
console.log('Request received:', ctx.method, ctx.path);
await next();
}
});

app.use(driverRoutes.routes()).use(driverRoutes.allowedMethods());

export default app;
4 changes: 0 additions & 4 deletions apps/api/src/models/Driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ interface DriverCreationAttributes extends Optional<DriverAttributes, 'id'> {}


class Driver extends Model<DriverAttributes, DriverCreationAttributes> {




public async comparePassword(password: string): Promise<boolean> {
return bcrypt.compare(password, this.dataValues.password);
}
Expand Down
7 changes: 4 additions & 3 deletions apps/api/src/routes/driver-routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Router from "@koa/router";
import { registerDriver } from "../controllers/driver-controller";
import Router from '@koa/router';
import { registerDriver } from '../controllers/driver-controller';

const router = new Router();
router.post("/drivers/register", registerDriver);

router.post('/register', registerDriver);

export default router;
8 changes: 8 additions & 0 deletions apps/api/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Router from '@koa/router';
import driverRoutes from './driver-routes';

const router = new Router();

router.use('/drivers', driverRoutes.routes(), driverRoutes.allowedMethods());

export default router;
10 changes: 10 additions & 0 deletions apps/api/src/start.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import app from './app';

const PORT = process.env.PORT || 3000;
// Temporary test route
app.use(async (ctx, next) => {
if (ctx.path === '/') {
ctx.body = 'Server is up!';
} else {
console.log('Request received:', ctx.method, ctx.path);
await next();
}
});

app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
5 changes: 3 additions & 2 deletions apps/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ESNext",
"target": "ES6",
"module": "CommonJS",
"rootDir": ".",
"outDir": "dist",
Expand All @@ -9,7 +9,8 @@
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"removeComments": true
},
"include": ["src", "test"],
"exclude": ["node_modules", "dist"]
Expand Down
1 change: 1 addition & 0 deletions apps/driver-app/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
EXPO_PUBLIC_MORRO_API_BASE_URL=http://192.168.68.106:3000
53 changes: 53 additions & 0 deletions apps/driver-app/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ApiResponse, DriverData } from './models';

const API_BASE_URL = process.env.EXPO_PUBLIC_MORRO_API_BASE_URL;

export const registerDriver = async (driverData: DriverData): Promise<ApiResponse> => {
try {
console.log('TEST', API_BASE_URL)
const response = await fetch(`${API_BASE_URL}/drivers/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(driverData),
});
console.log('TEST2', await response.text())
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Registration failed');
}

return response.json();
} catch (error) {
if (error instanceof TypeError && error.message === 'Network request failed') {
console.log(error)
throw new Error('Network error: Please check your internet connection and try again.');
}
throw error;
}
};

export const loginDriver = async (driverData: DriverData): Promise<ApiResponse> => {
try {
const response = await fetch(`${API_BASE_URL}/drivers/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(driverData),
});

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Login failed');
}

return response.json();
} catch (error) {
if (error instanceof TypeError && error.message === 'Network request failed') {
throw new Error('Network error: Please check your internet connection and try again.');
}
throw error;
}
};
9 changes: 9 additions & 0 deletions apps/driver-app/api/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface DriverData {
email: string;
password: string;
}

export interface ApiResponse {
message: string;
}

7 changes: 6 additions & 1 deletion apps/driver-app/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import {
useFonts,
} from "@expo-google-fonts/inter";
import { Poppins_700Bold } from "@expo-google-fonts/poppins";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { View } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import SignUp from "./signup";

export default function TabLayout() {
const queryClient = new QueryClient();

export default function HomeLayout() {
const [fontsLoaded] = useFonts({
Inter_400Regular,
Inter_700Bold,
Expand All @@ -25,7 +28,9 @@ export default function TabLayout() {
return (
<GestureHandlerRootView>
<KeyboardDismiss>
<QueryClientProvider client={queryClient}>
<SignUp />
</QueryClientProvider>
</KeyboardDismiss>
</GestureHandlerRootView>
);
Expand Down
45 changes: 15 additions & 30 deletions apps/driver-app/components/sign-up-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from "react";
import { Keyboard, Text, View } from "react-native";
import { useRegisterDriver } from "../../hooks/api/use-register-driver";
import Checkbox from "../checkbox";
import InputTextField from "../input-text-field";
import RoundedButton from "../rounded-button";
Expand All @@ -12,12 +13,10 @@ const SignUpForm: React.FC = () => {
const [isChecked, setIsChecked] = useState(false);

const [emailError, setEmailError] = useState<string | undefined>(undefined);
const [passwordError, setPasswordError] = useState<string | undefined>(
undefined
);
const [confirmPasswordError, setConfirmPasswordError] = useState<
string | undefined
>(undefined);
const [passwordError, setPasswordError] = useState<string | undefined>(undefined);
const [confirmPasswordError, setConfirmPasswordError] = useState<string | undefined>(undefined);

const { mutate, status, isError, error, isSuccess } = useRegisterDriver();

const isValidEmail = (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
Expand Down Expand Up @@ -48,41 +47,25 @@ const SignUpForm: React.FC = () => {
setConfirmPasswordError(undefined);
}

if (isValid) {
console.log("Sign Up:", { email, password, confirmPassword });
if (isValid && isChecked) {
mutate({ email, password });
}
};

const handleEmailChange = (text: string) => {
setEmail(text);
if (!isValidEmail(text)) {
setEmailError("Please enter a valid email address");
} else {
setEmailError(undefined);
}
setEmailError(!isValidEmail(text) ? "Please enter a valid email address" : undefined);
};

const handlePasswordChange = (text: string) => {
setPassword(text);
if (text.length < 8) {
setPasswordError("Password must be at least 8 characters");
} else {
setPasswordError(undefined);
}
if (confirmPassword && text !== confirmPassword) {
setConfirmPasswordError("Passwords do not match");
} else if (confirmPassword) {
setConfirmPasswordError(undefined);
}
setPasswordError(text.length < 8 ? "Password must be at least 8 characters" : undefined);
setConfirmPasswordError(confirmPassword && text !== confirmPassword ? "Passwords do not match" : undefined);
};

const handleConfirmPasswordChange = (text: string) => {
setConfirmPassword(text);
if (text !== password) {
setConfirmPasswordError("Passwords do not match");
} else {
setConfirmPasswordError(undefined);
}
setConfirmPasswordError(text !== password ? "Passwords do not match" : undefined);
};

const handleSubmitEditing = () => {
Expand Down Expand Up @@ -145,16 +128,18 @@ const SignUpForm: React.FC = () => {
checked={isChecked}
onChange={(checked) => setIsChecked(checked)}
label="I accept the terms and privacy policy"
linkUrl="https://raw.githubusercontent.com/ArthurGareginyan/privacy-policy-template/refs/heads/master/privacy-policy.txt"
linkUrl="https://example.com/terms"
/>
</View>

<View style={styles.buttonContainer}>
<RoundedButton
text="Sign Up"
text={status === 'loading' ? "Signing Up..." : "Sign Up"}
onPress={handleSignUp}
testID="signup-button"
/>
{isError && <Text style={{ color: 'red' }}>{(error as Error).message}</Text>}
{isSuccess && <Text style={{ color: 'green' }}>Registration successful!</Text>}
</View>
</View>
);
Expand Down
13 changes: 13 additions & 0 deletions apps/driver-app/hooks/api/use-register-driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useMutation, UseMutationResult } from "@tanstack/react-query";
import { registerDriver } from "../../api/index";
import { ApiResponse, DriverData } from "../../api/models";

export const useRegisterDriver = (): UseMutationResult<
ApiResponse,
Error,
DriverData
> => {
return useMutation<ApiResponse, Error, DriverData>({
mutationFn: registerDriver,
});
};
2 changes: 2 additions & 0 deletions apps/driver-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "driver-app",
"main": "expo-router/entry",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
Expand All @@ -22,6 +23,7 @@
"@expo-google-fonts/poppins": "^0.2.3",
"@expo/vector-icons": "^14.0.4",
"@react-navigation/native": "^6.0.2",
"@tanstack/react-query": "^5.59.16",
"expo": "~51.0.28",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.10",
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2942,6 +2942,18 @@
dependencies:
tslib "^2.4.0"

"@tanstack/query-core@5.59.16":
version "5.59.16"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.59.16.tgz#aa4616e8a9c12afeef4cfbf3ed0f55f404d66e67"
integrity sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==

"@tanstack/react-query@^5.59.16":
version "5.59.16"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.59.16.tgz#1e701c6e6681965c04aa426df9da54b8edc6db1b"
integrity sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==
dependencies:
"@tanstack/query-core" "5.59.16"

"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz"
Expand Down

0 comments on commit a58e1a7

Please sign in to comment.