Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"multer": "^1.4.5-lts.1",
"ngx-cookie-service": "^16.1.0",
"rxjs": "~7.8.0",
"stripe": "^14.19.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"tslib": "^2.3.0",
Expand Down
58 changes: 58 additions & 0 deletions server/api/invoice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,62 @@ paths:
example: "Internal Server Error"
error:
type: string
/api/invoice/search:
get:
tags:
- Invoices
summary: Search invoices by customer's first and last name
description: API endpoint to search for invoices based on the customer's first and last name.
operationId: searchInvoicesByName
parameters:
- name: firstName
in: query
description: The first name of the customer
required: true
schema:
type: string
- name: lastName
in: query
description: The last name of the customer
required: true
schema:
type: string
responses:
'200':
description: Query successful
content:
application/json:
schema:
type: array
items:
type: object
properties:
userName:
type: string
lineItems:
type: array
items:
type: object
properties:
title:
type: string
price:
type: number
partsAmount:
type: number
laborAmount:
type: number
lineItemTotal:
type: number
total:
type: number
firstName:
type: string
lastName:
type: string
'404':
description: No invoices found
'500':
description: Internal server error


3 changes: 2 additions & 1 deletion server/models/invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const invoiceSchema = new Schema({
orderDate: { type: Date, default: new Date() },
firstName: { type: String },
lastName: { type: String },
status: { type: String, default: "Pending" }
status: { type: String, default: "Pending" },
payStatus: { type: String, default: "Unpaid" }
});

