diff --git a/src/client/App.js b/src/client/App.js deleted file mode 100644 index cd59a87c..00000000 --- a/src/client/App.js +++ /dev/null @@ -1,77 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { BrowserRouter as Router, Switch } from 'react-router-dom'; - -import LoaderAnimation from './components/loader-animation/loader-animation.component'; -import LoginPage from './containers/login-page/login-page.component'; -import ForgotPassword from './containers/forgot-password/forgot-password.component'; -import Dashboard from './containers/dashboard-page/dashboard-page.component'; -import Page404 from './containers/404-page/404-page.component'; -import Firebase, { FirebaseContext } from './firebase/index'; -import { getTokenWithHeaders } from './firebase/getTokenWithHeaders'; -import UserRoleContext from './helpers/UserRoleContext'; -import PrivateRoute from './helpers/PrivateRoute'; -import PublicRoute from './helpers/PublicRoute'; -import signInAsDefaultUser from './helpers/signInAsDefaultUser'; - -function App() { - const [userState, setUserState] = useState(null); - const [userFetched, setUserFetched] = useState(false); - const [userRole, setUserRole] = useState(''); - const [userName, setUserName] = useState(''); - - const fetchNameRole = async () => { - const headers = await getTokenWithHeaders(); - - const role = await fetch('/api/users/role', { - method: 'GET', - headers, - }).then((data) => data.json()); - setUserRole(role[0].name); - - const name = await fetch('/api/users/name', { - method: 'GET', - headers, - }).then((data) => data.json()); - setUserName(name[0].name); - }; - - useEffect(() => { - if (process.env.NODE_ENV === 'development') { - signInAsDefaultUser(); - } - - Firebase.getAuth().onAuthStateChanged((user) => { - if (user) { - setUserState(user); - fetchNameRole(); - } else { - setUserState(null); - } - setUserFetched(true); - }); - }, []); - - if (!userFetched) return ; - - return ( - - - - - - - - - - - - - - ); -} - -export default App; diff --git a/src/client/components/batch-queue-alert-window/batch-queue-alert-window.component.js b/src/client/components/batch-queue-alert-window/batch-queue-alert-window.component.js new file mode 100644 index 00000000..31b0ad30 --- /dev/null +++ b/src/client/components/batch-queue-alert-window/batch-queue-alert-window.component.js @@ -0,0 +1,80 @@ +import React from 'react'; +import Popup from 'reactjs-popup'; +import './batch-queue-alert-window.style.css'; +import PropTypes from 'prop-types'; +import moment from 'moment'; + +export default function BatchQueueAlertWindow({ + openState, + closeAction, + batchItem, +}) { + const popupView = () => ( + + + + + + ); + + return ( + + {popupView} + + ); +} + +const PopupModal = ({ closeAction, children, title }) => { + return ( +
+ +
{title}
+ {children} +
+ ); +}; + +const AlertContent = ({ batchItem, children }) => { + return ( + <> +
+

Batch #{batchItem.id} starts on

+

{moment(batchItem.seeding_date).format('DD MMMM YYYY')}

+
+ {children} + + ); +}; + +const AlertActions = ({ closeAction }) => ( +
+ +
+); + +BatchQueueAlertWindow.propTypes = { + openState: PropTypes.bool.isRequired, + closeAction: PropTypes.func.isRequired, + batchItem: PropTypes.oneOfType([PropTypes.object]).isRequired, +}; +AlertActions.propTypes = { + closeAction: PropTypes.func.isRequired, +}; +AlertContent.propTypes = { + batchItem: PropTypes.oneOfType([PropTypes.object]).isRequired, + children: PropTypes.oneOfType([PropTypes.object]).isRequired, +}; +PopupModal.propTypes = { + closeAction: PropTypes.func.isRequired, + children: PropTypes.oneOfType([PropTypes.object]).isRequired, + title: PropTypes.string.isRequired, +}; diff --git a/src/client/components/batch-queue-alert-window/batch-queue-alert-window.stories.js b/src/client/components/batch-queue-alert-window/batch-queue-alert-window.stories.js new file mode 100644 index 00000000..931fdb03 --- /dev/null +++ b/src/client/components/batch-queue-alert-window/batch-queue-alert-window.stories.js @@ -0,0 +1,36 @@ +import React from 'react'; +import BatchQueueAlertWindow from './batch-queue-alert-window.component'; +import { action } from '@storybook/addon-actions'; +import { withKnobs } from '@storybook/addon-knobs'; + +export default { + title: 'Batch Queue Alert Window', + component: BatchQueueAlertWindow, + decorators: [withKnobs], +}; + +export const BatchQueueAlertWindowStory = () => { + const batchItem = { + id: 1, + name: 'Lettuce ', + plant_variety: 'Asteraceae', + customer_name: 'Italian cucine', + number_of_seeded_pots: 50, + seeding_date: '2020-04-19T22:00:00.000Z', + current_stage: { + stage: 'propagation', + day: 4, + }, + status: 'active', + }; + + const closeAction = action('close popup'); + + return ( + + ); +}; diff --git a/src/client/components/batch-queue-alert-window/batch-queue-alert-window.style.css b/src/client/components/batch-queue-alert-window/batch-queue-alert-window.style.css new file mode 100644 index 00000000..520e3b28 --- /dev/null +++ b/src/client/components/batch-queue-alert-window/batch-queue-alert-window.style.css @@ -0,0 +1,67 @@ +.modal { + background-color: var(--light-grey); + border-radius: var(--border-radius-low); + box-shadow: var(--embossed-shadow-high); + font-size: 1.5em; + padding: 1em 0; + height: 15em !important; + width: 18em !important; +} + +/*styling modal header*/ +.modal > .header { + font-size: 1.2em; + margin: 1em 0 0.5em 0; + position: relative; + text-align: center; + text-transform: uppercase; + width: 100%; +} +.modal > .content { + width: 100%; + padding: 1.2em 0; + text-align: center; + margin-bottom: 2.5rem; +} +.modal > .content > p { + margin-bottom: 1rem; +} +.modal > .actions { + width: 100%; + padding: 10px 5px; + margin: auto; + text-align: center; +} + +/*close button on top right corner of pop up window styling*/ +.modal > .close { + border: none; + background: transparent; + color: var(--dark-grey); + cursor: pointer; + display: block; + font-size: 1.8em; + line-height: 1em; + position: absolute; + right: 0.4em; + top: 0.3em; +} + +/* Confoirm Logout button on pop up window styling */ +.okay { + background: var(--interaction-color); + border: var(--border-inputs); + border-radius: var(--border-radius-high); + box-shadow: var(--embossed-shadow-medium); + color: var(--white); + font-size: 1.7em; + line-height: 1.2em; + outline: none; + padding: 0.3em 3em; + text-align: center; + text-transform: uppercase; +} + +.sb-show-main .popup-content { + position: absolute !important; +} diff --git a/src/client/components/list-batches-buttons/list-batches-button-show.stories.js b/src/client/components/list-batches-buttons/list-batches-button-show.stories.js new file mode 100644 index 00000000..42c0193f --- /dev/null +++ b/src/client/components/list-batches-buttons/list-batches-button-show.stories.js @@ -0,0 +1,20 @@ +import React from 'react'; +import BatchesMenu from './list-batches-button-show.component'; +import { withKnobs, object } from '@storybook/addon-knobs'; + +export default { + title: 'Batches-Buttons', + component: BatchesMenu, + decorators: [withKnobs], +}; + +const label = 'value'; +const defaultValue = [ + { id: 1, label: 'Active Batches' }, + { id: 2, label: 'All Batches' }, +]; + +export const BatchesButtonsShow = () => { + const value = object(label, defaultValue); + return ; +}; diff --git a/src/client/components/list-batches-buttons/list-batches-button-sort.stories.js b/src/client/components/list-batches-buttons/list-batches-button-sort.stories.js new file mode 100644 index 00000000..93866dbd --- /dev/null +++ b/src/client/components/list-batches-buttons/list-batches-button-sort.stories.js @@ -0,0 +1,20 @@ +import React from 'react'; +import BatchesMenu from './list-batches-button-sort.component'; +import { withKnobs, object } from '@storybook/addon-knobs'; + +export default { + title: 'Batches-Buttons', + component: BatchesMenu, + decorators: [withKnobs], +}; + +const label = 'value'; +const defaultValue = [ + { id: 1, label: 'Customer Name' }, + { id: 2, label: 'Seeding Date' }, +]; + +export const BatchesButtonsSort = () => { + const value = object(label, defaultValue); + return ; +}; diff --git a/src/client/components/list-batches-buttons/list-batches-button.style.css b/src/client/components/list-batches-buttons/list-batches-button.style.css new file mode 100644 index 00000000..1f15c6c0 --- /dev/null +++ b/src/client/components/list-batches-buttons/list-batches-button.style.css @@ -0,0 +1,14 @@ +.batches-button-wrapper { + background: var(--light-grey); + color: var(--dark-blue); + box-sizing: border-box; + display: flex; + flex-direction: row; + text-align: center; + justify-content: flex-start; + padding: 0.5em 0; + transition: none; + width: 100%; + outline: none; + position: relative; +} diff --git a/src/client/components/list-batches/batch-data.json b/src/client/components/list-batches/batch-data.json new file mode 100644 index 00000000..081b010e --- /dev/null +++ b/src/client/components/list-batches/batch-data.json @@ -0,0 +1,43 @@ +{ + "batchesData": [ + { + "id": 1, + "name": "Lettuce ", + "plant_variety": "Asteraceae", + "customer_name": "Italian cucine", + "number_of_seeded_pots": 50, + "seeding_date": "2020-04-19T22:00:00.000Z", + "current_stage": { + "stage": "propagation", + "day": 4 + }, + "status": "active" + }, + { + "id": 2, + "name": "Shiso ", + "plant_variety": "Lamiaceae", + "customer_name": "Mexican tacos", + "number_of_seeded_pots": 50, + "seeding_date": "2020-04-30T22:00:00.000Z", + "current_stage": { + "stage": "queued", + "day": null + }, + "status": "queued" + }, + { + "id": 3, + "name": "Lettuce ", + "plant_variety": "Asteraceae", + "customer_name": "Turkish Kebab", + "number_of_seeded_pots": 50, + "seeding_date": "2020-05-31T22:00:00.000Z", + "current_stage": { + "stage": "queued", + "day": null + }, + "status": "queued" + } + ] +} diff --git a/src/client/components/list-batches/list-batches.component.js b/src/client/components/list-batches/list-batches.component.js new file mode 100644 index 00000000..623cf45d --- /dev/null +++ b/src/client/components/list-batches/list-batches.component.js @@ -0,0 +1,72 @@ +import React from 'react'; +import './list-batches.style.css'; +import PropTypes from 'prop-types'; +import moment from 'moment'; + +const headings = [ + 'BATCH#', + 'CROP NAME', + 'CUSTOMER', + 'STATUS', + 'CURRENT STAGE', + 'SEEDING POTS', + 'SEEDING DATE', +]; + +export default function ListBatches({ + batchData, + openDashboardFunc, + openAlertWindowFunc, +}) { + const columns = headings.map((column) => { + return {column}; + }); + + return ( + <> + + + {columns} + + + {batchData.length > 0 ? ( + batchData.map((batch, id) => ( + + batch.status === 'active' + ? openDashboardFunc(batch.id) + : openAlertWindowFunc(id) + } + > + + + + + + + + + )) + ) : ( + + + + )} + +
Batch #{batch.id} + {batch.plant_variety}- {batch.name} + {batch.customer_name}{batch.status} + {batch.current_stage.stage} + {batch.current_stage.day > 0 && + `- Day ${batch.current_stage.day}`} + {batch.number_of_seeded_pots}{moment(batch.seeding_date).format('DD MMMM YYYY')}
No data available
+ + ); +} + +ListBatches.propTypes = { + batchData: PropTypes.oneOfType([PropTypes.array]).isRequired, + openDashboardFunc: PropTypes.func.isRequired, + openAlertWindowFunc: PropTypes.func.isRequired, +}; diff --git a/src/client/components/list-batches/list-batches.stories.js b/src/client/components/list-batches/list-batches.stories.js new file mode 100644 index 00000000..ecb43b58 --- /dev/null +++ b/src/client/components/list-batches/list-batches.stories.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import ListBatches from './list-batches.component'; +import data from './batch-data.json'; + +export default { title: 'ListBatches' }; + +export const ListBatchesTable = () => ( + +); diff --git a/src/client/components/list-batches/list-batches.style.css b/src/client/components/list-batches/list-batches.style.css new file mode 100644 index 00000000..0835d216 --- /dev/null +++ b/src/client/components/list-batches/list-batches.style.css @@ -0,0 +1,22 @@ +table { + border-collapse: collapse; + border-style: hidden; + border-radius: var(--border-radius-low); + width: 80%; + background-color: rgba(255, 255, 255, 0.7); + text-align: center; +} + +th, +td { + height: 50px; + border: 1px solid var(--grey); +} + +td { + font-size: 0.9em; +} + +.capitalize { + text-transform: capitalize; +} diff --git a/src/client/containers/dashboard-page/dashboard-page.component.js b/src/client/containers/dashboard-page/dashboard-page.component.js index 27309473..c493fd15 100644 --- a/src/client/containers/dashboard-page/dashboard-page.component.js +++ b/src/client/containers/dashboard-page/dashboard-page.component.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useContext } from 'react'; import Firebase from '../../firebase/index'; -import { useHistory } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import UserRoleContext from '../../helpers/UserRoleContext'; import DashboardPage from '../../components/dashboard-page/dashboard-page.component'; @@ -9,6 +9,8 @@ import { getTokenWithHeaders } from '../../firebase/getTokenWithHeaders'; const DashboardPageContainer = () => { const history = useHistory(); + const location = useLocation(); + const { batchId } = location.state; const { userRole, userName } = useContext(UserRoleContext); const [logoutModal, setLogoutModal] = useState(false); @@ -23,7 +25,7 @@ const DashboardPageContainer = () => { const [lineChartDataWaterLevel, setLineChartDataWaterLevel] = useState(null); const [boundaryData, setBoundaryData] = useState(null); - const fetchData = async () => { + const fetchData = async (id) => { const headers = await getTokenWithHeaders(); const fetchSensorsData = async (endpoint, setSensorsData) => { @@ -34,7 +36,7 @@ const DashboardPageContainer = () => { setSensorsData(sensorsData.slice(-5)); }; - const stagesData = await fetch('/api/crop-stages/1', { + const stagesData = await fetch(`/api/crop-stages/${id}`, { method: 'GET', headers, }).then((data) => data.json()); @@ -47,7 +49,7 @@ const DashboardPageContainer = () => { fetchSensorsData('/api/sensor-reading/5', setLineChartDataWaterLevel); const defaultValues = await fetch( - '/api/batch-default-values/1?stage=current', + `/api/batch-default-values/${id}?stage=current`, { method: 'GET', headers, @@ -57,8 +59,8 @@ const DashboardPageContainer = () => { }; useEffect(() => { - fetchData(); - }, []); + fetchData(batchId); + }, [batchId]); useEffect(() => { if ( diff --git a/src/client/containers/list-batches-page/list-batches-page.component.js b/src/client/containers/list-batches-page/list-batches-page.component.js new file mode 100644 index 00000000..721e337f --- /dev/null +++ b/src/client/containers/list-batches-page/list-batches-page.component.js @@ -0,0 +1,126 @@ +import React, { useState, useEffect, useContext } from 'react'; +import UserRoleContext from '../../helpers/UserRoleContext'; +import { getTokenWithHeaders } from '../../firebase/getTokenWithHeaders'; +import { useHistory } from 'react-router-dom'; +import Firebase from '../../firebase/index'; +import BatchQueueAlertWindowStory from '../../components/batch-queue-alert-window/batch-queue-alert-window.component'; +import SidebarMenu from '../../components/side-navigation/sidebar.component'; +import Logout from '../../components/logout/logout.component'; +// import Button from '../../components/button/button.component'; +// import ShowButton from '../../components/list-batches-buttons/list-batches-button-show.component'; +// import SortButton from '../../components/list-batches-buttons/list-batches-button-sort.component'; +import ListBatches from '../../components/list-batches/list-batches.component'; + +import Footer from '../../components/footer/footer.component'; +import LoaderAnimation from '../../components/loader-animation/loader-animation.component'; +import './list-batches-page.style.css'; + +export default function ListBatchesPage() { + const history = useHistory(); + const { userRole, userName } = useContext(UserRoleContext); + + const [logoutModal, setLogoutModal] = useState(false); + const [itemId, setItemId] = useState(0); + const [alertWindow, setAlertWindow] = useState(false); + const [loading, setLoading] = useState(true); + const [listBatchData, setListBatchData] = useState(null); + + const fetchData = async () => { + const headers = await getTokenWithHeaders(); + + const batchesData = await fetch('/api/batches?detailed=true', { + method: 'GET', + headers, + }).then((data) => data.json()); + setListBatchData(batchesData); + }; + + useEffect(() => { + fetchData(); + }, []); + + useEffect(() => { + if (userRole && userName && listBatchData) setLoading(false); + }, [userRole, userName, listBatchData]); + + return loading ? ( + + ) : ( +
+ history.push('/dashboard')} + showBatchDetails={() => history.push('/batch-details')} + showAddBatch={() => history.push('/add-batch')} + logout={() => setLogoutModal(true)} + /> + setLogoutModal(false)} + logoutFunc={() => Firebase.signOut()} + /> +
+
+
+

BATCH LIST

+
+
+ {/* TODO + */} +
+ + history.push('/dashboard', { batchId }) + } + openAlertWindowFunc={(batchId) => { + setItemId(batchId); + return setAlertWindow(true); + }} + /> + setAlertWindow(false)} + batchItem={listBatchData[itemId]} + /> +
+ {/* TODO +
+ +
*/} +
+
+
+
+
+ ); +} diff --git a/src/client/containers/list-batches-page/list-batches-page.style.css b/src/client/containers/list-batches-page/list-batches-page.style.css new file mode 100644 index 00000000..2faad2c0 --- /dev/null +++ b/src/client/containers/list-batches-page/list-batches-page.style.css @@ -0,0 +1,104 @@ +.batch-list { + display: flex; + justify-content: flex-start; + min-height: 100vh; +} + +.batch-list main { + align-items: flex-start; + display: flex; + flex-direction: column; + height: auto; + justify-content: flex-start; + padding: 0 1em; + width: 100%; +} + +.batch-list .content { + display: flex; + flex-direction: column; + height: 100vh; + justify-content: flex-start; + margin-left: 130px; + width: 100%; +} + +.batch-list .content .wrapper { + display: flex; + flex-direction: column; + justify-content: center; + margin: 0 auto; + width: 1100px; +} + +.batch-list header { + padding: 1em 1em 0; +} + +.batch-list header h1 { + font-size: 1.3em; + margin: 1em 0 0; + padding: 0 1em; +} + +nav { + display: flex; + flex-direction: row; + padding: 1em 1em 0; +} + +.filter-options { + display: flex; + flex-direction: row; + align-items: center; + padding: 1em 2em 0 1em; +} + +.filter-buttons { + display: flex; + flex-direction: row; + align-items: center; + margin: 0 1em; +} + +.filter-labels { + font-weight: bold; +} + +.batch-table { + padding: 1em 1em 0; + margin: 2em 0 0; + width: 100%; +} + +.batch-table table { + text-align: left; + width: 100%; +} + +.batch-table table tbody tr { + cursor: pointer; +} + +.batch-table table th { + padding: 2em 0.5em 0.5em; +} + +.batch-table table td { + padding: 0 0.6em; +} + +.export-button { + margin: 0 1em 2em auto; +} + +.batch-list .modal > .content { + display: block; + height: auto; + margin-left: 0; +} + +.batch-list .sidebar-wrapper button:first-of-type, +.batch-list .sidebar-wrapper button:nth-of-type(2) { + display: none; +} diff --git a/src/client/containers/login-page/login-page.component.js b/src/client/containers/login-page/login-page.component.js deleted file mode 100644 index 139fbc02..00000000 --- a/src/client/containers/login-page/login-page.component.js +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useState, useEffect, useContext } from 'react'; -import { useHistory } from 'react-router-dom'; - -import './login-page.style.css'; -import Login from '../../components/login/login.component'; -import Firebase, { FirebaseContext } from '../../firebase/index'; - -function LoginPage() { - const history = useHistory(); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [errorMessage, setErrorMessage] = useState(''); - const [errorNotifications, setErrorNotifications] = useState(null); - const user = useContext(FirebaseContext); - - useEffect(() => { - if (user) history.replace('/dashboard'); - }); - - return ( -
- { - if (event.target.type === 'email') setEmail(event.target.value); - if (event.target.type === 'password') setPassword(event.target.value); - }} - loginFunc={async (event) => { - event.preventDefault(); - if (email && password) { - const { err } = await Firebase.signInEmailAndPassword( - email, - password, - ); - if (err) { - setErrorMessage(err.message); - setErrorNotifications(err.code); - } else history.replace('/dashboard'); - } - }} - /> -
- ); -} - -export default LoginPage; diff --git a/src/server/api/controllers/batches.controller.js b/src/server/api/controllers/batches.controller.js index 27e07aa8..151a6a6e 100644 --- a/src/server/api/controllers/batches.controller.js +++ b/src/server/api/controllers/batches.controller.js @@ -1,13 +1,16 @@ const knex = require('../../config/db'); const Error = require('../lib/utils/http-error'); +const moment = require('moment'); -const getBatches = async () => { +const getBatches = async (detailed) => { try { + if (detailed === 'true') return await getAllBatches(); return await knex('batches').select('id', 'customer_name'); } catch (error) { return error.message; } }; + const getBatchById = async (batchId) => { try { const batch = await knex('batches') @@ -21,7 +24,99 @@ const getBatchById = async (batchId) => { return error.message; } }; + +const currentStage = async (batchId) => { + try { + const batchesStages = await knex('batches') + .join('crop_stages', 'crop_stages.fk_crop_id', '=', 'batches.fk_crop_id') + .join('crops', 'crops.id', '=', 'crop_stages.fk_crop_id') + .select( + 'crop_stages.name', + 'crop_stages.duration', + 'batches.seeding_date', + ) + .where('batches.id', batchId); + if (batchesStages.length === 0) { + throw new Error(`incorrect entry with the id of ${batchId}`, 404); + } + /* eslint-disable no-param-reassign */ + batchesStages.reduce((sum, batch) => { + batch.duration += sum; + return batch.duration; + }, 0); + /* eslint-enable no-param-reassign */ + const today = moment(); + const seedingDate = moment(batchesStages[0].seeding_date); + if (seedingDate > today) return 'queued'; + const currentDay = today.diff(seedingDate, 'days') + 1; + const filterDurationLessThanToday = batchesStages.filter( + (batch) => batch.duration >= currentDay, + ); + const currentStageName = + filterDurationLessThanToday.length > 0 + ? filterDurationLessThanToday[0].name + : 'delivered'; + if (currentStage.length === 0) { + throw new Error( + `currentStage`, + `incorrect entry with the id of ${batchId}`, + 404, + ); + } + if (currentStageName === 'delivered' || currentStageName === 'queued') + return { stage: currentStageName, day: null }; + return { stage: currentStageName, day: currentDay }; + } catch (error) { + if (error instanceof Error) throw error; + console.error(error.message); + throw new Error('currentStage', 'Something went wrong on our side.', 500); + } +}; + +const getAllBatches = async () => { + try { + const batches = await knex('batches') + .join('crops', 'crops.id', '=', 'fk_crop_id') + .select( + 'batches.id', + 'crops.name', + 'crops.plant_variety', + 'batches.customer_name', + 'batches.number_of_seeded_pots', + 'batches.seeding_date', + ); + /* eslint-disable no-param-reassign */ + const addedCurrentStageStatus = await Promise.all( + batches.map(async (batch) => { + let stage = {}; + let status = ''; + if (moment(batch.seeding_date) > moment()) { + stage = { stage: 'queued', day: null }; + status = 'queued'; + } else { + stage = await currentStage(batch.id); + if (stage.stage === 'delivered') status = 'done'; + else status = 'active'; + } + batch.current_stage = stage; + batch.status = status; + return batch; + }), + ); + /* eslint-enable no-param-reassign */ + if (addedCurrentStageStatus.length === 0) { + throw new Error(`getAllBatches`, `incorrect entry`, 404); + } + return addedCurrentStageStatus; + } catch (error) { + if (error instanceof Error) throw error; + console.error(error.message); + throw new Error('getAllBatches', 'Something went wrong on our side.', 500); + } +}; + module.exports = { getBatches, getBatchById, + getAllBatches, }; diff --git a/src/server/api/routes/batches.router.js b/src/server/api/routes/batches.router.js index 6eb09f2c..ae76c995 100644 --- a/src/server/api/routes/batches.router.js +++ b/src/server/api/routes/batches.router.js @@ -9,19 +9,19 @@ const { // controllers const batchesController = require('../controllers/batches.controller'); -const batchController = require('../controllers/batches.controller'); -// ENDPOINT: /api/batches/ :GET to get all batches +// ENDPOINT: /api/batches/ :GET to get all batches - id, customer_name +// if /api/batches?detailed=true :GET to get all batches - id, name, plant_variety, customer_name, number_of_seeded_pots, seeding_date, current_stage, status router.get('/', (req, res, next) => { batchesController - .getBatches() + .getBatches(req.query.detailed) .then((result) => res.json(result)) .catch(next); }); -// ENDPOINT: /api/batch/:id :GET to get one module +// ENDPOINT: /api/batches/:id :GET to get specific batch by id router.get('/:batchId', checkIfAuthenticated, (req, res, next) => { - batchController + batchesController .getBatchById(req.params.batchId) .then((result) => res.json(result)) .catch(next); diff --git a/src/server/config/swagger.json b/src/server/config/swagger.json index 41010a93..a57b57b7 100644 --- a/src/server/config/swagger.json +++ b/src/server/config/swagger.json @@ -542,6 +542,39 @@ } } }, + "/batches/all": { + "get": { + "tags": ["all batches"], + "parameters": [ + { + "name": "authorization", + "in": "header", + "description": "Firebase token", + "required": true, + "type": "string", + "default": "Bearer ENTER_FIREBASE_TOKEN" + } + ], + "summary": "get list of all batches together with current stage and status", + "description": "get list of all batches together with current stage and status", + "operationId": "getAllBatches", + "produces": ["application/json"], + "responses": { + "200": { + "description": "Successful request" + }, + "401": { + "description": "Unauthorized request" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Server error" + } + } + } + }, "securityDefinitions": { "firebase_auth": { "type": "oauth2",