+>(({ className, ...props }, ref) => (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+))
+TableCell.displayName = "TableCell"
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableCaption.displayName = "TableCaption"
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/src/main/frontend/src/index.css b/src/main/frontend/src/index.css
new file mode 100644
index 0000000..89b96fd
--- /dev/null
+++ b/src/main/frontend/src/index.css
@@ -0,0 +1,99 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+@layer components {
+ .floating-shadow {
+ box-shadow: 0 4px 4px 4px rgba(0, 0, 0, 0.25);
+ }
+
+ .nav-shadow {
+ box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
+ }
+}
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ --radius: 0.5rem;
+ }
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+ h1 {
+ @apply text-4xl font-bold;
+ }
+ #root {
+ @apply h-screen flex flex-col;
+ }
+}
diff --git a/src/main/frontend/src/lib/utils.ts b/src/main/frontend/src/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/src/main/frontend/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/src/main/frontend/src/main.tsx b/src/main/frontend/src/main.tsx
new file mode 100644
index 0000000..7616a0f
--- /dev/null
+++ b/src/main/frontend/src/main.tsx
@@ -0,0 +1,30 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App.tsx'
+import './index.css'
+import {createBrowserRouter, createRoutesFromElements, Navigate, Route, RouterProvider} from "react-router-dom";
+import Layout from "@/components/Layout.tsx";
+import Login from "@/routes/Login.tsx";
+import { AuthProvider } from "@/components/Auth.tsx";
+import ProtectedRoute from "@/components/ProtectedRoute.tsx";
+
+const router = createBrowserRouter(
+ createRoutesFromElements(
+ }>
+ } />
+ } />
+ HELLO OTHER ROUTE} />
+ }>
+ } />
+
+
+ )
+)
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+
+ ,
+)
diff --git a/src/main/frontend/src/routes/Login.tsx b/src/main/frontend/src/routes/Login.tsx
new file mode 100644
index 0000000..5e4f72f
--- /dev/null
+++ b/src/main/frontend/src/routes/Login.tsx
@@ -0,0 +1,110 @@
+import {Input} from "@/components/ui/input.tsx";
+import {Button} from "@/components/ui/button.tsx";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import {zodResolver} from "@hookform/resolvers/zod";
+import {useForm} from "react-hook-form";
+import {z} from "zod"
+import {useAuth} from "@/components/Auth.tsx";
+import {useNavigate} from "react-router-dom";
+
+const formSchema = z.object({
+ email: z.string().email({
+ message: "Invalid email address",
+ }),
+ password: z.string({
+ message: "Invalid password",
+ }),
+})
+
+function Login() {
+ const { signIn } = useAuth();
+ const navigate = useNavigate();
+
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ email: "",
+ password: "",
+ },
+ })
+
+ const onSubmit = (data: z.infer) => {
+ fetch("http://localhost:8080/login", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ }).then((response) => {
+ if (!response.ok) {
+ throw new Error("Password is incorrect")
+ }
+ }).then(() => {
+ console.log(data);
+ signIn(data);
+ navigate("/app-demo");
+ }).catch((error) => {
+ form.setError(
+ "password",
+ {
+ type: "value",
+ message: error.message,
+ }
+ )
+ })
+ }
+
+ return (
+
+
+ Welcome Back!
+ Login to access loans and more.
+
+
+
+
+
+ Commerce
+
+
+ );
+}
+
+export default Login;
\ No newline at end of file
diff --git a/src/main/frontend/src/vite-env.d.ts b/src/main/frontend/src/vite-env.d.ts
new file mode 100644
index 0000000..b4f2572
--- /dev/null
+++ b/src/main/frontend/src/vite-env.d.ts
@@ -0,0 +1,25 @@
+///
+
+interface Props {
+ children?: React.ReactNode;
+}
+
+interface AuthContextType {
+ user: Login | undefined;
+ signIn: (data: any) => void;
+ signOut: () => void;
+}
+
+interface Account {
+ id: number;
+ userType: number;
+ username: string;
+ password: string;
+ email: string;
+ phoneNumber: string;
+}
+
+interface Login {
+ email: string;
+ password: string;
+}
\ No newline at end of file
diff --git a/src/main/frontend/tailwind.config.js b/src/main/frontend/tailwind.config.js
new file mode 100644
index 0000000..07e31ec
--- /dev/null
+++ b/src/main/frontend/tailwind.config.js
@@ -0,0 +1,63 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ darkMode: ["class"],
+ content: [
+ "./src/**/*.{ts,tsx}",
+ ],
+ theme: {
+ extend: {
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)'
+ },
+ colors: {
+ commerce: {
+ green: '#006649',
+ },
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))'
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))'
+ },
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))'
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))'
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))'
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))'
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))'
+ },
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ chart: {
+ '1': 'hsl(var(--chart-1))',
+ '2': 'hsl(var(--chart-2))',
+ '3': 'hsl(var(--chart-3))',
+ '4': 'hsl(var(--chart-4))',
+ '5': 'hsl(var(--chart-5))'
+ }
+ }
+ }
+ },
+ plugins: [require("tailwindcss-animate")],
+}
+
diff --git a/src/main/frontend/tsconfig.app.json b/src/main/frontend/tsconfig.app.json
new file mode 100644
index 0000000..2e88182
--- /dev/null
+++ b/src/main/frontend/tsconfig.app.json
@@ -0,0 +1,32 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+
+ /* Paths */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": [
+ "./src/*"
+ ]
+ }
+ },
+ "include": ["src"]
+}
diff --git a/src/main/frontend/tsconfig.json b/src/main/frontend/tsconfig.json
new file mode 100644
index 0000000..fec8c8e
--- /dev/null
+++ b/src/main/frontend/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ],
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/src/main/frontend/tsconfig.node.json b/src/main/frontend/tsconfig.node.json
new file mode 100644
index 0000000..0d3d714
--- /dev/null
+++ b/src/main/frontend/tsconfig.node.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/src/main/frontend/vite.config.ts b/src/main/frontend/vite.config.ts
new file mode 100644
index 0000000..908d5ca
--- /dev/null
+++ b/src/main/frontend/vite.config.ts
@@ -0,0 +1,15 @@
+import path from "path"
+import react from "@vitejs/plugin-react-swc"
+import { defineConfig } from "vite"
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 3000,
+ },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
+ },
+})
diff --git a/src/main/java/com/avengers/example/config/GlobalCorsConfig.java b/src/main/java/com/avengers/example/config/GlobalCorsConfig.java
new file mode 100644
index 0000000..8e69d0b
--- /dev/null
+++ b/src/main/java/com/avengers/example/config/GlobalCorsConfig.java
@@ -0,0 +1,21 @@
+package com.avengers.example.config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class GlobalCorsConfig {
+
+ @Bean
+ public WebMvcConfigurer corsConfigurer() {
+ return new WebMvcConfigurer() {
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**") //This applies the CORS configuration to all endpoints in the application
+ .allowedOrigins("http://localhost:3000")
+ .allowedMethods("GET", "POST", "PUT", "DELETE");
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/avengers/example/controller/AccountController.java b/src/main/java/com/avengers/example/controller/AccountController.java
index a4a2a24..19a59f3 100644
--- a/src/main/java/com/avengers/example/controller/AccountController.java
+++ b/src/main/java/com/avengers/example/controller/AccountController.java
@@ -5,10 +5,7 @@
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
@AllArgsConstructor
@RestController
@@ -16,12 +13,21 @@ public class AccountController
{
private final AccountService accountService;
+ /**
+ * Creates a new account object and saves it to the database.
+ *
+ * @param account the account instance to be added to the database.
+ * @return The response from the server.
+ */
@PostMapping("/account")
public ResponseEntity> save(@RequestBody Account account)
{
return new ResponseEntity<>(accountService.create(account), HttpStatus.CREATED);
}
+ /**
+ * @return A response object containing all accounts from the database.
+ */
@GetMapping("/accounts")
public ResponseEntity> findAll()
{
diff --git a/src/main/java/com/avengers/example/controller/LoanController.java b/src/main/java/com/avengers/example/controller/LoanController.java
new file mode 100644
index 0000000..9487088
--- /dev/null
+++ b/src/main/java/com/avengers/example/controller/LoanController.java
@@ -0,0 +1,39 @@
+package com.avengers.example.controller;
+
+import com.avengers.example.domain.Loan;
+import com.avengers.example.service.LoanService;
+import lombok.AllArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@AllArgsConstructor
+@RestController
+public class LoanController
+{
+ private final LoanService loanService;
+
+ /**
+ * Creates a new loan object and saves it to the database.
+ *
+ * @param loan the loan instance to be added to the database.
+ * @return The response from the server.
+ */
+ @PostMapping("/loan")
+ public ResponseEntity> save(@RequestBody Loan loan)
+ {
+ return new ResponseEntity<>(loanService.create(loan), HttpStatus.CREATED);
+ }
+
+ /**
+ * @return A response object containing all loans from the database.
+ */
+ @GetMapping("/loans")
+ public ResponseEntity> findAll()
+ {
+ return new ResponseEntity<>(loanService.findAll(), HttpStatus.OK);
+ }
+}
diff --git a/src/main/java/com/avengers/example/controller/LoginController.java b/src/main/java/com/avengers/example/controller/LoginController.java
new file mode 100644
index 0000000..b621a32
--- /dev/null
+++ b/src/main/java/com/avengers/example/controller/LoginController.java
@@ -0,0 +1,25 @@
+package com.avengers.example.controller;
+
+import com.avengers.example.domain.Login;
+import com.avengers.example.service.LoginService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class LoginController {
+ private final LoginService loginService;
+
+ public LoginController(LoginService loginService) {
+ this.loginService = loginService;
+ }
+
+ @PostMapping("/login")
+ public ResponseEntity> login(@RequestBody Login login) {
+ boolean isValid = loginService.isLoginValid(login.email(), login.password());
+ return new ResponseEntity<>(isValid ? HttpStatus.OK : HttpStatus.UNAUTHORIZED);
+ }
+}
diff --git a/src/main/java/com/avengers/example/domain/Account.java b/src/main/java/com/avengers/example/domain/Account.java
index aeec6cc..771f865 100644
--- a/src/main/java/com/avengers/example/domain/Account.java
+++ b/src/main/java/com/avengers/example/domain/Account.java
@@ -1,6 +1,8 @@
package com.avengers.example.domain;
import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;
import lombok.Getter;
@@ -10,34 +12,41 @@
@Entity
@Data
@Getter
-
public class Account
{
@Id
- private long acctId;
- private int userType;
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id; // primary key for the account table (renamed for compatibility).
+ private boolean isAdmin;
private String username;
private String password;
private String email;
private String phoneNumber;
+ /**
+ * No argument constructor for account object.
+ */
public Account()
{
}
- public Account(long acctId, int userType, String username, String password, String email, String phoneNumber)
+ public Account(boolean isAdmin, String username, String password, String email, String phoneNumber)
{
- this.acctId = acctId;
- this.userType = userType;
+ this.isAdmin = isAdmin;
this.username = username;
this.password = password;
this.email = email;
this.phoneNumber = phoneNumber;
}
- public void setUserType(int userType)
+ /**
+ * Sets the type of user account.
+ *
+ * @param isAdmin (boolean) indicator of administrator account.
+ */
+ public void setIsAdmin(boolean isAdmin)
{
- this.userType = userType;
+ this.isAdmin = isAdmin;
}
public void setUsername(String username)
@@ -49,7 +58,11 @@ public void setUsername(String username)
this.username = username;
}
-
+ /**
+ * Sets the password of the account.
+ *
+ * @param password (String) account password.
+ */
public void setPassword(String password)
{
if (null == password || password == "" || password == " ")
@@ -59,7 +72,11 @@ public void setPassword(String password)
this.password = password;
}
-
+ /**
+ * Sets the email on the account.
+ *
+ * @param email (String) account email.
+ */
public void setEmail(String email)
{
if (null == email || email == "" || email == " ")
@@ -69,7 +86,11 @@ public void setEmail(String email)
this.email = email;
}
-
+ /**
+ * Sets the phone number of the account.
+ *
+ * @param phoneNumber (String) account phone number.
+ */
public void setPhoneNumber(String phoneNumber)
{
if (null == phoneNumber || phoneNumber == "" || phoneNumber == " ")
@@ -78,5 +99,4 @@ public void setPhoneNumber(String phoneNumber)
}
this.phoneNumber = phoneNumber;
}
-
}
diff --git a/src/main/java/com/avengers/example/domain/Loan.java b/src/main/java/com/avengers/example/domain/Loan.java
index b759dc0..b3d5e8e 100644
--- a/src/main/java/com/avengers/example/domain/Loan.java
+++ b/src/main/java/com/avengers/example/domain/Loan.java
@@ -1,6 +1,8 @@
package com.avengers.example.domain;
import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;
import lombok.Getter;
@@ -13,7 +15,8 @@
public class Loan
{
@Id
- private Long loanId;
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id; // primary key for the loan table (renamed for compatibility).
private double originAmount;
private long interestRate;
@@ -21,12 +24,9 @@ public Loan()
{
}
- public Loan(Long loanId, double originAmount, long interestRate)
+ public Loan(double originAmount, long interestRate)
{
- this.loanId = loanId;
this.originAmount = originAmount;
this.interestRate = interestRate;
}
-
-
}
diff --git a/src/main/java/com/avengers/example/domain/Login.java b/src/main/java/com/avengers/example/domain/Login.java
new file mode 100644
index 0000000..799f9b0
--- /dev/null
+++ b/src/main/java/com/avengers/example/domain/Login.java
@@ -0,0 +1,12 @@
+package com.avengers.example.domain;
+
+import java.util.Objects;
+
+public record Login(String email, String password)
+{
+ public Login
+ {
+ Objects.requireNonNull(email, "Username cannot be null.");
+ Objects.requireNonNull(password, "Password cannot be null.");
+ }
+}
diff --git a/src/main/java/com/avengers/example/repository/AccountRepository.java b/src/main/java/com/avengers/example/repository/AccountRepository.java
index efcf511..96c9a66 100644
--- a/src/main/java/com/avengers/example/repository/AccountRepository.java
+++ b/src/main/java/com/avengers/example/repository/AccountRepository.java
@@ -7,4 +7,5 @@
@Repository
public interface AccountRepository extends JpaRepository
{
+ Account findByEmailAndPassword(String email, String password);
}
diff --git a/src/main/java/com/avengers/example/repository/LoanRepository.java b/src/main/java/com/avengers/example/repository/LoanRepository.java
new file mode 100644
index 0000000..673a3ef
--- /dev/null
+++ b/src/main/java/com/avengers/example/repository/LoanRepository.java
@@ -0,0 +1,8 @@
+package com.avengers.example.repository;
+
+import com.avengers.example.domain.Loan;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface LoanRepository extends JpaRepository
+{
+}
diff --git a/src/main/java/com/avengers/example/service/AccountService.java b/src/main/java/com/avengers/example/service/AccountService.java
index b316c17..ee93b9a 100644
--- a/src/main/java/com/avengers/example/service/AccountService.java
+++ b/src/main/java/com/avengers/example/service/AccountService.java
@@ -24,4 +24,9 @@ public List findAll()
{
return accountRepository.findAll();
}
+
+ public Account findByEmailAndPassword(String email, String password)
+ {
+ return accountRepository.findByEmailAndPassword(email, password);
+ }
}
diff --git a/src/main/java/com/avengers/example/service/LoanService.java b/src/main/java/com/avengers/example/service/LoanService.java
new file mode 100644
index 0000000..dd33070
--- /dev/null
+++ b/src/main/java/com/avengers/example/service/LoanService.java
@@ -0,0 +1,27 @@
+package com.avengers.example.service;
+
+import com.avengers.example.domain.Loan;
+import com.avengers.example.repository.LoanRepository;
+import jakarta.transaction.Transactional;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@AllArgsConstructor
+@Service
+public class LoanService
+{
+ private final LoanRepository loanRepository;
+
+ @Transactional
+ public Loan create(Loan loan)
+ {
+ return loanRepository.save(loan);
+ }
+
+ public List findAll()
+ {
+ return loanRepository.findAll();
+ }
+}
diff --git a/src/main/java/com/avengers/example/service/LoginService.java b/src/main/java/com/avengers/example/service/LoginService.java
new file mode 100644
index 0000000..546ca63
--- /dev/null
+++ b/src/main/java/com/avengers/example/service/LoginService.java
@@ -0,0 +1,20 @@
+package com.avengers.example.service;
+
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+
+@Service
+public class LoginService {
+
+ private final AccountService accountService;
+
+ public LoginService(AccountService accountService) {
+ this.accountService = accountService;
+ }
+
+ public boolean isLoginValid(String email, String password) {
+ return !Objects.isNull(accountService.findByEmailAndPassword(email, password));
+ }
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index b810432..108c3fa 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -3,7 +3,7 @@ server:
spring:
application:
- name: book-service
+ name: Loan-repayment-service
h2:
console:
diff --git a/src/test/java/com/avengers/example/ApplicationTests.java b/src/test/java/com/avengers/example/ApplicationTests.java
index 4d22c65..cb122aa 100644
--- a/src/test/java/com/avengers/example/ApplicationTests.java
+++ b/src/test/java/com/avengers/example/ApplicationTests.java
@@ -6,9 +6,8 @@
@SpringBootTest
class ApplicationTests
{
-
- @Test
- void contextLoads() {
- }
-
+ @Test
+ void contextLoads()
+ {
+ }
}
diff --git a/src/test/java/com/avengers/example/domain/AccountTests.java b/src/test/java/com/avengers/example/domain/AccountTests.java
new file mode 100644
index 0000000..f4ee614
--- /dev/null
+++ b/src/test/java/com/avengers/example/domain/AccountTests.java
@@ -0,0 +1,97 @@
+package com.avengers.example.domain;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SpringBootTest
+public class AccountTests
+{
+ private Account testAccount;
+ private long acctId = 1L;
+ private boolean isAdmin = false;
+ private String username = "user";
+ private String password = "pass";
+ private String email = "email@email.com";
+ private String phonenumber = "(111)111-1111";
+
+ @BeforeEach
+ public void setup()
+ {
+ testAccount = new Account(isAdmin, username, password, email, phonenumber);
+ }
+
+ @Test
+ public void testNoArgumentConstructor()
+ {
+ Account account = new Account();
+ assert account != null;
+ }
+
+ @Test
+ public void testAllArgumentConstructor()
+ {
+ assert testAccount != null;
+ }
+
+ @Test
+ public void testSetIsAdmin()
+ {
+ testAccount.setIsAdmin(false);
+ assert !testAccount.getIsAdmin();
+ }
+
+ @Test
+ public void testSetUserName()
+ {
+ testAccount.setUsername("test");
+ assert testAccount.getUsername() == "test";
+ }
+
+ @Test
+ public void testSetUserName_NullUserName()
+ {
+ assertThrows(IllegalArgumentException.class, () -> testAccount.setUsername(null));
+ }
+
+ @Test
+ public void testSetPassword()
+ {
+ testAccount.setPassword("test");
+ assert testAccount.getPassword() == "test";
+ }
+
+ @Test
+ public void testSetPassword_NullPassword()
+ {
+ assertThrows(IllegalArgumentException.class, () -> testAccount.setPassword(null));
+ }
+
+ @Test
+ public void testSetPhoneNumber()
+ {
+ testAccount.setPhoneNumber("test");
+ assert testAccount.getPhoneNumber() == "test";
+ }
+
+ @Test
+ public void testSetPhoneNumber_NullPhoneNumber()
+ {
+ assertThrows(IllegalArgumentException.class, () -> testAccount.setPhoneNumber(null));
+ }
+
+ @Test
+ public void testSetEmail()
+ {
+ testAccount.setEmail("test");
+ assert testAccount.getEmail() == "test";
+ }
+
+ @Test
+ public void testSetEmail_NullEmail()
+ {
+ assertThrows(IllegalArgumentException.class, () -> testAccount.setEmail(null));
+ }
+}
|