191 lines
5.4 KiB
JavaScript
191 lines
5.4 KiB
JavaScript
import { siteConfig } from 'site/site.config.mjs'
|
|
import yaml from 'js-yaml'
|
|
// Hooks
|
|
import { useContext } from 'react'
|
|
import { useTranslation } from 'next-i18next'
|
|
import { useAccount } from 'shared/hooks/use-account.mjs'
|
|
import { useBackend } from 'shared/hooks/use-backend.mjs'
|
|
import { useBugsnag } from 'shared/hooks/use-bugsnag.mjs'
|
|
// Context
|
|
import { ModalContext } from 'shared/context/modal-context.mjs'
|
|
// Components
|
|
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
|
|
import { ChoiceButton } from 'shared/components/choice-button.mjs'
|
|
import { CopyToClipboard } from 'shared/components/copy-to-clipboard.mjs'
|
|
import { WebLink } from 'shared/components/link.mjs'
|
|
import { Spinner } from 'shared/components/spinner.mjs'
|
|
|
|
export const ns = ['errors']
|
|
|
|
const ReportId = ({ id, t }) => (
|
|
<>
|
|
<p>{t('leadId')}:</p>
|
|
<div className="bg-primary rounded-lg border border-primary border-solid bg-opacity-10">
|
|
<div
|
|
className={`
|
|
flex flex-row justify-between items-center
|
|
text-sm font-medium text-primary
|
|
p-4
|
|
`}
|
|
>
|
|
<span>{id}</span>
|
|
<CopyToClipboard content={id} />
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
|
|
const userInfo = (account) => `
|
|
## About the user
|
|
|
|
This report was submitted by <a href="https://freesewing.org/users/${account.id}">FreeSewing user #${account.id}</a>
|
|
with username **${account.username}**.`
|
|
|
|
const patronInfo = (account) =>
|
|
account.patron
|
|
? `
|
|
## :purple_heart: This was reported by a FreeSewing patron
|
|
|
|
> Please prioritize this issue as well as other issues by patrons as they are.
|
|
|
|
`
|
|
: `
|
|
|
|
## :snail: This was not reported by a FreeSewing patron
|
|
|
|
> With limited time and resources available, please prioritize issues from patrons instead.
|
|
|
|
`
|
|
|
|
const issueTemplate = ({ isPublic, account, data, bug = { id: 'unknown', url: '#' } }) => `
|
|
${patronInfo(account)}
|
|
|
|
- **Site**: ${siteConfig.site}
|
|
- **Public report**: ${isPublic ? 'Yes' : 'No'}
|
|
- **Bugsnag Context ID**: [${bug.id}](${bug.url})
|
|
|
|
Please refer to [the Bugsnag report](${bug.url}) for in-depth data and stack trace.
|
|
However, please note that this is not publicly available.
|
|
|
|
${isPublic ? userInfo(account) : ''}
|
|
|
|
## Data included in this issue
|
|
|
|
\`\`\`yaml
|
|
${yaml.dump(data)}
|
|
\`\`\`
|
|
`
|
|
|
|
const ModalSpinner = ({ t }) => (
|
|
<ModalWrapper>
|
|
<div className="w-full max-w-md text-center">
|
|
<h2>{t('pleaseWait')}</h2>
|
|
<Spinner className="w-24 h-24 text-secondary animate-spin m-auto" />
|
|
</div>
|
|
</ModalWrapper>
|
|
)
|
|
|
|
const ModalResult = ({ t, bug, issue, clearModal }) => (
|
|
<ModalWrapper>
|
|
<h2>{t('reportCreated')}</h2>
|
|
<ReportId id={bug.id} t={t} />
|
|
{issue ? (
|
|
<>
|
|
<p>{t('leadIssue')}</p>
|
|
<WebLink href={issue.url} txt={`github.com/freesewing/freesewing/issues/${issue.nr}`} />
|
|
</>
|
|
) : null}
|
|
<p>
|
|
<button className="btn btn-neutral mt-4" onClick={clearModal}>
|
|
{t('close')}
|
|
</button>
|
|
</p>
|
|
</ModalWrapper>
|
|
)
|
|
|
|
export const ModalProblemReport = ({ title, data }) => {
|
|
// Hooks
|
|
const { t } = useTranslation(ns)
|
|
const reportError = useBugsnag()
|
|
const { account } = useAccount()
|
|
const backend = useBackend()
|
|
|
|
// Context
|
|
const { clearModal, setModal } = useContext(ModalContext)
|
|
|
|
// Helper method to create an issue
|
|
const createIssue = async ({ title, body, labels }) => {
|
|
let result
|
|
try {
|
|
result = await backend.createIssue({ title, body, labels })
|
|
} catch (err) {
|
|
console.log(err)
|
|
}
|
|
|
|
if (result.success && result.data.issue)
|
|
return { url: result.data.issue.html_url, nr: result.data.issue.number }
|
|
|
|
return false
|
|
}
|
|
|
|
// Helper method for private error report
|
|
const reportPrivate = async (evt) => {
|
|
evt.stopPropagation()
|
|
setModal(<ModalSpinner t={t} />)
|
|
const error = new Error(`[private] ${title}`)
|
|
const bug = await reportError(error, {
|
|
...data,
|
|
private: true,
|
|
public: false,
|
|
patron: account.patron,
|
|
})
|
|
let issue = false
|
|
if (account.patron) {
|
|
const issueData = {
|
|
title,
|
|
body: issueTemplate({ isPublic: false, account, title, data, bug }),
|
|
labels: ['problem-report', 'public-report', 'impacts-patron'],
|
|
}
|
|
issue = await createIssue(issueData)
|
|
}
|
|
setModal(<ModalResult {...{ bug, issue, t, clearModal }} />)
|
|
}
|
|
|
|
// Helper method for public error report
|
|
const reportPublic = async (evt) => {
|
|
evt.stopPropagation()
|
|
setModal(<ModalSpinner t={t} />)
|
|
const error = new Error(`[public] ${title}`)
|
|
const bug = await reportError(error, {
|
|
...data,
|
|
private: false,
|
|
public: true,
|
|
patron: account.patron,
|
|
})
|
|
const issueData = {
|
|
title,
|
|
body: issueTemplate({ isPublic: true, account, title, data, bug }),
|
|
labels: ['problem-report', 'public-report'],
|
|
}
|
|
if (account.patron) issueData.labels.push('impacts-patron')
|
|
const issue = await createIssue(issueData)
|
|
setModal(<ModalResult {...{ bug, issue, t, clearModal }} />)
|
|
}
|
|
|
|
return (
|
|
<ModalWrapper>
|
|
<div className="grid gap-2 p-4 grid-cols-1 max-w-lg w-full">
|
|
<h2>{t('errors:newReport')}</h2>
|
|
<ChoiceButton title={t('privateReport.t')} onClick={reportPrivate}>
|
|
<p>{t('privateReport.d')}</p>
|
|
</ChoiceButton>
|
|
<ChoiceButton title={t('publicReport.t')} onClick={reportPublic}>
|
|
<p>{t('publicReport.d')}</p>
|
|
</ChoiceButton>
|
|
<button className="btn btn-neutral mt-4" onClick={clearModal}>
|
|
{t('cancel')}
|
|
</button>
|
|
</div>
|
|
</ModalWrapper>
|
|
)
|
|
}
|