Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • shreyp2305/dbms-inventory-manager
1 result
Show changes
Commits on Source (20)
Showing
with 1332 additions and 26 deletions
......@@ -19,10 +19,11 @@ import MyOrganizations from "./components/myorg/MyOrganizations";
import OrganizationDetails from "./components/myorg/OrganizationDetails";
import NotFound from "./components/error/NotFound";
import OrganizationRoster from "./components/myorg/roster/OrganizationRoster";
import OrganizationItems from "./components/myorg/items/OrganizationItems";
import OrganizationItemDetails from "./components/myorg/items/OrganizationItemDetails";
import OrganizationItemCreate from "./components/myorg/items/OrganizationItemCreate";
import OrganizationItems from "./components/myorg/item/OrganizationItems";
import OrganizationItemDetails from "./components/myorg/item/OrganizationItemDetails";
import OrganizationItemCreate from "./components/myorg/item/OrganizationItemCreate";
import OrganizationLocations from "./components/myorg/location/OrganizationLocations";
import OrganizationRequests from "./components/myorg/request/OrganizationRequests";
function App() {
// const [token, setToken] = useState();
......@@ -74,6 +75,9 @@ function App() {
<Route element={<PrivateRoutes token={token} />}>
<Route path="/organizations/:orgId/locations" element={<OrganizationLocations token={token}/> } />
</Route>
<Route element={<PrivateRoutes token={token} />}>
<Route path="/organizations/:orgId/requests" element={<OrganizationRequests token={token}/> } />
</Route>
<Route path='*' element={<NotFound />}/>
<Route path='/404' element={<NotFound />}/>
{/*<Route path="/organizations/:orgId" element={<OrganizationDetails token={token}/>}>*/}
......
import React, { useState, useEffect } from 'react';
import {useParams, Navigate, useNavigate } from 'react-router-dom';
import Axios from 'axios';
import './OrganizationDetails.css'
// import './OrganizationDetails.css'
const OrganizationDetails = ({token}) => {
const { orgId } = useParams();
const [organization, setOrganization] = useState(null);
const [type, setType] = useState('');
const navigate = useNavigate();
useEffect(() => {
const fetchOrganizationDetails = async () => {
......@@ -17,6 +18,7 @@ const OrganizationDetails = ({token}) => {
if (response.data.result === 'success') {
setOrganization(response.data.data);
setType(response.data.type);
} else {
console.error('Error fetching organization details');
navigate('/404');
......@@ -66,6 +68,9 @@ const OrganizationDetails = ({token}) => {
navigate(`/organizations/${orgId}/locations`);
};
const handleRequestButtonClick = () => {
navigate(`/organizations/${orgId}/requests`);
};
if (!organization) {
return <div>Loading...</div>;
}
......@@ -82,7 +87,7 @@ const OrganizationDetails = ({token}) => {
{/* Buttons at the bottom */}
<div className="button-container">
<button className="blue-button" onClick={handleRosterButtonClick}>Roster</button>
<button className="blue-button">Requests</button>
{(type === 'MANAGER' || type === 'OWNER') && <button className="blue-button" onClick={handleRequestButtonClick}>Requests</button>}
<button className="blue-button" onClick={handleItemsButtonClick}>Items</button>
<button className="blue-button" onClick={handleLocationButtonClick}>Locations</button>
<button className="dark-red-button" onClick={handleLeaveButtonClick}>Leave Organization</button>
......
import React, { useState, useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import Axios from 'axios';
import '../../user/Login.css'
const OrganizationItemCreate = ({ token }) => {
const { orgId } = useParams();
const navigate = useNavigate();
const [locations, setLocations] = useState([]);
const [formData, setFormData] = useState({
status: 'AVAILABLE',
category: 'TOOLS',
description: '',
name: '',
quantity: 0,
locationId: '',
});
const [error, setError] = useState(null); // New state for error handling
useEffect(() => {
const fetchLocations = async () => {
try {
const response = await Axios.post('http://localhost:8080/item/user/location', {
orgId: parseInt(orgId),
jwt: token.jwt,
});
if (response.data.result === 'success') {
setLocations(response.data.data);
if (response.data.type === 'MEMBER')
{
//members are not supposed to create items, redirect them to 404 page
navigate('/404');
}
} else {
console.error('Error fetching locations');
}
} catch (error) {
console.error('Error fetching locations:', error);
}
};
fetchLocations();
}, [orgId, token]);
const handleDropdownChange = (field, value) => {
setFormData((prevData) => ({
...prevData,
[field]: value,
}));
};
const handleCreateItem = async () => {
try {
const response = await Axios.post('http://localhost:8080/item/user/oneitem/create', {
orgId: parseInt(orgId),
jwt: token.jwt,
...formData,
});
if (response.data.result === 'success') {
navigate(`/organizations/${orgId}/items`);
} else {
console.error('Error creating item');
setError('Error, incomplete fields'); // Set error message
}
} catch (error) {
console.error('Some unexpected error occurred:', error);
setError('Error, incomplete fields'); // Set error message
}
};
return (
<div className="organization-item-create">
<h2>Create Item</h2>
<div className="create-item-fields">
<div>
<span>Status:</span>
<select value={formData.status} onChange={(e) => handleDropdownChange('status', e.target.value)}>
<option value="AVAILABLE">Available</option>
<option value="BORROWED">Borrowed</option>
</select>
</div>
<div>
<span>Category:</span>
<select value={formData.category} onChange={(e) => handleDropdownChange('category', e.target.value)}>
<option value="STATIONERY">Stationery</option>
<option value="MARKETING">Marketing</option>
<option value="ELECTRONICS">Electronics</option>
<option value="SUPPLIES">Supplies</option>
<option value="PERISHABLES">Perishables</option>
<option value="MERCHANDISE">Merchandise</option>
<option value="TOOLS">Tools</option>
<option value="CHEMICALS">Chemicals</option>
<option value="FLAMMABLE">Flammable</option>
<option value="OTHER">Other</option>
<option value="UNIQUE">Unique</option>
<option value="BOOKS">Books</option>
{/* Add other category options */}
</select>
</div>
<label>
Description:
<input
type="text"
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
/>
</label>
<label>
Name:
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
</label>
<label>
Quantity:
<input
type="number"
value={formData.quantity}
onChange={(e) => setFormData({ ...formData, quantity: e.target.value })}
/>
</label>
<label>
Location:
<select
value={formData.locationId}
onChange={(e) => handleDropdownChange('locationId', e.target.value)}
>
<option value="">Select a location</option>
{locations.map(([locationName, locationId]) => (
<option key={locationId} value={locationId}>
{locationName}
</option>
))}
</select>
</label>
{error && <p className="error-message">{error}</p>}
</div>
<button onClick={handleCreateItem} className="create-item-button">
Create Item
</button>
</div>
);
};
export default OrganizationItemCreate;
/* OrganizationItemDetails.css */
.organization-item-details {
max-width: 600px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
align-items: center;
}
.item-details {
margin-top: 20px;
display: flex;
flex-direction: column;
align-items: center; /* Center the entire content */
text-align: center;
}
.item-details span {
display: block;
margin-bottom: 10px;
text-align: left;
width: 100%;
}
.modify-item-button {
display: block;
margin-top: 20px;
padding: 10px 20px;
color: #fff;
background-color: #5bc0de; /* Blue color for modify button */
border: 1px solid #5bc0de;
border-radius: 5px;
cursor: pointer;
}
.modify-item-button:hover {
background-color: #46b8da; /* Darker blue color on hover */
}
.delete-item-button {
background-color: #8b0000; /* Dark red color */
color: #fff;
border: 1px solid #6b0000; /* Dark red border color */
padding: 10px 20px;
font-size: 14px;
cursor: pointer;
border-radius: 5px;
margin-top: 20px;
}
.delete-item-button:hover
{
background-color: #6b0000; /* Dark red color on hover */
}
/* Style for dropdowns */
.item-details select {
padding: 8px;
width: 100%;
margin-bottom: 10px;
}
// OrganizationItemDetails.jsx
import React, { useState, useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import Axios from 'axios';
import './OrganizationItemDetails.css';
import '../OrganizationDetails.css'
const OrganizationItemDetails = ({ token }) => {
const { orgId, itemId } = useParams();
const [itemInfo, setItemInfo] = useState(null);
const [locations, setLocations] = useState([]);
const [modifiedLocation, setModifiedLocation] = useState('');
const [modifiedFields, setModifiedFields] = useState({
status: '',
category: '',
description: '',
name: '',
quantity: 0,
});
const navigate = useNavigate();
const [error, setError] = useState(null); // New state for error handling
const handleDeleteItem = async () => {
// Logic to handle item deletion
if (
window.confirm(
`Are you sure you want to permanently delete all quantities of ${itemInfo.data[1]}? This will delete the entire item.`
)
) {
console.log('Deleting item...');
try {
const response = await Axios.post('http://localhost:8080/item/user/oneitem/delete', {
orgId: parseInt(orgId), // Convert orgId to integer
jwt: token.jwt,
itemId: parseInt(itemId), // Convert itemId to integer
});
if (response.data.result === 'success') {
navigate(`/organizations/${orgId}/items`);
} else {
console.error('Error deleting item');
}
} catch (error) {
console.error('Some unexpected error occurred:', error);
}
}
};
const handleDropdownChange = (field, value) => {
setModifiedFields((prevFields) => ({
...prevFields,
[field]: value,
}));
};
const handleModifyItem = async () => {
// Logic to handle item modification
try {
const modifiedFieldsToSend = {
orgId: parseInt(orgId),
jwt: token.jwt,
itemId: parseInt(itemId),
...modifiedFields,
};
// Check if a modified field is left blank and use the current value from itemInfo
Object.keys(modifiedFields).forEach((key) => {
if (modifiedFields[key] === '') {
console.log(key);
if (key === 'status')
modifiedFieldsToSend[key] = itemInfo.data[6];
else if (key === 'quantity')
modifiedFieldsToSend[key] = itemInfo.data[4];
else if (key === 'category')
modifiedFieldsToSend[key] = itemInfo.data[5];
else if (key === 'name')
modifiedFieldsToSend[key] = itemInfo.data[1];
else if (key === 'description')
modifiedFieldsToSend[key] = itemInfo.data[2];
else if (key === 'location')
modifiedFieldsToSend[key] = itemInfo.data[9];
}
});
const response = await Axios.put('http://localhost:8080/item/user/oneitem', modifiedFieldsToSend);
if (response.data.result === 'success') {
// Reload the item details after modification
const fetchItemDetails = async () => {
try {
const response = await Axios.post('http://localhost:8080/item/user/oneitem', {
orgId: parseInt(orgId), // Convert orgId to integer
jwt: token.jwt,
itemId: parseInt(itemId), // Convert itemId to integer
});
if (response.data.result === 'success') {
setItemInfo(response.data);
} else {
console.error('Error fetching item information');
}
} catch (error) {
console.error('Error fetching item information:', error);
}
};
fetchItemDetails();
} else {
console.error('Error modifying item');
}
} catch (error) {
console.error('Some unexpected error occurred:', error);
}
};
// Fetch item details on component mount
useEffect(() => {
const fetchLocations = async () => {
try {
const response = await Axios.post('http://localhost:8080/item/user/location', {
orgId: parseInt(orgId),
jwt: token.jwt,
});
if (response.data.result === 'success') {
setLocations(response.data.data);
} else {
console.error('Error fetching locations');
}
} catch (error) {
console.error('Error fetching locations:', error);
}
};
fetchLocations();
const fetchItemDetails = async () => {
try {
const response = await Axios.post('http://localhost:8080/item/user/oneitem', {
orgId: parseInt(orgId),
jwt: token.jwt,
itemId: parseInt(itemId),
});
if (response.data.result === 'success') {
setItemInfo(response.data);
} else {
console.error('Error fetching item information');
}
} catch (error) {
console.error('Error fetching item information:', error);
}
};
fetchItemDetails();
}, [orgId, itemId, token]);
if (!itemInfo) {
return <div>Loading...</div>;
}
return (
<div className="organization-item-details">
<h2>Item Details</h2>
<div className="item-details">
<span>Name: {itemInfo.data[1]}</span>
<span>Description: {itemInfo.data[2]}</span>
<span>Owner Email: {itemInfo.data[3]}</span>
<span>Quantity: {itemInfo.data[4]}</span>
<span>Category: {itemInfo.data[5]}</span>
<span>Status: {itemInfo.data[6]}</span>
<span>Location: {itemInfo.data[9]}</span>
</div>
{(itemInfo.type === 'OWNER' || itemInfo.type === 'MANAGER') && (
<div>
<h3>Modify Item</h3>
<div className="modify-item-fields">
<div>
<span>Status:</span>
<select value={modifiedFields.status} defaultValue={itemInfo.data[6]} onChange={(e) => handleDropdownChange('status', e.target.value)}>
<option value="AVAILABLE">Available</option>
<option value="BORROWED">Borrowed</option>
</select>
</div>
<div>
<span>Category:</span>
<select value={modifiedFields.category} defaultValue={itemInfo.data[5]} onChange={(e) => handleDropdownChange('category', e.target.value)}>
<option value="STATIONERY">Stationery</option>
<option value="MARKETING">Marketing</option>
<option value="ELECTRONICS">Electronics</option>
<option value="SUPPLIES">Supplies</option>
<option value="PERISHABLES">Perishables</option>
<option value="MERCHANDISE">Merchandise</option>
<option value="TOOLS">Tools</option>
<option value="CHEMICALS">Chemicals</option>
<option value="FLAMMABLE">Flammable</option>
<option value="OTHER">Other</option>
<option value="UNIQUE">Unique</option>
<option value="BOOKS">Books</option>
</select>
</div>
<label>
Description:
<input
type="text"
value={modifiedFields.description}
onChange={(e) => setModifiedFields({ ...modifiedFields, description: e.target.value })}
/>
</label>
<label>
Name:
<input
type="text"
value={modifiedFields.name}
onChange={(e) => setModifiedFields({ ...modifiedFields, name: e.target.value })}
/>
</label>
<label>
Quantity:
<input
type="number"
value={modifiedFields.quantity}
onChange={(e) => setModifiedFields({ ...modifiedFields, quantity: e.target.value })}
/>
</label>
</div>
<label>
Location:
<select
value={modifiedFields.locationId}
onChange={(e) => setModifiedFields( { ...modifiedFields, locationId:e.target.value})}
>
<option value="">Select a location</option>
{locations.map(([locationName, locationId], index) => (
<option key={locationId} value={locationId}>
{locationName}
</option>
))}
</select>
</label>
<button onClick={handleModifyItem} className="modify-item-button">
Modify Item
</button>
</div>
)}
{(itemInfo.type === 'OWNER' || itemInfo.type === 'MANAGER') && (
<button onClick={handleDeleteItem} className="delete-item-button">
Delete Item
</button>
)}
</div>
);
};
export default OrganizationItemDetails;
.organization-items {
max-width: 800px;
margin: 0 auto;
}
h2 {
color: #333;
}
.filters {
margin-bottom: 20px;
}
label {
margin-right: 10px;
}
select,
input {
margin-right: 10px;
padding: 5px;
}
button {
padding: 5px 10px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
.items-list {
display: grid;
gap: 20px;
}
.item-item {
border: 1px solid #ddd;
padding: 15px;
border-radius: 8px;
background-color: #f9f9f9;
text-align: left;
}
.item-details {
display: flex;
flex-direction: column;
gap: 8px;
}
.item-details span {
display: block;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background-color: #f0f0f0; /* Optional background color for the header */
}
.create-item-button {
display: block;
margin-top: 20px;
padding: 10px 20px;
color: #fff;
background-color: #5bc0de; /* Blue color for modify button */
border: 1px solid #5bc0de;
border-radius: 5px;
cursor: pointer;
width: 300px;
}
/* Add more styles as needed */
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
import './OrganizationItems.css'
import './OrganizationItemDetails.css'
import {useNavigate, useParams} from "react-router-dom";
const OrganizationItems = ({ token }) => {
// const [orgId, setOrgId] = useState(null);
const [items, setItems] = useState([]);
const { orgId } = useParams();
const [filteredItems, setFilteredItems] = useState([]);
const [categories, setCategories] = useState([]);
const [locations, setLocations] = useState([]);
const [categoryFilter, setCategoryFilter] = useState('');
const [locationFilter, setLocationFilter] = useState('');
const [quantityFilter, setQuantityFilter] = useState('');
const [searchFilter, setSearchFilter] = useState('');
const navigate = useNavigate();
const handleItemClick = (item) => {
navigate(`/organizations/${orgId}/items/${item}`);
};
const [type, setType] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await Axios.post('http://localhost:8080/item/user/all', {
orgId: orgId,
jwt: token.jwt,
});
if (response.data.result === 'success') {
setItems(response.data.data);
setType(response.data.type);
setFilteredItems(response.data.data);
// Extract categories and locations for filtering
const uniqueCategories = [...new Set(response.data.data.map(item => item[5]))];
const uniqueLocations = [...new Set(response.data.data.map(item => item[9]))];
setCategories(['', ...uniqueCategories]);
setLocations(['', ...uniqueLocations]);
} else {
console.error('Error fetching organization items');
navigate('/404');
}
} catch (error) {
console.error('Error fetching organization items:', error);
navigate('/404');
}
};
// Set orgId based on your logic (e.g., from token or passed as a prop)
// setOrgId(2);
fetchData();
}, [orgId, token]);
const handleFilter = () => {
let filteredItemsCopy = [...items];
// Apply filters
if (categoryFilter) {
filteredItemsCopy = filteredItemsCopy.filter(item => item[5] === categoryFilter);
}
if (locationFilter) {
filteredItemsCopy = filteredItemsCopy.filter(item => item[9] === locationFilter);
}
if (quantityFilter) {
filteredItemsCopy = filteredItemsCopy.filter(item => item[4] >= parseInt(quantityFilter, 10));
}
if (searchFilter) {
filteredItemsCopy = filteredItemsCopy.filter(item =>
item[1].toLowerCase().includes(searchFilter.toLowerCase())
);
}
setFilteredItems(filteredItemsCopy);
};
const handleCreateButton = () => {
navigate(`/organizations/${orgId}/items/create`);
}
return (
<div className="organization-items">
<div className="header">
<h2>Organization Items</h2>
{(type === 'MANAGER' || type === 'OWNER') && <button className="create-item-button" onClick={() => handleCreateButton()}>
Create Item
</button>}
</div>
<div className="filters">
<label>
Category:
<select onChange={(e) => setCategoryFilter(e.target.value)}>
{categories.map((category, index) => (
<option key={index} value={category}>{category}</option>
))}
</select>
</label>
<label>
Location:
<select onChange={(e) => setLocationFilter(e.target.value)}>
{locations.map((location, index) => (
<option key={index} value={location}>{location}</option>
))}
</select>
</label>
<label>
Minimum Quantity:
<input
type="number"
value={quantityFilter}
onChange={(e) => setQuantityFilter(e.target.value)}
/>
</label>
<label>
Search by Name:
<input
type="text"
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
/>
</label>
<button onClick={handleFilter}>Apply Filters</button>
</div>
<div className="items-list">
{filteredItems.length > 0 ? (
filteredItems.map((item, index) => (
<div key={index} className="item-item" onClick={() => handleItemClick(item[0])}>
<div className="item-details" >
<span><strong>Name:</strong> {item[1]}</span>
<span><strong>Description:</strong> {item[2]}</span>
<span><strong>Owner Email:</strong> {item[3]}</span>
<span><strong>Quantity:</strong> {item[4]}</span>
<span><strong>Category:</strong> {item[5]}</span>
<span><strong>Status:</strong> {item[6]}</span>
<span><strong>Location:</strong> {item[9]}</span>
</div>
</div>
))
) : (
<p>No items found.</p>
)}
</div>
</div>
);
};
export default OrganizationItems;
.organization-requests {
max-width: 800px;
margin: 0 auto;
}
.requests-list {
list-style-type: none;
padding: 0;
}
.request-item {
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 10px;
padding: 10px;
position: relative;
}
.request-details {
display: flex;
justify-content: space-between;
}
.request-details span {
flex: 1;
padding: 10px;
}
.request-buttons {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 10px;
}
.accept-button,
.deny-button {
margin-left: 10px;
padding: 5px 10px;
cursor: pointer;
}
.accept-button:hover
{
background-color: #5cb85c;
border-color: #4cae4c;
color: #fff;
}
.deny-button:hover {
background-color: #6b0000; /* Dark red color on hover */
color: #fff;
}
.deny-button {
background-color: #8b0000; /* Dark red color */
border: 1px solid #6b0000; /* Dark red border color */
}
.status-column {
min-width: 100px;
}
.actions-column {
min-width: 120px;
text-align: right;
}
.pending-status {
color: #d9534f;
font-weight: bold;
}
.approved-status {
color: #5cb85c;
font-weight: bold;
}
.denied-status {
color: #777;
font-weight: bold;
}
// OrganizationRequests.jsx
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import Axios from 'axios';
import './OrganizationRequests.css'
const OrganizationRequests = ({ token }) => {
const { orgId } = useParams();
const [requests, setRequests] = useState([]);
const [userType, setUserType] = useState('');
useEffect(() => {
const fetchRequests = async () => {
try {
const response = await Axios.post('http://localhost:8080/request/user/requests', {
jwt: token.jwt,
orgId: parseInt(orgId),
});
if (response.data.result === 'success') {
setRequests(response.data.data);
setUserType(response.data.type);
} else {
console.error('Error fetching requests');
}
} catch (error) {
console.error('Error fetching requests:', error);
}
};
fetchRequests();
}, [orgId, token]);
const handleDecision = async (requestId, status) => {
try {
const response = await Axios.post('http://localhost:8080/request/user/request/update', {
jwt: token.jwt,
orgId: parseInt(orgId),
requestId: requestId,
status: status,
});
if (response.data.result === 'success') {
// Refresh the requests after making a decision
// fetchRequests();
window.location.reload(false);
} else {
console.error('Error making decision');
}
} catch (error) {
console.error('Error making decision:', error);
}
};
return (
<div className="organization-requests">
<h2>Organization Requests</h2>
<ul className="requests-list">
{requests.map(([requestId, userEmail, orgId, requestStatus, requestMessage, requestType, itemId, requestQuantity]) => (
<li key={requestId} className="request-item">
<div className="request-details">
<span className="status-column">
{requestStatus === 'PENDING' ? (
<span className="pending-status">{requestStatus}</span>
) : requestStatus === 'APPROVED' ? (
<span className="approved-status">{requestStatus}</span>
) : (
<span className="denied-status">{requestStatus}</span>
)}
</span>
<span>{userEmail}</span>
{/*<span>{requestStatus}</span>*/}
<span>{requestMessage}</span>
<span>{requestType}</span>
{requestType === 'ITEM' && <span>{requestQuantity}</span>}
</div>
<div className="request-buttons">
{requestStatus === 'PENDING' && (
<>
<button
className="accept-button"
onClick={() => handleDecision(requestId, 'ACCEPTED')}
>
Accept
</button>
<button
className="deny-button"
onClick={() => handleDecision(requestId, 'DECLINED')}
>
Decline
</button>
</>
)}
</div>
</li>
))}
</ul>
</div>
);
};
export default OrganizationRequests;
......@@ -80,17 +80,6 @@ WHERE true;
# OR o.owner_email = 'emilyjohnson@example.com';
# DROP TABLE REQUEST; DROP TABLE FAVORITE; DROP TABLE LISTING; DROP TABLE ITEM; DROP TABLE LOCATION;
CREATE TABLE IF NOT EXISTS REQUEST (
request_id INT AUTO_INCREMENT NOT NULL,
user_email VARCHAR(128) NOT NULL,
organization_id INT NOT NULL,
status ENUM('PENDING', 'ACCEPTED', 'DECLINED') NOT NULL,
description VARCHAR(256),
type ENUM('JOIN', 'ITEM') NOT NULL,
PRIMARY KEY (request_id),
CONSTRAINT fk_user_request FOREIGN KEY (user_email) REFERENCES USER (email) ON DELETE CASCADE,
CONSTRAINT fk_organization_request FOREIGN KEY (organization_id) REFERENCES ORGANIZATION (organization_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS LOCATION (
location_id INT AUTO_INCREMENT,
......@@ -122,6 +111,42 @@ CREATE TABLE IF NOT EXISTS ITEM (
CONSTRAINT fk_location_item FOREIGN KEY (location_id) REFERENCES LOCATION (location_id),
CONSTRAINT fk_organization_item FOREIGN KEY (organization_id) REFERENCES ORGANIZATION (organization_id) ON DELETE CASCADE
);
INSERT INTO ITEM (name, description, owner_email, quantity, category, status, location_id, organization_id)
VALUES
('Chess Set 1', 'Standard chess set with board', 'chesssociety@example.com', 5, 'TOOLS', 'AVAILABLE', 1, 2),
('Chess Clock', 'Digital chess clock for tournaments', 'chesssociety@example.com', 2, 'ELECTRONICS', 'AVAILABLE', 2, 2),
('Chess Strategy Book', 'Guide to advanced chess strategies', 'chesssociety@example.com', 3, 'BOOKS', 'AVAILABLE', 3, 2),
('Chess Tactics Guide', 'Book on improving chess tactics', 'chesssociety@example.com', 4, 'BOOKS', 'AVAILABLE', 1, 2),
('Chess Magazine', 'Latest issue of chess magazine', 'chesssociety@example.com', 1, 'BOOKS', 'AVAILABLE', 2, 2),
('Chess Puzzle Set', 'Collection of challenging chess puzzles', 'chesssociety@example.com', 6, 'TOOLS', 'AVAILABLE', 3, 2),
('Chess Club T-Shirt', 'Official Chess Society T-Shirt', 'chesssociety@example.com', 10, 'MERCHANDISE', 'AVAILABLE', 1, 2),
('Chess Trophy', 'Tournament winner trophy', 'chesssociety@example.com', 1, 'UNIQUE', 'AVAILABLE', 2, 2),
('Chess Analysis Board', 'Board for analyzing game positions', 'chesssociety@example.com', 2, 'TOOLS', 'AVAILABLE', 3, 2),
('Chess Membership Card', 'Official membership card for Chess Society', 'chesssociety@example.com', 1, 'OTHER', 'AVAILABLE', 1, 2);
CREATE TABLE IF NOT EXISTS REQUEST (
request_id INT AUTO_INCREMENT NOT NULL,
user_email VARCHAR(128) NOT NULL,
organization_id INT NOT NULL,
status ENUM('PENDING', 'ACCEPTED', 'DECLINED') NOT NULL,
description VARCHAR(256),
type ENUM('JOIN', 'ITEM') NOT NULL,
item_id INT,
quantity INT,
PRIMARY KEY (request_id),
CONSTRAINT fk_user_request FOREIGN KEY (user_email) REFERENCES USER (email) ON DELETE CASCADE,
CONSTRAINT fk_organization_request FOREIGN KEY (organization_id) REFERENCES ORGANIZATION (organization_id) ON DELETE CASCADE,
CONSTRAINT fk_item_request FOREIGN KEY (item_id) REFERENCES ITEM (item_id)
);
INSERT INTO REQUEST (user_email, organization_id, status, description, type)
VALUES
('alicedoe@example.com', 1, 'PENDING', 'Hi Im really interested in programming, wanted to check out this club', 'JOIN'),
('emilyjohnson@example.com', 1, 'ACCEPTED', 'Wanted to check this club out, Im a cs major', 'JOIN');
INSERT INTO REQUEST (user_email, organization_id, status, description, type, item_id, quantity )
VALUES
('johnsmith@example.com', 2, 'PENDING', 'I wanted to borrow a chess set to practice', 'ITEM', 1, 1),
('emilyjohnson@example.com', 2, 'PENDING', 'I wanted to borrow the chess magazine so I can read', 'ITEM', 5, 1);
INSERT INTO ITEM (name, description, owner_email, quantity, category, status, location_id, organization_id)
VALUES
......
......@@ -78,8 +78,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
......
......@@ -64,7 +64,6 @@ public class AuthController {
public @ResponseBody Map<String, String> verify(@RequestBody Map<String, Object> json)
{
Map<String, String> res = new HashMap<String, String>();
System.out.println(json.entrySet());
if (json.containsKey("jwt"))
{
Claims claim = JWT.decodeJWT((String) json.get("jwt")); //this will be a string
......
......@@ -35,7 +35,6 @@ public class ItemController {
public @ResponseBody Map<String, Object> getItems(@RequestBody Map<String, Object> json)
{
Map<String, Object> response = new HashMap<>();
System.out.println(json.entrySet());
if (!json.containsKey("orgId"))
{
response.put("result", "failure - bad request");
......@@ -58,7 +57,6 @@ public class ItemController {
public @ResponseBody Map<String, Object> getItemsToken(@RequestBody Map<String, Object> json)
{
Map<String, Object> response = new HashMap<>();
System.out.println(json.entrySet());
if (!json.containsKey("orgId") || !json.containsKey("jwt"))
{
response.put("result", "failure - bad request");
......@@ -155,14 +153,12 @@ public class ItemController {
public @ResponseBody Map<String, Object> deleteItemToken(@RequestBody Map<String, Object> json)
{
Map<String, Object> response = new HashMap<>();
System.out.println(json.entrySet());
if (!json.containsKey("orgId") || !json.containsKey("jwt") || !json.containsKey("itemId"))
{
response.put("result", "failure - bad request");
return response;
}
Map<String, Object> map = getUserOrg(json);
System.out.println(map.entrySet());
if (map.get("result").equals("success"))
{
Integer itemId;
......@@ -262,6 +258,12 @@ public class ItemController {
orgId = (Integer) json.get("orgId");
else
orgId = Integer.parseInt((String)(json.get("orgId")));
if (map.get("type") == OrganizationRoster.Type.MEMBER)
{
response.put("result", "failure - members are not authorized to create locations");
response.put("type", map.get("type"));
return response;
}
response.put("data", customItemRepository.createLocation((String)json.get("location"), orgId));
response.put("result", "success");
response.put("type", map.get("type"));
......@@ -289,6 +291,12 @@ public class ItemController {
locationId = (Integer) json.get("locationId");
else
locationId = Integer.parseInt((String)(json.get("locationId")));
if (map.get("type") == OrganizationRoster.Type.MEMBER)
{
response.put("result", "failure - members are not authorized to delete locations");
response.put("type", map.get("type"));
return response;
}
response.put("data", customItemRepository.deleteLocation(locationId));
response.put("result", "success");
response.put("type", map.get("type"));
......
......@@ -78,6 +78,17 @@ public class MyOrgRosterRepository implements OrgRosterRepository{
}
@Transactional
public Map<String, Object> addMember(Integer orgId, String memberEmail, OrganizationRoster.Type type)
{
String query = "INSERT INTO ORGANIZATION_ROSTER(user_email, organization_id, type) VALUES(:userEmail, :orgId, :type)";
Query nativeQuery = entityManager.createNativeQuery(query)
.setParameter("userEmail", memberEmail)
.setParameter("orgId", orgId)
.setParameter("type", type);
Map<String, Object> result = new HashMap<>();
return result;
}
@Transactional
public Map<String, Object> updateMember(Integer orgId, String memberEmail, OrganizationRoster.Type type)
{
......@@ -131,7 +142,6 @@ public class MyOrgRosterRepository implements OrgRosterRepository{
int updatedRows = promoteManagerNativeQuery.executeUpdate();
System.out.println("updated a random manager to be the owner, " + updatedRows);
if (updatedRows > 0) {
// Update owner_email in Organization table
String updateOwnerEmailQuery = "UPDATE organization SET owner_email = (SELECT user_email FROM organization_roster WHERE organization_id = :orgId AND type = 'OWNER') WHERE organization_id = :orgId";
......@@ -154,7 +164,6 @@ public class MyOrgRosterRepository implements OrgRosterRepository{
promoteMemberNativeQuery.setParameter("orgId", orgId);
int updatedRows = promoteMemberNativeQuery.executeUpdate();
System.out.println("updated a random member to be the owner, " + updatedRows);
if (updatedRows > 0) {
// Update owner_email in Organization table
String updateOwnerEmailQuery = "UPDATE organization SET owner_email = (SELECT user_email FROM organization_roster WHERE organization_id = :orgId AND type = 'OWNER') WHERE organization_id = :orgId";
......
......@@ -116,7 +116,6 @@ public class OrgRosterController {
// System.out.println(json.entrySet());
if (!json.containsKey("orgId") || !json.containsKey("newtype") || !json.containsKey("jwt") || !json.containsKey("memberEmail"))
{
System.out.println("thought there were wrong headers");
result.put("result", "failure bad request");
return result;
}
......
package com.example.accessingdatamysql.request;
import com.example.accessingdatamysql.item.Item;
import com.example.accessingdatamysql.myorg.OrganizationRoster;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Repository
public class CustomRequestRepository {
@Autowired
private EntityManager entityManager;
@Transactional
public List<Object> getRequests(Integer orgId)
{
String nativeQuery = "SELECT R.* FROM REQUEST R WHERE R.organization_id = :orgId";
Query query = entityManager.createNativeQuery(nativeQuery)
.setParameter("orgId", orgId);
return query.getResultList();
}
@Transactional
public Object updateRequest(@RequestBody Map<String, Object> json)
{
Map<String, Object> result = new HashMap<>();
//this is basically just for join type requests
Integer requestId;
if (json.get("requestId") instanceof Integer)
requestId = (Integer) json.get("requestId");
else
requestId = Integer.parseInt((String) json.get("requestId"));
Integer orgId;
if (json.get("orgId") instanceof Integer)
orgId = (Integer) json.get("orgId");
else
orgId = Integer.parseInt((String) json.get("orgId"));
String nativeQuery = "UPDATE REQUEST r SET status = ?1 WHERE r.request_id = ?2";
Query query = entityManager.createNativeQuery(nativeQuery)
.setParameter(1, json.get("status"))
.setParameter(2, requestId);
query.executeUpdate();
// System.out.println(json.entrySet());
if (json.get("status").equals("ACCEPTED"))
{
String getQuery = "SELECT r.* FROM REQUEST r WHERE r.request_id = ?1";
Query getq = entityManager.createNativeQuery(getQuery, Request.class)
.setParameter(1, requestId);
Request request = (Request) getq.getSingleResult();
// System.out.println(objectList.get(0));
// System.out.println(request.toString());
// System.out.println(userEmail);
if (request.getType() == Request.Type.JOIN) {
String insertQuery = "INSERT INTO ORGANIZATION_ROSTER(user_email, organization_id, type) VALUES(?1, ?2, ?3)";
Query inq = entityManager.createNativeQuery(insertQuery)
.setParameter(1, request.getUserEmail())
.setParameter(2, orgId)
.setParameter(3, "MEMBER");
String memberCount = "UPDATE ORGANIZATION o SET member_count = member_count + 1 WHERE o.organization_id = ?1";
Query memberQ = entityManager.createNativeQuery(memberCount)
.setParameter(1, orgId);
inq.executeUpdate();
//add the user as a member to the organization
memberQ.executeUpdate(); //update member count
result.put("member", "added successfully");
}
else
{
String getItemQuery = "SELECT i.quantity FROM ITEM i WHERE i.item_id = ?1";
Query itemQuery = entityManager.createNativeQuery(getItemQuery, Integer.class)
.setParameter(1, request.getItem_id());
Integer itemQuantity = (Integer) itemQuery.getSingleResult();
if (itemQuantity <= request.getQuantity())
{
// if it's greater just give them everything.
String itemUpdateQuery = "UPDATE ITEM i SET status = 'BORROWED', owner_email=?2 WHERE i.item_id = ?1";
Query iuQuery = entityManager.createNativeQuery(itemUpdateQuery)
.setParameter(1, request.getItem_id())
.setParameter(2, request.getUserEmail());
iuQuery.executeUpdate();
}
else
{
String itemUpdateQuery = "UPDATE ITEM i SET quantity = ?1 WHERE i.item_id = ?2"; //reduce quantity
Query iuQuery = entityManager.createNativeQuery(itemUpdateQuery)
.setParameter(1, (itemQuantity - request.getQuantity()))
.setParameter(2, request.getItem_id());
iuQuery.executeUpdate(); //update the item to be less quantity in available state
String getItemAllQuery = "SELECT i.* FROM ITEM i WHERE i.item_id = ?1";
Query getItemAllQ = entityManager.createNativeQuery(getItemAllQuery, Item.class)
.setParameter(1, request.getItem_id());
Item existingItem = (Item) getItemAllQ.getSingleResult();
String itemInsertQuery = "INSERT INTO ITEM(name, description, owner_email, quantity, category, status, location_id, organization_id) "
+ "VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)";
Query iQuery = entityManager.createNativeQuery(itemInsertQuery)
.setParameter(1, existingItem.getName())
.setParameter(2, existingItem.getDescription())
.setParameter(3, request.getUserEmail())
.setParameter(4, request.getQuantity())
.setParameter(5, existingItem.getCategory().name())
.setParameter(6, "BORROWED")
.setParameter(7, existingItem.getLocationId())
.setParameter(8, existingItem.getOrganizationId());
iQuery.executeUpdate();
}
}
}
return query.executeUpdate() > 0;
}
}
package com.example.accessingdatamysql;
package com.example.accessingdatamysql.request;
import jakarta.persistence.*;
......@@ -20,14 +20,58 @@ public class Request {
private String userEmail;
private int orgId;
private Integer organizationId;
private String description;
public Integer getItem_id() {
return item_id;
}
public void setItem_id(Integer item_id) {
this.item_id = item_id;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
@Enumerated(EnumType.STRING)
private Status status;
@Enumerated(EnumType.STRING)
private Type type;
private Integer item_id;
@Override
public String toString() {
return "Request{" +
"userEmail='" + userEmail + '\'' +
", organizationId=" + organizationId +
", description='" + description + '\'' +
", status=" + status +
", type=" + type +
", item_id=" + item_id +
", quantity=" + quantity +
", requestId=" + requestId +
'}';
}
private Integer quantity;
public Integer getOrganizationId() {
return organizationId;
}
public void setOrganizationId(Integer organizationId) {
this.organizationId = organizationId;
}
public Status getStatus() {
return status;
}
......@@ -52,31 +96,26 @@ public class Request {
this.userEmail = userEmail;
}
public int getOrgId() {
return orgId;
}
public void setOrgId(int orgId) {
this.orgId = orgId;
}
public String getDesc() {
public String getDescription() {
return description;
}
public void setDesc(String description) {
public void setDescription(String description) {
this.description = description;
}
@Id
@Column(nullable = false)
private int reqId;
public int getReqId() {
return reqId;
public int getRequestId() {
return requestId;
}
public void setReqId(int reqId) {
this.reqId = reqId;
public void setRequestId(int requestId) {
this.requestId = requestId;
}
@Id
@Column(nullable = false)
private int requestId;
}
\ No newline at end of file
package com.example.accessingdatamysql.request;
import com.example.accessingdatamysql.User;
import com.example.accessingdatamysql.UserRepository;
import com.example.accessingdatamysql.auth.AuthController;
import com.example.accessingdatamysql.myorg.MyOrgRosterRepository;
import com.example.accessingdatamysql.myorg.OrganizationRoster;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@CrossOrigin
@RestController // This means that this class is a Controller
@RequestMapping(path="/request") // This means URL's start with /request (after Application path)
public class RequestController {
@Autowired
private RequestRepository requestRepository;
@Autowired
private CustomRequestRepository customRequestRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private MyOrgRosterRepository myOrgRosterRepository;
@GetMapping(path="/all")
public @ResponseBody Iterable<Request> getAll()
{
return requestRepository.findAll();
}
@GetMapping(path="/random")
public @ResponseBody Request getOne()
{
return requestRepository.findById(1).get();
}
@PostMapping(path = "/user/requests")
public @ResponseBody Map<String, Object> getOrgRequest(@RequestBody Map<String, Object> json)
{
Map<String, Object> response = new HashMap<>();
if (!json.containsKey("jwt") || !json.containsKey("orgId"))
{
response.put("result", "failure - bad request");
return response;
}
Map<String, Object> map = getUserOrg(json);
if (map.get("result").equals("success"))
{
Integer orgId;
if (json.get("orgId") instanceof Integer)
orgId = (Integer) json.get("orgId");
else
orgId = Integer.parseInt((String)(json.get("orgId")));
if (map.get("type") == OrganizationRoster.Type.MEMBER)
{
response.put("result", "failure - not authorized members cannot view requests");
response.put("type", map.get("type"));
return response;
}
response.put("data", customRequestRepository.getRequests(orgId));
response.put("result", "success");
response.put("type", map.get("type"));
}
else
{
response.put("result", "failure - not authorized");
}
return response;
}
@PostMapping(path = "/user/request/update")
public @ResponseBody Map<String, Object> updateOrgRequest(@RequestBody Map<String, Object> json)
{
Map<String, Object> response = new HashMap<>();
if (!json.containsKey("jwt") || !json.containsKey("orgId") || !json.containsKey("status") || !json.containsKey("requestId"))
{
response.put("result", "failure - bad request");
return response;
}
Map<String, Object> map = getUserOrg(json);
if (map.get("result").equals("success"))
{
Integer orgId;
if (json.get("orgId") instanceof Integer)
orgId = (Integer) json.get("orgId");
else
orgId = Integer.parseInt((String)(json.get("orgId")));
if (map.get("type") == OrganizationRoster.Type.MEMBER)
{
response.put("result", "failure - not authorized members cannot view requests");
response.put("type", map.get("type"));
return response;
}
response.put("data", customRequestRepository.updateRequest(json));
response.put("result", "success");
response.put("type", map.get("type"));
}
else
{
response.put("result", "failure - not authorized");
}
return response;
}
/*
Private helper function to validate that the user is supposed to see this data
*/
private @ResponseBody Map<String, Object> getUserOrg(Map<String, Object> json)
{
Map<String, Object> response = new HashMap<>();
if (!json.containsKey("orgId") || !json.containsKey("jwt"))
{
response.put("result", "failed - bad request");
return response;
}
User found = new User();
AuthController au = new AuthController();
Map<String, String> res = au.verify(json); // if the jwt token could not be verified
if (res.containsKey("login") && res.get("login").equals("failed"))
{
response.put("result", "failed = bad token or bad request");
return response;
}
Optional<User> usr = userRepository.findById(res.get("user"));
if (!usr.isPresent())
{
response.put("result", "failed = user not found");
return response;
}
if (json.get("orgId") instanceof Integer)
return myOrgRosterRepository.findUserOrg(usr.get().getEmail(), (Integer) json.get("orgId"));
return myOrgRosterRepository.findUserOrg(usr.get().getEmail(), Integer.parseInt((String) json.get("orgId")));
}
}
package com.example.accessingdatamysql.request;
import org.springframework.data.repository.CrudRepository;
public interface RequestRepository extends CrudRepository<Request, Integer>{
}