Skip to content

Commit

Permalink
ref(): update bootstrap ecosystem, reorganize components, update visu…
Browse files Browse the repository at this point in the history
…als to black and white theme
  • Loading branch information
lubarskyy committed Jul 21, 2022
1 parent 9bae813 commit d1f9696
Show file tree
Hide file tree
Showing 68 changed files with 516 additions and 554 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ We offer commercial support for Bootzooka and related technologies, as well as d

## Copyright

Copyright (C) 2013-2020 SoftwareMill [https://softwaremill.com](https://softwaremill.com).
Copyright (C) 2013-2022 SoftwareMill [https://softwaremill.com](https://softwaremill.com).
14 changes: 7 additions & 7 deletions docs/frontend.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: default
title: "Frontend application"
title: 'Frontend application'
---

Bootzooka's frontend is a true Single Page Application built with React. It can be treated as a completely separate application or as a client for Bootzooka server.
Expand All @@ -11,15 +11,15 @@ Please note, that the UI is based on [fantastic tool called Create React App](ht

## Installing Node.js & Yarn

To work with the `ui` module you need to have `node.js` installed in version 12.0 or newer. Make sure you have `node` command available on `PATH`.
To work with the `ui` module you need to have `node.js` installed in version 16.0 or newer. Make sure you have `node` command available on `PATH`.

As a package manager, Bootzooka's UI uses [Yarn](https://yarnpkg.com). Make sure to have it installed before the first run.

## First run

If this is your first attempt to run `ui`, please go to `ui` project and run

yarn install
yarn install

This will install all required dependencies for this project. If all is well you can start your development version of frontend by issuing `yarn start` from command line (or running the provided `frontend-start` script in the main directory). It should start your browser and point you to [Bootzooka home page](http://0.0.0.0:3000/#/).

Expand All @@ -29,10 +29,10 @@ Build system exposes several tasks that can be run, you can find them in `packag

The most important tasks exposed are:

* `yarn start`
* `yarn build`
* `yarn test`
* `yarn test:ci`
- `yarn start`
- `yarn build`
- `yarn test`
- `yarn test:ci`

## `yarn start` task

Expand Down
10 changes: 5 additions & 5 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
---
layout: default
title: "Getting started"
title: 'Getting started'
---

## Prerequisites

In order to build and develop on Bootzooka foundations you need the following:

* Java JDK >= 11
* [sbt](http://www.scala-sbt.org/) >= 1.2
* Node.js >= 10.0 (We recommend [nvm](https://github.com/creationix/nvm) - node version manager)
* PostgreSQL
- Java JDK >= 11
- [sbt](http://www.scala-sbt.org/) >= 1.2
- Node.js >= 16.0 (We recommend [nvm](https://github.com/creationix/nvm) - node version manager)
- PostgreSQL

## How to run

Expand Down
8 changes: 4 additions & 4 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@
"@types/react-router-dom": "^5.3.3",
"@types/yup": "^0.29.14",
"axios": "^0.27.2",
"bootstrap": "^4.5.2",
"bootstrap": "^5.2.0",
"eslint-config-prettier": "^8.5.0",
"formik": "^2.2.9",
"immer": "^9.0.15",
"prettier": "^2.7.1",
"react": "^17.0.0",
"react-bootstrap": "^1.3.0",
"react-bootstrap": "^2.4.0",
"react-dom": "^17.0.0",
"react-icons": "^3.11.0",
"react-router-bootstrap": "^0.25.0",
"react-icons": "^4.4.0",
"react-router-bootstrap": "^0.26.2",
"react-router-dom": "^6.3.0",
"react-scripts": "^4.0.0",
"react-use-promise-matcher": "^1.4.2",
Expand Down
3 changes: 1 addition & 2 deletions ui/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from "react";
import { render } from "@testing-library/react";
import App from "./App";
import { App } from "./App";

test("should render", () => {
const { getByText } = render(<App />);
Expand Down
8 changes: 3 additions & 5 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React from "react";
import { BrowserRouter } from "react-router-dom";
import Main from "./main/Main/Main";
import { UserContextProvider } from "./contexts/UserContext/UserContext";
import { Main } from "main";
import { UserContextProvider } from "contexts";

const App: React.FC = () => (
export const App: React.FC = () => (
<BrowserRouter>
<UserContextProvider>
<Main />
</UserContextProvider>
</BrowserRouter>
);

export default App;
File renamed without changes
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ interface ErrorMessageProps {
error: any;
}

const ErrorMessage: React.FC<ErrorMessageProps> = ({ error }) => (
export const ErrorMessage: React.FC<ErrorMessageProps> = ({ error }) => (
<span className="text-danger">{(error?.response?.data?.error || error?.message || "Unknown error").toString()}</span>
);

export default ErrorMessage;
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Form from "react-bootstrap/Form";
import { IconType } from "react-icons";
import { BsExclamationCircle, BsCheck } from "react-icons/bs";
import { PromiseResultShape } from "react-use-promise-matcher";
import ErrorMessage from "../ErrorMessage/ErrorMessage";
import useFormikValuesChanged from "./useFormikValuesChanged";
import { ErrorMessage } from "../";

interface FeedbackButtonProps extends ButtonProps {
label: string;
Expand All @@ -16,7 +16,7 @@ interface FeedbackButtonProps extends ButtonProps {
successLabel?: string;
}

const FeedbackButton: React.FC<FeedbackButtonProps> = ({
export const FeedbackButton: React.FC<FeedbackButtonProps> = ({
result,
clear,
label,
Expand All @@ -42,26 +42,24 @@ const FeedbackButton: React.FC<FeedbackButtonProps> = ({
</Button>
),
Rejected: (error) => (
<>
<div>
<Button {...buttonProps} variant="danger">
<BsExclamationCircle role="error" />
&nbsp;{label}
</Button>
<Form.Text className="d-inline-block mx-3">
<ErrorMessage error={error} />
</Form.Text>
</>
</div>
),
Resolved: () => (
<>
<div>
<Button {...buttonProps} variant="success">
<BsCheck role="success" />
&nbsp;{label}
</Button>
<Form.Text className="text-success d-inline-block mx-3">{successLabel}</Form.Text>
</>
</div>
),
});
};

export default FeedbackButton;
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ interface FormikInputProps {
label: string;
}

const FormikInput: React.FC<FormikInputProps> = ({ type = "text", name, label }) => (
export const FormikInput: React.FC<FormikInputProps> = ({ type = "text", name, label }) => (
<Field name={name}>
{({ field, meta }: FieldProps<string>) => (
<Form.Group as={Row}>
<Form.Group className="mb-4" as={Row}>
<Form.Label column sm={3} htmlFor={name}>
{label}
</Form.Label>
Expand All @@ -25,13 +25,11 @@ const FormikInput: React.FC<FormikInputProps> = ({ type = "text", name, label })
isInvalid={!!meta.error && meta.touched}
{...field}
/>
<Form.Control.Feedback type="invalid" className="text-right">
<Form.Control.Feedback type="invalid" className="text-end">
{meta.error}
</Form.Control.Feedback>
</Col>
</Form.Group>
)}
</Field>
);

export default FormikInput;
25 changes: 25 additions & 0 deletions ui/src/components/TwoColumnHero/TwoColumnHero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Container from "react-bootstrap/Container";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import Fade from "react-bootstrap/Fade";
import Image from "react-bootstrap/Image";
import logo from "assets/sml-logo-vertical-white-all-trans.png";

export const TwoColumnHero: React.FC = ({ children }) => {
return (
<Container className="h-100">
<Row className="h-100">
<Col xs={12} xl={6} className="bg-dark d-flex justify-content-center align-items-center">
<Image src={logo} fluid alt="SoftwareMill logotype" />
</Col>
<Col xs={12} xl={6}>
<Fade className="h-100" appear in>
<Container className="bg-light d-flex flex-column justify-content-center align-items-center px-5">
{children}
</Container>
</Fade>
</Col>
</Row>
</Container>
);
};
4 changes: 4 additions & 0 deletions ui/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./ErrorMessage/ErrorMessage";
export * from "./FeedbackButton/FeedbackButton";
export * from "./FormikInput/FormikInput";
export * from "./TwoColumnHero/TwoColumnHero";
12 changes: 10 additions & 2 deletions ui/src/contexts/UserContext/UserContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,34 @@ export type UserAction =
| { type: "LOG_IN"; user: UserDetails }
| { type: "LOG_OUT" };

const UserReducer = (state: UserState, action: UserAction): UserState => {
const userReducer = (state: UserState, action: UserAction): UserState => {
switch (action.type) {
case "SET_API_KEY":
return immer(state, (draftState) => {
draftState.apiKey = action.apiKey;
});

case "UPDATE_USER_DATA":
return immer(state, (draftState) => {
if (!draftState.user) return;
draftState.user = { ...draftState.user, ...action.user };
});

case "LOG_IN":
return immer(state, (draftState) => {
draftState.user = action.user;
draftState.loggedIn = true;
});

case "LOG_OUT":
return immer(state, (draftState) => {
draftState.apiKey = null;
draftState.user = null;
draftState.loggedIn = false;
});

default:
return state;
}
};

Expand All @@ -59,7 +65,9 @@ export const UserContext = React.createContext<{
});

export const UserContextProvider: React.FC = ({ children }) => {
const [state, dispatch] = React.useReducer(UserReducer, initialUserState);
const [state, dispatch] = React.useReducer(userReducer, initialUserState);

return <UserContext.Provider value={{ state, dispatch }}>{children}</UserContext.Provider>;
};

export const useUserContext = () => React.useContext(UserContext);
1 change: 1 addition & 0 deletions ui/src/contexts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./UserContext/UserContext";
5 changes: 5 additions & 0 deletions ui/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
html,
body,
#root {
height: 100%;
}
4 changes: 2 additions & 2 deletions ui/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { App } from "./App";
import * as serviceWorker from "./serviceWorker";
import "bootstrap/dist/css/bootstrap.min.css";
import "./index.css";

ReactDOM.render(<App />, document.getElementById("root"));

Expand Down
36 changes: 3 additions & 33 deletions ui/src/main/Footer/Footer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from "react";
import { render } from "@testing-library/react";
import Footer from "./Footer";
import versionService from "../../services/VersionService/VersionService";
import { Footer } from "./Footer";
import { versionService } from "services";

jest.mock("../../services/VersionService/VersionService");
jest.mock("services");

beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -18,38 +17,9 @@ test("renders version data", async () => {

await findAllByRole("loader");

const buildDate = await findByText(/testDate/i);
const buildSha = await findByText(/testSha/i);

expect(versionService.getVersion).toBeCalledWith();
expect(info).toBeInTheDocument();
expect(buildDate).toBeInTheDocument();
expect(buildSha).toBeInTheDocument();
});

test("catches error of version data", async () => {
const testError = new Error("Test Error");
(versionService.getVersion as jest.Mock).mockRejectedValueOnce(testError);

const { getByText, findAllByRole } = render(<Footer />);

const info = getByText(/Bootzooka - application scaffolding by /);

await findAllByRole("loader");

expect(info).toBeInTheDocument();
expect(getByText("Test Error")).toBeInTheDocument();
});

test("catches undefined error of version data", async () => {
(versionService.getVersion as jest.Mock).mockRejectedValueOnce(undefined);

const { getByText, findAllByRole } = render(<Footer />);

const info = getByText(/Bootzooka - application scaffolding by /);

await findAllByRole("loader");

expect(info).toBeInTheDocument();
expect(getByText("Unknown error")).toBeInTheDocument();
});
Loading

0 comments on commit d1f9696

Please sign in to comment.