// Export Schema
Expand Down
50 changes: 48 additions & 2 deletions server/routes/invoiceApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,15 @@ router.get("/all", async (req, res) => {
// Update invoices
router.put("/:invoiceId", async (req, res) => {
const { invoiceId } = req.params;
const { status } = req.body;
const { status, payStatus } = req.body;

try {
const updatedInvoice = await Invoice.findByIdAndUpdate(invoiceId, { status }, { new: true });
const updatedInvoice = await Invoice.findByIdAndUpdate(
invoiceId,
{ $set: { status: status, payStatus: payStatus } },
{ new: true }
);

res.status(200).json({
status: "200",
message: "Invoice status updated successfully",
Expand All @@ -124,5 +129,46 @@ router.put("/:invoiceId", async (req, res) => {
}
});

// Search for Invoices by First Name and Last Name
router.get("/search", async (req, res) => {
const { firstName, lastName } = req.query;

// Check if both firstName and lastName are provided
if (!firstName || !lastName) {
return res.status(400).json({
status: "400",
message: "Both firstName and lastName are required"
});
}

try {
// Use the find method to search for invoices with the given firstName and lastName
const invoices = await Invoice.find({ firstName: firstName, lastName: lastName });

// Check if any invoices are found
if (!invoices.length) {
return res.status(404).json({
status: "404",
message: "No invoices found with the provided firstName and lastName"
});
}

// Return the found invoices
res.status(200).json({
status: "200",
message: "Query successful",
data: invoices
});

} catch (e) {
console.error(e);
res.status(500).json({
status: "500",
message: "Internal Server Error",
error: e.message
});
}
});


module.exports = router;
12 changes: 12 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { MyProfileComponent } from './my-profile/my-profile.component';
import { ServiceRepairComponent } from './service-repair/service-repair.component';
import { InvoiceSummaryComponent } from './invoice-summary/invoice-summary.component';
import { ViewInvoicesComponent } from './view-invoices/view-invoices.component';
import { InvoiceSearchComponent } from './invoice-search/invoice-search.component';
import { ServicesComponent } from './services/services.component';

// routes array with a path, component, and title for each route in the application (e.g. home, about, contact, etc.)
const routes: Routes = [
Expand Down Expand Up @@ -55,6 +57,11 @@ const routes: Routes = [
component: AboutComponent,
title: 'BCRS: About'
},
{
path: 'services',
component: ServicesComponent,
title: 'BCRS: Services'
},
{
path: 'security/signin',
component: SigninComponent,
Expand All @@ -65,6 +72,11 @@ const routes: Routes = [
component: FaqComponent,
title: 'BCRS: FAQ'
},
{
path: 'invoice-search',
component: InvoiceSearchComponent,
title: 'BCRS: Invoice Search',
},
{
path: 'employee-landing',
component: EmployeeLandingComponent,
Expand Down
6 changes: 5 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { MyProfileComponent } from './my-profile/my-profile.component';
import { ServiceRepairComponent } from './service-repair/service-repair.component';
import { InvoiceSummaryComponent } from './invoice-summary/invoice-summary.component';
import { ViewInvoicesComponent } from './view-invoices/view-invoices.component';
import { InvoiceSearchComponent } from './invoice-search/invoice-search.component';
import { ServicesComponent } from './services/services.component';

// @NgModule assign declarations and imports with bootstrap value
@NgModule({
Expand All @@ -55,7 +57,9 @@ import { ViewInvoicesComponent } from './view-invoices/view-invoices.component';
MyProfileComponent,
ServiceRepairComponent,
InvoiceSummaryComponent,
ViewInvoicesComponent
ViewInvoicesComponent,
InvoiceSearchComponent,
ServicesComponent
],
imports: [
BrowserModule,
Expand Down
39 changes: 39 additions & 0 deletions src/app/invoice-search/invoice-search.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.btn {
border-radius: 0 !important;
color:#f8b400 !important;
border-color: #f8b400 !important;
}

/* Override theme on hover */
.btn:hover {
background-color: #f8b400 !important;
color: black !important;
border-color: #f8b400 !important;
}

/* Remove border on input field and override color */
input {
border: none;
border-radius: 0;
background-color: transparent;
border-bottom: 2px solid #f8b400;
outline: none;
color: white;
}

/* Remove white background from input field when focused */
input:focus {
background-color: transparent !important;
outline: none !important;
box-shadow: none !important;
color: white;
}

/* Change placeholder color due to dark background */
input::placeholder {
color: white;
}

.input {
color: white;
}
61 changes: 61 additions & 0 deletions src/app/invoice-search/invoice-search.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<div class="container mt-5">
<div class="card bg-dark text-white text-center mb-5" style="width: auto; max-width: 400px; margin: auto;">
<h1 class="pt-4" style="color:#f8b400">Search for Invoice</h1>
<div class="row mb-4">
<div class="col p-5">
<form [formGroup]="invoiceSearchForm" (ngSubmit)="searchInvoices()">
<div class="form-group">
<input formControlName="firstName" type="text" class="form-control mb-3" placeholder="First Name">
<input formControlName="lastName" type="text" class="form-control mb-5" placeholder="Last Name">
<button class="btn btn-outline-secondary w-100" type="submit">Search</button>
<div *ngIf="errorMessage" class="mt-3" style="color: #f8b400">{{ errorMessage }}</div>
</div>
</form>
</div>
</div>
</div>


<!-- Results Table -->
<div class="d-flex justify-content-center">
<div class="card text-white bg-dark mb-3 w-100">
<div class="card-header text-center h1 gold">Search Results</div>
<div class="card-body">
<table class="table" style="background-color: #343a40 !important; color: white !important;">
<!-- Table headers -->
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Customer</th>
<th scope="col">Total</th>
<th scope="col">Order Date</th>
<th scope="col">Services</th>
<th scope="col">Pay Status</th>
<th scope="col">Status</th>
<th scope="col">Actions</th>
</tr>
</thead>
<!-- Invoice information -->
<tbody>
<tr *ngFor="let invoice of searchResults; index as i">
<th scope="row">{{ i + 1 }}</th>
<td>{{ invoice.firstName }} {{ invoice.lastName }}</td>
<td>{{ invoice.total | currency }}</td>
<td>{{ invoice.orderDate | date }}</td>
<td>
<div *ngFor="let item of invoice.lineItems">
{{ item.title }}: {{ item.price | currency }}
</div>
</td>
<td>{{ invoice.payStatus }}</td>
<td>{{ invoice.status }}</td>
<td>
<button class="btn">Pay</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
21 changes: 21 additions & 0 deletions src/app/invoice-search/invoice-search.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { InvoiceSearchComponent } from './invoice-search.component';

describe('InvoiceSearchComponent', () => {
let component: InvoiceSearchComponent;
let fixture: ComponentFixture<InvoiceSearchComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [InvoiceSearchComponent]
});
fixture = TestBed.createComponent(InvoiceSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading