Add eslint to redaktionsapp

This commit is contained in:
Christoph Lienhard 2021-01-30 16:12:10 +01:00
parent 77ef07d9ff
commit 8afea80dd2
Signed by: christoph.lienhard
GPG Key ID: 6B98870DDC270884
34 changed files with 1582 additions and 1075 deletions

14
backend/deep_reset_db.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
docker-compose stop postgres
CONTAINER=$(docker image rm candymat-postgres:11.5 2> >(grep -P '[a-f0-9]{12}' -o) | head -1)
echo "Going to remove container: $CONTAINER"
docker container rm $CONTAINER
docker image rm candymat-postgres:11.5
echo "Deleting db-data docker volumes ..."
VOLUMES=$(docker volume ls -q | grep "db-data")
for volume in ${VOLUMES[@]}; do
echo "Deleting volume '$volume'"
docker volume rm $volume
done;
docker-compose up --build postgres

View File

@ -0,0 +1,19 @@
env:
browser: true
extends:
- 'eslint:recommended'
- 'plugin:react/recommended'
- 'plugin:@typescript-eslint/recommended'
parser: '@typescript-eslint/parser'
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: 12
sourceType: module
plugins:
- react
- '@typescript-eslint'
rules: {}
settings:
react:
version: detect

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,10 @@
"@types/react": "^16.9.46",
"@types/react-dom": "^16.9.8",
"@types/react-router-dom": "^5.1.5",
"@typescript-eslint/eslint-plugin": "^4.12.0",
"@typescript-eslint/parser": "^4.12.0",
"eslint-config-prettier": "^7.1.0",
"eslint-plugin-react": "^7.22.0",
"husky": "^4.3.6",
"jest-environment-jsdom-sixteen": "^1.0.3",
"lint-staged": "^10.5.3",
@ -44,8 +48,10 @@
}
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown",
"*.{js,css,md}": "prettier --write"
"*.{js,jsx,ts,tsx}": [
"eslint",
"prettier --write"
]
},
"browserslist": {
"production": [

View File

@ -1,11 +1,11 @@
import "./App.css";
import React from "react";
import Main from "./components/Main";
import { Redirect, Route, Switch } from "react-router-dom";
import { Redirect, Route, RouteProps, Switch } from "react-router-dom";
import SignIn from "./components/SignIn";
import SignUp from "./components/SignUp";
function App() {
function App(): React.ReactElement {
return (
<Switch>
<PrivateRoute exact path={"/"}>
@ -21,10 +21,9 @@ function App() {
);
}
export const isLoggedIn = () => !!localStorage.getItem("token");
export const isLoggedIn = (): boolean => !!localStorage.getItem("token");
// @ts-ignore
function PrivateRoute({ children, ...rest }) {
function PrivateRoute({ children, ...rest }: RouteProps) {
return (
<Route
{...rest}
@ -44,12 +43,11 @@ function PrivateRoute({ children, ...rest }) {
);
}
// @ts-ignore
function NotLoggedInOnlyRoute({ children, ...rest }) {
function NotLoggedInOnlyRoute({ children, ...rest }: RouteProps) {
return (
<Route
{...rest}
render={({ location }) =>
render={() =>
!isLoggedIn() ? (
children
) : (

View File

@ -121,9 +121,9 @@ const addAnswerToRootField = (
cache.modify({
fields: {
answerByQuestionRowIdAndPersonRowId: (
answerRefs: Reference | StoreObject | null = null,
answerRefs: Reference | StoreObject | null,
{ storeFieldName }
): Reference | StoreObject | void => {
) => {
if (matchesStoreFieldName(storeFieldName, personRowId, questionRowId)) {
return newAnswerRef;
}
@ -147,7 +147,7 @@ export const updateCacheAfterAddingAnswer = (
cache: ApolloCache<AddAnswerResponse>,
{ data }: FetchResult<AddAnswerResponse>,
question: QuestionAnswerResponse
) => {
): void => {
const answer = data?.createAnswer?.answer;
if (answer) {
const newAnswerRef = writeAnswerToCache(cache, answer);

View File

@ -46,7 +46,7 @@ interface AccordionQuestionAnswerProps {
export default function AccordionQuestionAnswer(
props: AccordionQuestionAnswerProps
) {
): React.ReactElement {
const {
rowId: questionRowId,
title: questionTitle,

View File

@ -40,7 +40,9 @@ interface AccordionWithEditProps {
onDeleteButtonClick?(): void;
}
export default function AccordionWithEdit(props: AccordionWithEditProps) {
export default function AccordionWithEdit(
props: AccordionWithEditProps
): React.ReactElement {
const classes = useStyles();
return (

View File

@ -22,7 +22,7 @@ interface AddCardProps {
handleClick?(): void;
}
export default function AddCard(props: AddCardProps) {
export default function AddCard(props: AddCardProps): React.ReactElement {
const classes = useStyles();
return (

View File

@ -36,7 +36,9 @@ interface ButtonWithSpinnerProps {
color?: PropTypes.Color;
}
export default function ButtonWithSpinner(props: ButtonWithSpinnerProps) {
export default function ButtonWithSpinner(
props: ButtonWithSpinnerProps
): React.ReactElement {
const classes = useStyles();
return (

View File

@ -33,7 +33,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export function CandidatePositionLegend() {
export function CandidatePositionLegend(): React.ReactElement {
const classes = useStyles();
const getChip = (position: CandidatePosition, legendText: string) => {

View File

@ -27,7 +27,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function CategoryList() {
export default function CategoryList(): React.ReactElement {
const categories = useQuery<GetAllCategoriesResponse, null>(
GET_ALL_CATEGORIES
).data?.allCategories.nodes;

View File

@ -11,7 +11,7 @@ interface CategorySelectionMenuProps {
export default function CategorySelectionMenu(
props: CategorySelectionMenuProps
) {
): React.ReactElement {
const onCategoryIdChange = (
e: ChangeEvent<{ name?: string; value: unknown }>
) => {

View File

@ -2,7 +2,7 @@ import Typography from "@material-ui/core/Typography";
import Link from "@material-ui/core/Link";
import React from "react";
export function Copyright() {
export function Copyright(): React.ReactElement {
return (
<Typography variant="body2" color="textSecondary" align="center">
{"Copyright © "}

View File

@ -16,7 +16,7 @@ const useStyles = makeStyles({
},
});
function CustomAppBar() {
function CustomAppBar(): React.ReactElement {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);

View File

@ -11,7 +11,9 @@ interface DialogSimpleActionProps {
onConfirmButtonClick(): void;
}
export function DialogActionBar(props: DialogSimpleActionProps) {
export function DialogActionBar(
props: DialogSimpleActionProps
): React.ReactElement {
return (
<DialogActions>
<Button onClick={props.onClose} color="primary">

View File

@ -25,7 +25,7 @@ import {
export const dialogChangeCategoryId = makeVar<string>("");
export const dialogChangeCategoryOpen = makeVar<boolean>(false);
export default function DialogChangeCategory() {
export default function DialogChangeCategory(): React.ReactElement {
const [addMode, setAddMode] = useState(true);
const [title, setTitle] = useState("");
const [details, setDetails] = useState("");

View File

@ -30,7 +30,7 @@ import {
export const dialogChangeQuestionId = makeVar<string>("");
export const dialogChangeQuestionOpen = makeVar<boolean>(false);
export default function DialogChangeQuestion() {
export default function DialogChangeQuestion(): React.ReactElement {
const [addMode, setAddMode] = useState(true);
const [title, setTitle] = useState("");
const [details, setDetails] = useState("");

View File

@ -17,7 +17,7 @@ export const dialogDeleteCategoryId = makeVar<string>("");
export const dialogDeleteCategoryTitle = makeVar<string>("");
export const dialogDeleteCategoryOpen = makeVar<boolean>(false);
export default function DialogDeleteCategory() {
export default function DialogDeleteCategory(): React.ReactElement {
const { enqueueSnackbar } = useSnackbar();
const [deleteCategory, { loading }] = useMutation<
DeleteCategoryResponse,

View File

@ -17,7 +17,7 @@ export const dialogDeleteQuestionId = makeVar<string>("");
export const dialogDeleteQuestionTitle = makeVar<string>("");
export const dialogDeleteQuestionOpen = makeVar<boolean>(false);
export default function DialogDeleteQuestion() {
export default function DialogDeleteQuestion(): React.ReactElement {
const { enqueueSnackbar } = useSnackbar();
const [deleteQuestion, { loading }] = useMutation<
DeleteQuestionResponse,

View File

@ -17,7 +17,9 @@ interface DialogSimpleProps {
onClose(): void;
}
export default function DialogSimple(props: DialogSimpleProps) {
export default function DialogSimple(
props: DialogSimpleProps
): React.ReactElement {
return (
<Dialog
open={props.open}

View File

@ -17,7 +17,9 @@ interface DialogTitleAndDetailsProps {
onDetailsChange(newDetails: string): void;
}
export function DialogTitleAndDetails(props: DialogTitleAndDetailsProps) {
export function DialogTitleAndDetails(
props: DialogTitleAndDetailsProps
): React.ReactElement {
const classes = useStyles();
return (

View File

@ -26,7 +26,9 @@ interface EditAnswerSectionProps {
question: QuestionAnswerResponse;
}
export default function EditAnswerSection(props: EditAnswerSectionProps) {
export default function EditAnswerSection(
props: EditAnswerSectionProps
): React.ReactElement {
const { enqueueSnackbar } = useSnackbar();
const { data } = useQuery<
GetAnswerByQuestionAndPersonResponse,

View File

@ -31,7 +31,9 @@ interface EditAnswerTextSectionProps {
onSaveClick(text: string): void;
}
export default function EditAnswerText(props: EditAnswerTextSectionProps) {
export default function EditAnswerText(
props: EditAnswerTextSectionProps
): React.ReactElement {
const classes = useStyles();
const [answerText, setAnswerText] = useState<string>(props.remoteText);

View File

@ -21,7 +21,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
function Main() {
function Main(): React.ReactElement {
const classes = useStyles();
const getMainPage = () => {
const jwt = getJsonWebToken();

View File

@ -29,7 +29,9 @@ interface MainPageCandidateProps {
personRowId: number;
}
export function MainPageCandidate(props: MainPageCandidateProps) {
export function MainPageCandidate(
props: MainPageCandidateProps
): React.ReactElement {
const personRowId = getJsonWebToken()?.person_row_id;
const questionAnswers = useQuery<
GetAllQuestionAnswersResponse,

View File

@ -13,7 +13,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export function MainPageEditor() {
export function MainPageEditor(): React.ReactElement {
const classes = useStyles();
return (

View File

@ -10,7 +10,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export function MainPageUser() {
export function MainPageUser(): React.ReactElement {
const classes = useStyles();
return (

View File

@ -27,7 +27,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function QuestionList() {
export default function QuestionList(): React.ReactElement {
const questions = useQuery<GetAllQuestionsResponse, null>(GET_ALL_QUESTIONS)
.data?.allQuestions.nodes;
const classes = useStyles();

View File

@ -44,7 +44,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function SignIn() {
export default function SignIn(): React.ReactElement {
const history = useHistory();
const queryParams = new URLSearchParams(useLocation().search);
const classes = useStyles();

View File

@ -46,7 +46,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function SignUp() {
export default function SignUp(): React.ReactElement {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [firstName, setFirstName] = useState("");

View File

@ -29,7 +29,7 @@ interface ToggleButtonGroupAnswerPositionProps {
export default function ToggleButtonGroupAnswerPosition(
props: ToggleButtonGroupAnswerPositionProps
) {
): React.ReactElement {
const classes = useStyles();
return (

View File

@ -11,7 +11,7 @@ import DeleteIcon from "@material-ui/icons/Delete";
import AddIcon from "@material-ui/icons/Add";
const memoizedGetIconPath = (icon: JSX.Element) => {
let cache: { path?: string } = {};
const cache: { path?: string } = {};
return (): string => {
if (cache?.path) {
return cache.path;

View File

@ -25,7 +25,7 @@ type Config = {
onUpdate?: (registration: ServiceWorkerRegistration) => void;
};
export function register(config?: Config) {
export function register(config?: Config): void {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
@ -133,7 +133,7 @@ function checkValidServiceWorker(swUrl: string, config?: Config) {
});
}
export function unregister() {
export function unregister(): void {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready
.then((registration) => {