Skip to content

Commit 5387984

Browse files
authored
Refactoring/frontend recoil mvvm (#90)
* Added recoil and major refactoring * Fixed add todo * Added css * Fixed login logout * Working version * Completed TodoViewModel with Recoil integration * Fixed all issues and added eslint
1 parent 1a75c2f commit 5387984

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+9264
-9210
lines changed

frontend/.eslintignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
*/proto/*
2+
/src/bitloops/proto/*
3+
*/src/bitloops/proto/*
4+
/src/bitloops/proto/todo.ts
5+
*/src/bitloops/proto/todo.ts
6+
/src/bitloops/proto/todo_pb.d.ts
7+
*/src/bitloops/proto/todo_pb.d.ts
8+
/src/bitloops/proto/TodoServiceClient_Pb.ts
9+
*/src/bitloops/proto/TodoServiceClient_Pb.ts

frontend/.eslintrc.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"settings": {
3+
"import/resolver": {
4+
"node": {
5+
"extensions": [".js", ".jsx", ".ts", ".tsx"]
6+
}
7+
}
8+
},
9+
"env": {
10+
"browser": true,
11+
"es2021": true
12+
},
13+
"extends": [
14+
"airbnb",
15+
"airbnb-typescript",
16+
"plugin:react/recommended",
17+
"plugin:jsx-a11y/recommended",
18+
"prettier"
19+
],
20+
"overrides": [
21+
{
22+
"extends": [],
23+
"files": [
24+
"*.ts",
25+
"*.tsx"
26+
]
27+
}
28+
],
29+
"parserOptions": {
30+
"ecmaVersion": "latest",
31+
"sourceType": "module",
32+
"project": "./tsconfig.json"
33+
},
34+
"plugins": [
35+
"react",
36+
"jsx-a11y",
37+
"react-hooks",
38+
"@typescript-eslint",
39+
"prettier"
40+
],
41+
"rules": {
42+
"prettier/prettier": "error",
43+
"no-shadow": "off",
44+
"@typescript-eslint/no-shadow": ["error"],
45+
"react/function-component-definition": "off",
46+
"react/jsx-filename-extension": [1, { "extensions": [".tsx", ".ts"] }],
47+
"react/react-in-jsx-scope": "off",
48+
"class-methods-use-this": "off",
49+
"import/extensions": [
50+
"error",
51+
"ignorePackages",
52+
{
53+
"ts": "never",
54+
"tsx": "never",
55+
"js": "never",
56+
"jsx": "never"
57+
}
58+
],
59+
"quotes": ["error", "single"],
60+
"@typescript-eslint/quotes": ["error", "single"],
61+
"no-underscore-dangle": "off"
62+
}
63+
}

frontend/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# dependencies
44
/node_modules
5+
node_modules
56
/.pnp
67
.pnp.js
78

frontend/.prettierignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
*/proto/*
2+
/src/bitloops/proto/*
3+
*/src/bitloops/proto/*
4+
/src/bitloops/proto/todo.ts
5+
*/src/bitloops/proto/todo.ts
6+
/src/bitloops/proto/todo_pb.d.ts
7+
*/src/bitloops/proto/todo_pb.d.ts
8+
/src/bitloops/proto/TodoServiceClient_Pb.ts
9+
*/src/bitloops/proto/TodoServiceClient_Pb.ts

frontend/.prettierrc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
{
2-
"singleQuote": true
2+
"singleQuote": true,
3+
"printWidth": 100,
4+
"tabWidth": 2,
5+
"useTabs": false,
6+
"semi": true,
7+
"trailingComma": "es5"
38
}

frontend/.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"editor.codeActionsOnSave": {
3+
"source.fixAll.eslint": true
4+
},
5+
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
6+
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
7+
}

frontend/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:19-alpine
1+
FROM node:19-alpine
22
# Set the working directory to /app inside the container
33
WORKDIR /app
44
# Copy app files

frontend/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,34 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
44

55
You should not need to run it directly from this folder as it is part of the docker build file but if you want to run it locally for development you can follow the instructions below at "Available Scripts".
66

7+
## Web App Design
8+
9+
The web app is using a flavour of the MVVM (Model-View-View Model) pattern. You should be aware of the following ideas:
10+
11+
### Components
12+
13+
Here we have simple React code combined with imports of CSS files for the formatting. These components do not have state and you could inject anything you like through the props to test them.
14+
15+
### Controllers
16+
17+
A controller wraps a Component and maps the functions and values coming from a View Model (see below). You might have the occasional useState for maybe an [open, setOpen] value but nothing more.
18+
19+
### State
20+
21+
For state Recoil is being used but the state interacts with the ViewModel and should not be directly accessed or mutated from the Controllers with the exception of the App.tsx file that sets the setters and getters in the View Models.
22+
23+
### View Models
24+
25+
A View Model is a class that has a clearly defined interface that provides controllers with all the necessary functions and values to feed to their Components. We are utilizing the concept of CQRS meaning that we have separate functions that cause side-effects (aka mutations) and separate values that work like queries.
26+
27+
### Repositories
28+
29+
Repositories are used to interact with the app state (e.g. localStorage) and the Services (see below). For example, a service expects some authentication metadata (JWT) with the requests and instead of complicating the ViewModel with these details, a Repository provides a cleaner interface to the ViewModel for using the Services by taking care of the JWT injection etc. You can also use Repositories to deal with local caching etc.
30+
31+
### Services
32+
33+
Services are the gRPC endpoints that are automatically generated by the protobuf file and you could also add other services to wrap 3rd party API calls (e.g. Google Maps API).
34+
735
## Technologies Used
836

