mirror of
https://github.com/mue/mue.git
synced 2026-07-17 14:04:09 +02:00
feat: Enhance AuthorInfo component with link support and add ErrorElement for improved error handling
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { MdPerson, MdOpenInNew } from 'react-icons/md';
|
||||
import { MdPerson } from 'react-icons/md';
|
||||
import { HiMiniArrowUpRight } from 'react-icons/hi2';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
import { useT } from 'contexts';
|
||||
import QuoteButtons from './QuoteButtons';
|
||||
@@ -39,7 +40,20 @@ export default function AuthorInfo({
|
||||
|
||||
{author ? (
|
||||
<div className="author-content">
|
||||
<span className="title">{author}</span>
|
||||
{hasLink ? (
|
||||
<a
|
||||
href={authorlink}
|
||||
className="author-name-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={t('widgets.quote.author_info_aria')}
|
||||
>
|
||||
<span className="title">{author}</span>
|
||||
<HiMiniArrowUpRight className="author-arrow" />
|
||||
</a>
|
||||
) : (
|
||||
<span className="title">{author}</span>
|
||||
)}
|
||||
{authorOccupation && authorOccupation !== 'Unknown' && (
|
||||
<span className="subtitle">{authorOccupation}</span>
|
||||
)}
|
||||
@@ -52,19 +66,6 @@ export default function AuthorInfo({
|
||||
)}
|
||||
|
||||
<div className="quote-buttons">
|
||||
{hasLink && (
|
||||
<Tooltip title={t('widgets.quote.link_tooltip')}>
|
||||
<a
|
||||
href={authorlink}
|
||||
className="quoteAuthorLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={t('widgets.quote.author_info_aria')}
|
||||
>
|
||||
<MdOpenInNew />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<QuoteButtons
|
||||
onCopy={onCopy}
|
||||
onFavourite={onFavourite}
|
||||
|
||||
@@ -113,6 +113,29 @@ h1.quoteauthor {
|
||||
}
|
||||
}
|
||||
|
||||
.author-name-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: opacity 0.2s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.author-arrow {
|
||||
font-size: 1rem;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover .author-arrow {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
}
|
||||
|
||||
.author-holder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
61
src/router/ErrorElement.jsx
Normal file
61
src/router/ErrorElement.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useRouteError } from 'react-router';
|
||||
import { captureException } from '@sentry/react';
|
||||
import variables from 'config/variables';
|
||||
import { useState } from 'react';
|
||||
|
||||
/**
|
||||
* Error element for React Router error boundaries
|
||||
* Displays when a route throws an error or fails to load
|
||||
*/
|
||||
export default function ErrorElement() {
|
||||
const error = useRouteError();
|
||||
const [showReport, setShowReport] = useState(true);
|
||||
|
||||
console.error('Route error:', error);
|
||||
|
||||
const reportError = () => {
|
||||
captureException(error);
|
||||
setShowReport(false);
|
||||
};
|
||||
|
||||
const title = variables.getMessage
|
||||
? variables.getMessage('error_boundary.title')
|
||||
: 'An error occurred';
|
||||
const message = variables.getMessage
|
||||
? variables.getMessage('error_boundary.message')
|
||||
: 'Something went wrong. Please try refreshing the page.';
|
||||
const reportButton = variables.getMessage
|
||||
? variables.getMessage('error_boundary.report_button')
|
||||
: 'Report Error';
|
||||
const sentSuccessfully = variables.getMessage
|
||||
? variables.getMessage('error_boundary.sent_successfully')
|
||||
: 'Report sent successfully';
|
||||
const supportDiscord = variables.getMessage
|
||||
? variables.getMessage('error_boundary.support_discord')
|
||||
: 'Get Support on Discord';
|
||||
|
||||
return (
|
||||
<div className="criticalError">
|
||||
<div className="criticalError-message">
|
||||
<h1>{title}</h1>
|
||||
<p>{message}</p>
|
||||
{error?.message && (
|
||||
<details style={{ marginTop: '1rem', opacity: 0.7 }}>
|
||||
<summary>Error Details</summary>
|
||||
<pre style={{ marginTop: '0.5rem', fontSize: '0.9em' }}>{error.message}</pre>
|
||||
</details>
|
||||
)}
|
||||
<div className="criticalError-actions">
|
||||
{showReport ? (
|
||||
<button onClick={reportError}>{reportButton}</button>
|
||||
) : (
|
||||
<p>{sentSuccessfully}</p>
|
||||
)}
|
||||
<a href="https://discord.gg/zv8C9F8" target="_blank" rel="noreferrer">
|
||||
{supportDiscord}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { lazy } from 'react';
|
||||
import { Navigate } from 'react-router';
|
||||
import App from '../App';
|
||||
import ErrorElement from './ErrorElement';
|
||||
|
||||
// Lazy load tab components
|
||||
const Settings = lazy(() => import('../features/misc/views/Settings'));
|
||||
@@ -19,6 +20,7 @@ export const routes = [
|
||||
{
|
||||
path: '/',
|
||||
element: <App />,
|
||||
errorElement: <ErrorElement />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
|
||||
Reference in New Issue
Block a user