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
15 changes: 14 additions & 1 deletion backend/src/grant/grant.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, Get, Param, Put, Body } from '@nestjs/common';
import { Controller, Get, Param, Put, Body, Patch } from '@nestjs/common';
import { GrantService } from './grant.service';

@Controller('grant')
Expand All @@ -10,6 +10,8 @@ export class GrantController {
return await this.grantService.getAllGrants();
}

// the @Param looks for arguments, in the decorator above the function definition, and
// then uses it as a parameter
@Get(':id')
async getGrantById(@Param('id') GrantId: string) {
return await this.grantService.getGrantById(parseInt(GrantId, 10));
Expand All @@ -29,4 +31,15 @@ export class GrantController {
return await this.grantService.unarchiveGrants(grantIds)
}

@Put('save/status')
async saveStatus(
@Body('status') status: string
) {
await this.grantService.updateGrant(1, 'status', status)
return { message: 'Status has been updated' };
}




}
4 changes: 3 additions & 1 deletion backend/src/grant/grant.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ export interface Grant {
attached_resources: string[];
comments: string[];
isArchived : boolean;
}
}

// TODO: [JAN-13} Switch deadline to Proper "Date Time"
35 changes: 35 additions & 0 deletions backend/src/grant/grant.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class GrantService {

// function to retrieve all grants in our database
async getAllGrants(): Promise<Grant[]> {
// loads in the environment variable for the table now
const params = {
TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE',
};
Expand Down Expand Up @@ -79,4 +80,38 @@ export class GrantService {
};
return successfulUpdates;
}

/**
* Given primary key, attribute name, and the content to update, queries database to update
* that info. Returns true if operation was successful. Assumes inputs are valid.
* @param grantId
* @param attributeName
* @param newValue
*/
async updateGrant(grantId: number, attributeName: string, newValue: string): Promise<void> {
const params = {
TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE',
Key: {
grantId: grantId
},
UpdateExpression: `set #s = :newValue`,
ExpressionAttributeNames: {
'#s': attributeName,
},
ExpressionAttributeValues: {
":newValue": newValue,
},
ReturnValues: "UPDATED_NEW",
}
console.log(params);

try {
const result = await this.dynamoDb.update(params).promise();
console.log(result);
} catch(err) {
console.log(err);
throw new Error(`Failed to update Grant ${grantId} attribute
${attributeName} with ${newValue}`);
}
}
}
5 changes: 4 additions & 1 deletion backend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ async function bootstrap() {
region: process.env.AWS_REGION,
accessKeyId: process.env.OPEN_HATCH,
secretAccessKey: process.env.CLOSED_HATCH
});
});

const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3001);
}
dotenv.config();
bootstrap();


9 changes: 5 additions & 4 deletions backend/src/notifications/notification.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';
import { NotificationController } from './notification.controller';
import { NotificationService } from './notifcation.service';


@Module({
providers: [NotificationService],
controllers: [NotificationController],
exports: [NotificationService],
providers: [NotificationService], // providers perform business logic
controllers: [NotificationController], // controllers directly take in http requests
// and are the starting point for anything happening
exports: [NotificationService], // by putting it under exports, this service is available
// to other modules outside of this
})
export class NotificationsModule {}
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^2.1.8"
}
}
}
38 changes: 1 addition & 37 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
@@ -1,45 +1,9 @@
#root {
max-width: 1280px;
max-width: 100%;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}

