Skip to content

Commit

Permalink
add error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
JensRavens committed Jan 16, 2025
1 parent bce687a commit e86a238
Show file tree
Hide file tree
Showing 17 changed files with 462 additions and 195 deletions.
23 changes: 23 additions & 0 deletions app/frontend/components/errors/errors.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.errors {
position: fixed;
top: 5px;
right: 5px;
z-index: 1000;

&__notification {
position: relative;
background-color: #f44336;
color: #fff;
padding: 10px 20px;
border-radius: 5px;
margin-bottom: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}

&__notification-close {
position: absolute;
top: 0;
right: 0;
cursor: pointer;
}
}
28 changes: 28 additions & 0 deletions app/frontend/components/errors/errors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { useErrors } from '../../util/errors';
import { Stack } from '../stack/stack';
import './errors.scss';

export function Errors(): JSX.Element {
const errors = useErrors();
return (
<div className="errors">
<Stack size={4}>
{errors.map((e) => (
<div className="errors__notification" key={e.message}>
<button
className="errors__notification-close"
onClick={(event) => {
event.preventDefault();
e.confirm();
}}
>
X
</button>
{e.message}
</div>
))}
</Stack>
</div>
);
}
6 changes: 5 additions & 1 deletion app/frontend/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ModalWrapper } from './components/modal/modal';
import { Errors } from './components/errors/errors';
import React, { ReactNode } from 'react';
import { loadI18n } from './util/i18n';
import { Formatter } from './util/formatter';
Expand All @@ -15,7 +16,10 @@ const formatter = new Formatter({ locale });
export function Layout({ children }: Props): JSX.Element {
return (
<DependenciesProvider i18n={i18n} formatter={formatter}>
<ModalWrapper>{children}</ModalWrapper>
<>
<Errors />
<ModalWrapper>{children}</ModalWrapper>
</>
</DependenciesProvider>
);
}
6 changes: 6 additions & 0 deletions app/frontend/util/basic_hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useCallback, useState } from 'react';

export function useRefresh(): () => void {
const [, setUpdate] = useState(0);
return useCallback(() => setUpdate((u) => u + 1), [setUpdate]);
}
59 changes: 59 additions & 0 deletions app/frontend/util/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useEffect } from 'react';
import { useRefresh } from './basic_hooks';

interface DisplayedError {
message: string;
error: unknown;
confirm: () => void;
}

class ErrorManager {
static _instance: ErrorManager;

static get instance() {
if (!this._instance) {
this._instance = new ErrorManager();
}
return this._instance;
}

errors: DisplayedError[] = [];
listeners: (() => void)[] = [];

handleError(error: unknown) {
const message = error instanceof Error ? error.message : String(error);
const displayedError: DisplayedError = {
message,
error,
confirm: () => {},
};
displayedError.confirm = () => {
this.errors = this.errors.filter((e) => e !== displayedError);
this.listeners.forEach((listener) => listener());
};
this.errors.push(displayedError);
this.listeners.forEach((listener) => listener());
}

addListener(listener: () => void) {
this.listeners.push(listener);
}

removeListener(listener: () => void) {
this.listeners = this.listeners.filter((l) => l !== listener);
}
}

export function useErrors(): DisplayedError[] {
const refresh = useRefresh();
const errors = ErrorManager.instance.errors;
useEffect(() => {
ErrorManager.instance.addListener(refresh);
return () => ErrorManager.instance.removeListener(refresh);
}, []);
return errors;
}

export function handleError(error: unknown) {
ErrorManager.instance.handleError(error);
}
1 change: 1 addition & 0 deletions app/views/components/_head.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ head

/ Assets
= vite_client_tag
= vite_react_refresh_tag
= vite_typescript_tag "application"

= yield :additional_head_tags if content_for? :additional_head_tags
2 changes: 2 additions & 0 deletions app/views/inventories/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Container } from '../../frontend/components/container/container';
import { useFormatter, useTranslate } from '../../frontend/util/dependencies';
import { DateField } from '../../frontend/components/date_field/date_field';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

