Add eslint to redaktionsapp
This commit is contained in:
parent
77ef07d9ff
commit
8afea80dd2
14
backend/deep_reset_db.sh
Executable file
14
backend/deep_reset_db.sh
Executable 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
|
19
redaktions-app/.eslintrc.yml
Normal file
19
redaktions-app/.eslintrc.yml
Normal 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
|
2520
redaktions-app/package-lock.json
generated
2520
redaktions-app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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": [
|
||||
|
|
|
@ -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
|
||||
) : (
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -46,7 +46,7 @@ interface AccordionQuestionAnswerProps {
|
|||
|
||||
export default function AccordionQuestionAnswer(
|
||||
props: AccordionQuestionAnswerProps
|
||||
) {
|
||||
): React.ReactElement {
|
||||
const {
|
||||
rowId: questionRowId,
|
||||
title: questionTitle,
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -11,7 +11,7 @@ interface CategorySelectionMenuProps {
|
|||
|
||||
export default function CategorySelectionMenu(
|
||||
props: CategorySelectionMenuProps
|
||||
) {
|
||||
): React.ReactElement {
|
||||
const onCategoryIdChange = (
|
||||
e: ChangeEvent<{ name?: string; value: unknown }>
|
||||
) => {
|
||||
|
|
|
@ -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 © "}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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("");
|
||||
|
|
|
@ -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("");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}));
|
||||
|
||||
function Main() {
|
||||
function Main(): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const getMainPage = () => {
|
||||
const jwt = getJsonWebToken();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -13,7 +13,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}));
|
||||
|
||||
export function MainPageEditor() {
|
||||
export function MainPageEditor(): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
|
|
|
@ -10,7 +10,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}));
|
||||
|
||||
export function MainPageUser() {
|
||||
export function MainPageUser(): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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("");
|
||||
|
|
|
@ -29,7 +29,7 @@ interface ToggleButtonGroupAnswerPositionProps {
|
|||
|
||||
export default function ToggleButtonGroupAnswerPosition(
|
||||
props: ToggleButtonGroupAnswerPositionProps
|
||||
) {
|
||||
): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Loading…
Reference in a new issue