diff --git a/media-store/README.md b/media-store/README.md index 364e4173..80a39821 100644 --- a/media-store/README.md +++ b/media-store/README.md @@ -4,22 +4,33 @@ Welcome to your new project. It contains these folders and files, following our recommended project layout: -File or Folder | Purpose ----------|---------- -`app/` | Contains already bundled js code from [this repository](https://github.com/Dmitriynj/media-store-front) -`db/` | your domain models and data go here -`srv/` | your service models and code go here -`package.json` | project metadata and configuration -`readme.md` | this getting started guide +| File or Folder | Purpose | +| -------------- | ------------------------------------ | +| `app/` | Contains frontend app on react | +| `db/` | your domain models and data go here | +| `srv/` | your service models and code go here | +| `package.json` | project metadata and configuration | +| `readme.md` | this getting started guide | +## Start development steps -## Next Steps +- At first open a new terminal and run `npm run deploy`. It should create new sqlite source and fill initial data from `db/data`. You can browse database in any sqlite client +- Run `cds watch`. This will start cds service on 4004 port in watch mode +- Open `app` folder and run `npm run start`. This will start frontend dev server on 3000 port. It supports debug in chrome and hot reloading out of the box by create-react-app +- Now all things are done for development -- Open a new terminal and run `cds watch` -- (in VS Code simply choose _**Terminal** > Run Task > cds watch_) -- Start adding content, for example, a [db/schema.cds](db/schema.cds). -- To adjust UI simply clone [this](https://github.com/Dmitriynj/media-store-front) repo. Run `yarn start` for development. When you are done, run `yarn build` and copy all files from '/build' to the '/app' folder of the current repo. +## Deployment steps +- Make sure you already have hanatrial instance in your cockpit dashboard (SAP Cloud Platform). + Or if you are using hana instance - change it in mta.yaml config file from hanatrial to hana +- Replace `"kind": "sql"` with `"kind": "hana"` in package.json require section +- Make sure that current folder does not contain any previous `*.mtar` build file and `gen` folder + If exists - remove it +- Run `cf login` for Cloud Foundry authentication +- Open `app` folder and run `npm run build`. This will start create new production frontend bundles +- Run `mbt build -t ./`. This will create new build in `*.mtar` file +- Run `cf deploy <.mtar file>` # for example, media-store_1.0.0.mtar +- Now your services should be deployed with hanatrial instance and filled with initial data ## Learn More diff --git a/media-store/app/src/api/axiosInstance.js b/media-store/app/src/api/axiosInstance.js index abde261a..49fd0fa6 100644 --- a/media-store/app/src/api/axiosInstance.js +++ b/media-store/app/src/api/axiosInstance.js @@ -9,7 +9,7 @@ import { responseErrorInterceptor } from "./responseErrorInterceptor"; const axiosInstance = axios.create({ baseURL: API, - timeout: 1000, + timeout: 2000, }); const user = getUserFromLS(); const locale = getLocaleFromLS(); diff --git a/media-store/app/src/components/Editable.js b/media-store/app/src/components/Editable.js deleted file mode 100644 index 72624c5a..00000000 --- a/media-store/app/src/components/Editable.js +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useEffect, useRef } from "react"; - -const Editable = ({ value, onChange, type }) => { - const inputRef = useRef(); - - useEffect(() => { - const { current } = inputRef; - - current.value = value; - - const handleFocus = () => { - console.log("input is focussed"); - // current.disabled = false; - current.style.backgroundColor = "#f0f2f5"; - }; - const handleBlur = () => { - console.log("input is blurred"); - // current.disabled = true; - current.style.backgroundColor = "white"; - }; - - const handleInput = (e) => onChange(e.target.value); - - current.addEventListener("focus", handleFocus); - current.addEventListener("blur", handleBlur); - current.addEventListener("input", handleInput); - - return () => { - current.removeEventListener("focus", handleFocus); - current.removeEventListener("blur", handleBlur); - current.removeEventListener("input", handleInput); - }; - }); - - return ( - - ); -}; - -export { Editable }; diff --git a/media-store/app/src/contexts/AppStateContext.js b/media-store/app/src/contexts/AppStateContext.js index f2ea5eb2..e0029d6e 100644 --- a/media-store/app/src/contexts/AppStateContext.js +++ b/media-store/app/src/contexts/AppStateContext.js @@ -40,7 +40,6 @@ const AppStateContextProvider = ({ children }) => { setUser(newUser); }; emitter.on("UPDATE_USER", updateUser); - console.log("listener was registered"); return () => { emitter.removeListener("UPDATE_USER", updateUser); }; diff --git a/media-store/app/src/hooks/useErrors.js b/media-store/app/src/hooks/useErrors.js index 712ca779..086a5a04 100644 --- a/media-store/app/src/hooks/useErrors.js +++ b/media-store/app/src/hooks/useErrors.js @@ -19,7 +19,7 @@ const useErrors = () => { setError({ status: "", statusText: "Error", - message: "Something went wrong", + message: "Something went wrong. Seems like request is too long", }); } diff --git a/media-store/app/src/pages/Login.js b/media-store/app/src/pages/Login.js index a96de9d8..3efd5a6d 100644 --- a/media-store/app/src/pages/Login.js +++ b/media-store/app/src/pages/Login.js @@ -25,14 +25,17 @@ const tailLayout = { const Login = () => { const [form] = Form.useForm(); const history = useHistory(); - const { setLoading } = useAppState(); + const { setLoading, setInvoicedItems } = useAppState(); const { handleError } = useErrors(); const onFinish = (values) => { setLoading(true); login({ email: values.email, password: values.password }) - .then((response) => { - emitter.emit("UPDATE_USER", response.data); + .then(({ data: user }) => { + emitter.emit("UPDATE_USER", user); + if (user.roles.includes("employee")) { + setInvoicedItems([]); + } history.push("/"); }) .catch((error) => { diff --git a/media-store/app/src/pages/MyInvoicesPage.js b/media-store/app/src/pages/MyInvoicesPage.js index a1edf62c..ba8f2813 100644 --- a/media-store/app/src/pages/MyInvoicesPage.js +++ b/media-store/app/src/pages/MyInvoicesPage.js @@ -178,7 +178,6 @@ const MyInvoicesPage = () => { {invoiceElements && ( {invoiceElements} )} - {"Pagination steel needed"} ); }; diff --git a/media-store/app/src/pages/PersonPage.js b/media-store/app/src/pages/PersonPage.js index 102569d2..e941c4a6 100644 --- a/media-store/app/src/pages/PersonPage.js +++ b/media-store/app/src/pages/PersonPage.js @@ -64,17 +64,13 @@ const PersonPage = () => { }; const personProperties = map(Object.keys(person), (currentKey) => ( - - - +
+ + + +
)); - console.log(person, "person"); - return ( <> {person.lastName !== "" && ( diff --git a/media-store/app/src/pages/TracksPage.js b/media-store/app/src/pages/TracksPage.js index 7e0c6a7a..f3842535 100644 --- a/media-store/app/src/pages/TracksPage.js +++ b/media-store/app/src/pages/TracksPage.js @@ -10,8 +10,6 @@ import { useAbortableEffect } from "../hooks/useAbortableEffect"; import { requireEmployee } from "../util/constants"; import "./TracksPage.css"; -let counter = 0; - const { Search } = Input; const { Option } = Select; @@ -52,7 +50,6 @@ const TracksContainer = () => { const getTracksRequest = fetchTacks(); const getGenresReq = fetchGenres(); - console.log("calling requests", counter++); Promise.all([countTracksReq, getTracksRequest, getGenresReq]) .then( ([ diff --git a/media-store/app/src/pages/tracks/EditAction.js b/media-store/app/src/pages/tracks/EditAction.js index 2e960c26..a469c018 100644 --- a/media-store/app/src/pages/tracks/EditAction.js +++ b/media-store/app/src/pages/tracks/EditAction.js @@ -49,7 +49,6 @@ const EditAction = ({ }; const handleCancel = () => { - console.log("Clicked cancel button"); setVisible(false); }; diff --git a/media-store/srv/browse-invoices-service.js b/media-store/srv/browse-invoices-service.js index 0a65091c..ee669f96 100644 --- a/media-store/srv/browse-invoices-service.js +++ b/media-store/srv/browse-invoices-service.js @@ -23,7 +23,7 @@ module.exports = async function () { this.on("invoice", async (req) => { const { tracks } = req.data; - const trackIds = tracks.map(({ ID }) => ID); + const newInvoicedTracks = tracks.map(({ ID }) => ID); const customerId = req.user.attr.ID; const total = tracks.reduce( (acc, { unitPrice }) => acc + Number(unitPrice), @@ -33,6 +33,27 @@ module.exports = async function () { const transaction = await db.tx(req); + // check if already exists + const invoicedTracks = await transaction.run( + SELECT.from(InvoiceItems) + .columns("track_ID") + .where( + "invoice_ID in", + SELECT("ID").from(Invoices).where({ + customer_ID: req.user.attr.ID, + status: 1, + }) + ) + ); + const isNewInvoiceHasInvoicedTracks = invoicedTracks.some( + ({ track_ID: curID }) => newInvoicedTracks.includes(curID) + ); + if (isNewInvoiceHasInvoicedTracks) { + await transaction.rollback(); + req.reject(400, "Invoice contains already owned values"); + return; + } + // getting last ids for new records let { ID: lastInvoiceId } = await transaction.run( SELECT.one(Invoices).columns("ID").orderBy({ ID: "desc" }) diff --git a/media-store/srv/user-service.js b/media-store/srv/user-service.js index ef9c38eb..eb305111 100644 --- a/media-store/srv/user-service.js +++ b/media-store/srv/user-service.js @@ -3,8 +3,8 @@ const jwt = require("jsonwebtoken"); const bcrypt = require("bcryptjs"); const { ACCESS_TOKEN_SECRET, REFRESH_TOKEN_SECRET } = cds.env; -const ACCESS_TOKEN_EXP_IN = "10s"; -const REFRESH_TOKEN_EXPIRES_IN = "30s"; +const ACCESS_TOKEN_EXP_IN = "10m"; +const REFRESH_TOKEN_EXPIRES_IN = "20m"; const comparePasswords = async (password, hashedPassword) => { return new Promise((resolve, reject) =>