interface Form {
name: string;
Expand Down Expand Up @@ -44,6 +45,7 @@ export default function ({
});
modal.close();
},
onSubmitError: handleError,
});
return (
<Container>
Expand Down
2 changes: 2 additions & 0 deletions app/views/inventories/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useModalInfo } from '../../frontend/components/modal/modal';
import { useReaction } from '../../frontend/sprinkles/reaction';
import { useTranslate } from '../../frontend/util/dependencies';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

interface Form {
userId: string;
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function ({
});
modal.close();
},
onSubmitError: handleError,
});
return (
<Form onSubmit={onSubmit}>
Expand Down
2 changes: 2 additions & 0 deletions app/views/leaves/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useModalInfo } from '../../frontend/components/modal/modal';
import { SelectOption } from '../../frontend/components/form_field/form_field';
import { Text } from '../../frontend/components/text/text';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

interface Form {
userId: string;
Expand Down Expand Up @@ -48,6 +49,7 @@ export default function ({
});
modal.close();
},
onSubmitError: handleError,
});
const leaveTypes: SelectOption<Form['type']>[] = [
{ value: 'paid', label: t('leaves.new.paid') },
Expand Down
2 changes: 2 additions & 0 deletions app/views/payslips/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FileField } from '../../frontend/components/file_field/file_field';
import { useReaction } from '../../frontend/sprinkles/reaction';
import { useFormatter } from '../../frontend/util/dependencies';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

interface Form {
userId: string;
Expand Down Expand Up @@ -41,6 +42,7 @@ export default function ({
});
modal.close();
},
onSubmitError: handleError,
});
return (
<Form onSubmit={onSubmit}>
Expand Down
2 changes: 2 additions & 0 deletions app/views/sessions/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Container } from '../../frontend/components/container/container';
import { useForm } from '@nerdgeschoss/react-use-form-library';
import { useReaction } from '../../frontend/sprinkles/reaction';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

export default function EditSession({
data: { email },
Expand All @@ -28,6 +29,7 @@ export default function EditSession({
setInvalidCode(true);
}
},
onSubmitError: handleError,
});
return (
<Container>
Expand Down
2 changes: 2 additions & 0 deletions app/views/sessions/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Container } from '../../frontend/components/container/container';
import { useForm } from '@nerdgeschoss/react-use-form-library';
import { useReaction } from '../../frontend/sprinkles/reaction';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

export default function NewSession(): JSX.Element {
const reaction = useReaction();
Expand All @@ -21,6 +22,7 @@ export default function NewSession(): JSX.Element {
refresh: false,
});
},
onSubmitError: handleError,
});
return (
<Container>
Expand Down
2 changes: 2 additions & 0 deletions app/views/sprint_feedbacks/edit_retro.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { PageProps } from '../../../data.d';
import { useTranslate } from '../../frontend/util/dependencies';
import { handleError } from '../../frontend/util/errors';
import { Stack } from '../../frontend/components/stack/stack';
import { Button } from '../../frontend/components/button/button';
import { useForm } from '@nerdgeschoss/react-use-form-library';
Expand Down Expand Up @@ -44,6 +45,7 @@ export default function ({
});
modal.close();
},
onSubmitError: (error) => handleError(error),
});

return (
Expand Down
2 changes: 2 additions & 0 deletions app/views/sprints/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useReaction } from '../../frontend/sprinkles/reaction';
import { useModalInfo } from '../../frontend/components/modal/modal';
import { NumberField } from '../../frontend/components/number_field/number_field';
import { Form } from '../../frontend/components/form/form';
import { handleError } from '../../frontend/util/errors';

export default function ({
data: { sprint },
Expand Down Expand Up @@ -40,6 +41,7 @@ export default function ({
});
modal.close();
},
onSubmitError: handleError,
});

return (
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@types/react": "^19.0.3",
"@types/react-dom": "^19.0.2",
"@types/react-flatpickr": "^3.8.11",
"@vitejs/plugin-react": "^4.3.4",
"classnames": "^2.5.1",
"i18n-js": "^4.5.1",
"react": "^19.0.0",
Expand Down
3 changes: 2 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineConfig } from 'vite';
import Rails from 'vite-plugin-rails';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [Rails()],
plugins: [Rails(), react()],
});
Loading

0 comments on commit e86a238

Please sign in to comment.