937
To communicate with the backend, grpc-web is being used for the main functionality while a couple REST requests are used for registration and login.

frontend/package.json

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
{
22
"name": "todo-frontend",
3-
"version": "0.1.1",
3+
"version": "0.2.0",
44
"private": true,
55
"dependencies": {
6-
"axios": "^1.3.5",
6+
"@chakra-ui/icons": "^2.1.0",
7+
"@chakra-ui/react": "^2.8.0",
8+
"@chakra-ui/system": "^2.6.0",
9+
"@emotion/react": "^11.11.1",
10+
"@emotion/styled": "^11.11.0",
11+
"@grpc/grpc-js": "^1.8.21",
12+
"eventemitter3": "^5.0.1",
13+
"framer-motion": "^10.15.0",
714
"google-protobuf": "^3.21.2",
815
"grpc-web": "^1.4.2",
16+
"jwt-decode": "^3.1.2",
917
"react": "^18.2.0",
1018
"react-dom": "^18.2.0",
11-
"react-icons": "^4.8.0"
19+
"react-icons": "^4.8.0",
20+
"react-router-dom": "^6.14.2",
21+
"recoil": "^0.7.7",
22+
"source-map-loader": "^4.0.1"
1223
},
1324
"scripts": {
1425
"start": "react-scripts start",
1526
"build": "react-scripts build",
1627
"test": "react-scripts test",
1728
"eject": "react-scripts eject",
18-
"proto": "protoc -I=. src/bitloops/proto/todo.proto --js_out=import_style=commonjs,binary:. --grpc-web_out=import_style=typescript,mode=grpcwebtext:.",
29+
"proto": "protoc -I=. src/bitloops/proto/todo.proto --ts_out=. --ts_opt=target=web,json_names,unary_rpc_promise=true,no_namespace --grpc-web_out=import_style=typescript,mode=grpcwebtext:.",
1930
"docker:build": "docker build -t todo-frontend .",
2031
"docker:run": "docker run -dp 3000:3000 --name todo-frontend todo-frontend",
21-
"docker": "docker build -t todo-frontend . && docker run -dp 3000:3000 --name todo-frontend todo-frontend"
32+
"docker": "docker build -t todo-frontend . && docker run -dp 3000:3000 --name todo-frontend todo-frontend",
33+
"postinstall": "patch-package",
34+
"lint": "eslint ."
2235
},
2336
"eslintConfig": {
2437
"extends": [
@@ -39,16 +52,37 @@
3952
]
4053
},
4154
"devDependencies": {
42-
"@testing-library/jest-dom": "^5.16.5",
43-
"@testing-library/react": "^14.0.0",
44-
"@testing-library/user-event": "^14.4.3",
45-
"@types/jest": "^29.5.0",
46-
"@types/node": "^18.15.11",
47-
"@types/react": "^18.0.34",
48-
"@types/react-dom": "^18.0.11",
49-
"protoc-gen-grpc-web": "^1.4.1",
55+
"@testing-library/jest-dom": "^5.14.1",
56+
"@testing-library/react": "^13.0.0",
57+
"@testing-library/user-event": "^13.2.1",
58+
"@types/jest": "^27.0.1",
59+
"@types/node": "^16.7.13",
60+
"@types/react": "^18.0.0",
61+
"@types/react-dom": "^18.0.0",
62+
63+
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
64+
"@typescript-eslint/eslint-plugin": ">=6.0.0",
65+
"@typescript-eslint/parser": ">=6.0.0",
66+
"eslint": ">=8.0.0",
67+
"eslint-config-airbnb": "^19.0.4",
68+
"eslint-config-airbnb-typescript": "^17.1.0",
69+
"eslint-config-prettier": "^8.9.0",
70+
"eslint-config-xo": "^0.43.1",
71+
"eslint-config-xo-typescript": "^1.0.1",
72+
"eslint-plugin-import": "^2.28.0",
73+
"eslint-plugin-prettier": "^5.0.0",
74+
"eslint-plugin-react": "^7.33.1",
75+
"eslint-plugin-jsx-a11y": "^6.7.1",
76+
"eslint-plugin-react-hooks": "^4.6.0",
77+
"prettier": "^3.0.0",
78+
5079
"react-scripts": "5.0.1",
51-
"typescript": "^5.0.4",
52-
"web-vitals": "^3.3.1"
80+
"typescript": ">=4.7",
81+
"web-vitals": "^2.1.0",
82+
83+
"@types/google-protobuf": "^3.15.6",
84+
"patch-package": "^8.0.0",
85+
"postinstall-postinstall": "^2.1.0",
86+
"protoc-gen-grpc-web": "^1.4.2"
5387
}
5488
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
diff --git a/node_modules/grpc-web/index.d.ts b/node_modules/grpc-web/index.d.ts
2+
index 09fb671..9b6b233 100644
3+
--- a/node_modules/grpc-web/index.d.ts
4+
+++ b/node_modules/grpc-web/index.d.ts
5+
@@ -71,8 +71,8 @@ declare module "grpc-web" {
6+
export class MethodDescriptor<REQ, RESP> {
7+
constructor(name: string,
8+
methodType: string,
9+
- requestType: new (...args: unknown[]) => REQ,
10+
- responseType: new (...args: unknown[]) => RESP,
11+
+ requestType: new (...args: any) => REQ,
12+
+ responseType: new (...args: any) => RESP,
13+
requestSerializeFn: any,
14+
responseDeserializeFn: any);
15+
getName(): string;

0 commit comments

Comments
 (0)