Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 725946d
Author: Ryaken Nakamoto <[email protected]>
Date:   Wed Feb 12 09:11:12 2025 -0500

    Connecting Front-End to DynamoDB Grant Data (#72)

    * created satchel store for all grants fetched from the backend to display in grantlist

commit fa05bd1
Author: Jane Kamata <[email protected]>
Date:   Wed Jan 29 19:02:58 2025 -0500

    ISO Date typed interface (#66)

    * Added navigation controls from account settings to grant list; changed dashboard to account

    * [JAN-1] Notifications base layer wrap-up

    * [JAN-11] Add navigation controls to go from the "dashboard" < -- > grant list

    * [JAN-1] Notifications base layer wrap-up

    * Created typed interface for ISO dates and tested

    ---------

    Co-authored-by: Jaren Adams <[email protected]>

commit a91bc98
Merge: f888c3b b47a86d
Author: prooflesben <[email protected]>
Date:   Wed Jan 29 18:33:48 2025 -0500

    Merge pull request #67 from Code-4-Community/ryaken

    Fixed

commit b47a86d
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 29 18:12:57 2025 -0500

    got rid of dependency

commit 0bbf3b6
Author: Jaren Adams <[email protected]>
Date:   Tue Jan 28 20:37:53 2025 -0500

    code style changes

commit 33b2948
Merge: 5f6d437 f888c3b
Author: Jaren Adams <[email protected]>
Date:   Tue Jan 28 20:27:45 2025 -0500

    pull in recent updates

commit 5f6d437
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 20:01:04 2025 -0500

    go away lock

commit 4c858b5
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 19:58:46 2025 -0500

    cleared node modules in frontend

commit 53c37d0
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 19:49:30 2025 -0500

    fixed gitignore

commit 06fbf94
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 19:47:59 2025 -0500

    fix crypto error

commit a1c29aa
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 19:34:55 2025 -0500

    try4

commit bf1ed9a
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 19:29:16 2025 -0500

    try3

commit 48c1832
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 19:18:57 2025 -0500

    try2

commit 9b39987
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 22 18:58:54 2025 -0500

    json change

commit 52b5fcb
Author: ryaken-nakamoto <[email protected]>
Date:   Tue Jan 21 18:38:32 2025 -0500

    added save functionality status

commit c291cef
Author: ryaken-nakamoto <[email protected]>
Date:   Wed Jan 15 18:52:36 2025 -0500

    cleaning up App.css and index.css, margin removed on edges when grant is expanded
  • Loading branch information
janekamata committed Feb 16, 2025
1 parent 2ffecab commit e99cfe6
Show file tree
Hide file tree
Showing 16 changed files with 344 additions and 104 deletions.
12 changes: 11 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 Down Expand Up @@ -29,4 +29,14 @@ 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
9 changes: 9 additions & 0 deletions frontend/src/external/bcanSatchel/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { action } from 'satcheljs';
import { Grant } from './store.js'

/**
* Set whether the user is authenticated, update the user object,
Expand All @@ -24,3 +25,11 @@ export const updateUserProfile = action('updateUserProfile', (user: any) => ({
* Completely log out the user (clear tokens, user data, etc.).
*/
export const logoutUser = action('logoutUser');

/**
* Moves along the all grants that are fetched from back end to mutator.
*/
export const fetchAllGrants = action(
'fetchAllGrants',
(grants: Grant[]) => ({grants})
);
11 changes: 10 additions & 1 deletion frontend/src/external/bcanSatchel/mutators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mutator } from 'satcheljs';
import { setAuthState, updateUserProfile, logoutUser } from './actions';
import { setAuthState, updateUserProfile, logoutUser, fetchAllGrants } from './actions';
import { getAppStore } from './store';

/**
Expand Down Expand Up @@ -34,3 +34,12 @@ mutator(logoutUser, () => {
store.user = null;
store.accessToken = null;
});


/**
* Reassigns all grants to new grants from the backend.
*/
mutator(fetchAllGrants, (actionMessage) => {
const store = getAppStore();
store.allGrants = actionMessage.grants;
});
21 changes: 21 additions & 0 deletions frontend/src/external/bcanSatchel/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,34 @@ export interface AppState {
isAuthenticated: boolean;
user: User | null;
accessToken: string | null;
allGrants: Grant[] | []
}

// model for Grant objects, matches exactly the same as backend grant.model
// TODO: should synchronize from same file?
export interface Grant {
grantId: number;
organization_name: string;
description: string;
is_bcan_qualifying: boolean;
status: string;
amount: number;
deadline: string;
notifications_on_for_user: boolean;
reporting_requirements: string;
restrictions: string;
point_of_contacts: string[];
attached_resources: string[];
comments: string[];
isArchived : boolean;
}

// Define initial state
const initialState: AppState = {
isAuthenticated: false,
user: null,
accessToken: null,
allGrants: []
};

const store = createStore<AppState>('appStore', initialState);
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
67 changes: 43 additions & 24 deletions frontend/src/grant-info/components/GrantItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,48 @@ import React, { useState } from "react";
import "./styles/GrantItem.css";
import { GrantAttributes } from "./GrantAttributes";
import GrantDetails from "./GrantDetails";
import { StatusContext } from "./StatusContext";
import { Grant } from "@/external/bcanSatchel/store.ts";

export interface GrantItemProps {
grantName: string;
applicationDate: Date;
generalStatus: string;
amount: number;
restrictionStatus: string;
interface GrantItemProps {
grant: Grant;
}
const GrantItem: React.FC<GrantItemProps> = (props) => {
const {
grantName,
applicationDate,
generalStatus,
amount,
restrictionStatus,
} = props;

// TODO: [JAN-14] Make uneditable field editable (ex: Description, Application Reqs, Additional Notes)
const GrantItem: React.FC<GrantItemProps> = ({ grant }) => {
const [isExpanded, setIsExpanded] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [curStatus, setCurStatus] = useState(grant.status);

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

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
Expand All @@ -34,21 +52,22 @@ const GrantItem: React.FC<GrantItemProps> = (props) => {
className={`grant-summary ${isExpanded ? "expanded" : ""}`}
onClick={toggleExpand}
>
<li className="grant-name">{grantName}</li>
<li className="application-date">
{applicationDate.toISOString().split("T")[0]}
</li>
<li className="status">{generalStatus}</li>
<li className="amount">${amount}</li>
<li className="restriction-status">{restrictionStatus}</li>
<li className="grant-name">{grant.organization_name}</li>
<li className="application-date">{"no attribute for app-date"}</li>
//.toISOString().split("T")[0]
<li className="status">{grant.status}</li>
<li className="amount">${grant.amount}</li>
<li className="restriction-status">{grant.restrictions}</li>
</ul>
<div className={`grant-body ${isExpanded ? "expanded" : ""}`}>
{isExpanded && (
<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
Loading

0 comments on commit e99cfe6

Please sign in to comment.