.app-container {
display: flex;
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/grant-info/components/GrantAttributes.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import React from 'react';
import React, { useContext} from 'react';
import './styles/GrantAttributes.css';
import {StatusContext} from './StatusContext.tsx';
interface GrantAttributesProps {
isEditing: boolean;
}

export const GrantAttributes: React.FC<GrantAttributesProps> = ({isEditing}) => {

// placeholder for now before reworking, will remove redundant useState()
const { curStatus, setCurStatus } = useContext(StatusContext);

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setCurStatus(event.target.value);
};

return (
<div className="grant-attributes">
<div className="attribute-row">
<div className="attribute-label">Status</div>
{isEditing ? (
<input
type="text"
type="text" /*This is also a place holder for updating the status*/
className="attribute-value"
value={curStatus}
onChange={handleChange}
/>
) : (
<span className="attribute-value"></span>
Expand Down
63 changes: 56 additions & 7 deletions frontend/src/grant-info/components/GrantItem.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, {useState} from 'react';
import React, {useState, useEffect } from 'react';
import './styles/GrantItem.css';
import { GrantAttributes } from './GrantAttributes';
import GrantDetails from './GrantDetails';
import {StatusContext} from './StatusContext';

// TODO: [JAN-14] Make uneditable field editable (ex: Description, Application Reqs, Additional Notes)
interface GrantItemProps {
grantName: string;
applicationDate: string;
Expand All @@ -11,24 +13,69 @@ interface GrantItemProps {
restrictionStatus: string;
}
const GrantItem: React.FC<GrantItemProps> = (props) => {
// will change back to const later once below is achieved.
const { grantName, applicationDate, generalStatus, amount, restrictionStatus } = props;

// NOTE: For now, generalStatus will be changed to demonstrate fetching from the database
// Once Front-End display matches database schema, the query will actually be made in
// GrantList.tsx. Furthermore, there is no established way for the front-end to know
// which grant will be edited. Will default to GrantId: 1 for now as well.
const [isExpanded, setIsExpanded] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [curStatus, setCurStatus] = useState(generalStatus);

// NOTE: ^^this is also a placeholder for generalStatus

// fetching initial status
useEffect(() => {
const fetchStatus = async () => {
try {
const rawResponse = await fetch('http://localhost:3001/grant/1');
if (rawResponse.ok) {
const data = await rawResponse.json();
setCurStatus(data.status);
} else {
console.error('Failed to fetch grant status:', rawResponse.statusText);
}
} catch (err) {
console.error('Error fetching status:', err);
}
};
fetchStatus();
}, []);

const toggleExpand = () => {
setIsExpanded(!isExpanded);
};
const toggleEdit = () => setIsEditing((prev) => !prev);
// when toggleEdit gets saved, then updates the backend to update itself with whatever
// is shown in the front-end

return (
const toggleEdit = async () => {
if(isEditing) { // if you are saving
try {
const response = await fetch('http://localhost:3001/grant/save/status', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({status: curStatus}),
});
const result = await response.json();
console.log(result);
} catch(err) {
console.error('Error saving data:', err);
}
// holding result for now
}
setIsEditing(!isEditing);
};

return (
// class name with either be grant-item or grant-item-expanded
<div className='grant-item-wrapper'>
<ul className={`grant-summary ${isExpanded ? 'expanded' : ''}`} onClick={toggleExpand}>
<li className="grant-name">{grantName}</li>
<li className="application-date">{applicationDate}</li>
<li className="status">{generalStatus}</li>
<li className="status">{curStatus}</li> {/*This is replacing generalStatus for now*/}
<li className="amount">${amount}</li>
<li className="restriction-status">{restrictionStatus}</li>
</ul>
Expand All @@ -37,8 +84,10 @@ const GrantItem: React.FC<GrantItemProps> = (props) => {
<div className="grant-description">
<h2>Community Development Initiative Grant</h2>
<div className = 'grant-content'>
<GrantAttributes isEditing={isEditing} />
<GrantDetails/>
<StatusContext.Provider value={{curStatus, setCurStatus}}>
<GrantAttributes isEditing={isEditing} />
<GrantDetails/>
</StatusContext.Provider>
</div>
<div className="bottom-buttons">
<button className="done-button" onClick={toggleEdit}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/grant-info/components/GrantList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const GrantList: React.FC = () => {
- totalPages is calculated
*/}
<PaginationRoot defaultPage={1} count={totalPages}>
{/*
{/*
Paging Controls:
- Prev / Next triggers
- Individual page items
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/grant-info/components/StatusContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// will later be renamed to "GrantContext" when front/backend schemas are aligned
import { createContext } from "react";

// Define the shape of the context
interface StatusContextType {
curStatus: string;
setCurStatus: React.Dispatch<React.SetStateAction<string>>;
}


export const StatusContext = createContext<StatusContextType>({
curStatus: "",
setCurStatus: () => {}, // No-op function as default
});
1 change: 1 addition & 0 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ button {
cursor: pointer;
transition: border-color 0.25s;
}

button:hover {
border-color: #646cff;
}
Expand Down
Loading