feat: report issue popup (#261)

* feat: report issue popup

* style: modified styles in feedback form

* chore: minor UI fixes

* chore: update useEffect

* chore: change width to 400px

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Isaiah David Rodriguez <51803892+IsaDavRod@users.noreply.github.com>
This commit is contained in:
Sriram Hariharan
2024-10-12 20:31:26 -05:00
committed by GitHub
parent bd17e33537
commit 65ff6bfbbf
14 changed files with 1855 additions and 989 deletions

View File

@@ -1,22 +1,26 @@
import splashText from '@assets/insideJokes';
import createSchedule from '@pages/background/lib/createSchedule';
import { background } from '@shared/messages';
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { openReportWindow } from '@shared/util/openReportWindow';
import Divider from '@views/components/common/Divider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import List from '@views/components/common/List';
import Text from '@views/components/common/Text/Text';
import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import clsx from 'clsx';
import useKC_DABR_WASM from 'kc-dabr-wasm';
import React, { useEffect, useState } from 'react';
import AddSchedule from '~icons/material-symbols/add';
import CalendarIcon from '~icons/material-symbols/calendar-month';
import Feedback from '~icons/material-symbols/flag';
import RefreshIcon from '~icons/material-symbols/refresh';
import SettingsIcon from '~icons/material-symbols/settings';
import { Button } from './common/Button';
import CourseStatus from './common/CourseStatus';
import DialogProvider from './common/DialogProvider/DialogProvider';
import { SmallLogo } from './common/LogoIcon';
@@ -34,10 +38,13 @@ export default function PopupMain(): JSX.Element {
useKC_DABR_WASM();
useEffect(() => {
initSettings().then(({ enableCourseStatusChips, enableDataRefreshing }) => {
const initAllSettings = async () => {
const { enableCourseStatusChips, enableDataRefreshing } = await initSettings();
setEnableCourseStatusChips(enableCourseStatusChips);
setEnableDataRefreshing(enableDataRefreshing);
});
};
initAllSettings();
const l1 = OptionsStore.listen('enableCourseStatusChips', async ({ newValue }) => {
setEnableCourseStatusChips(newValue);
@@ -68,7 +75,7 @@ export default function PopupMain(): JSX.Element {
const handleOpenOptions = async () => {
const url = chrome.runtime.getURL('/options.html');
await openTabFromContentScript(url);
background.openNewTab({ url });
};
const handleCalendarOpenOnClick = async () => {
@@ -93,6 +100,9 @@ export default function PopupMain(): JSX.Element {
<button className='bg-transparent px-2 py-1.25 btn' onClick={handleOpenOptions}>
<SettingsIcon className='size-6 color-ut-black' />
</button>
<button className='bg-transparent px-2 py-1.25 btn' onClick={openReportWindow}>
<Feedback className='size-6 color-ut-black' />
</button>
</div>
</div>
</div>
@@ -122,6 +132,16 @@ export default function PopupMain(): JSX.Element {
/>
)}
</List>
<div className='bottom-0 right-0 mt-2.5 w-full flex justify-end'>
<Button
variant='filled'
color='ut-burntorange'
className='h-fit p-0 btn'
onClick={() => createSchedule('New Schedule')}
>
<AddSchedule className='h-6 w-6' />
</Button>
</div>
</ScheduleDropdown>
</div>
{activeSchedule?.courses?.length === 0 && (

View File

@@ -0,0 +1,127 @@
import 'uno.css';
import { BrowserClient, captureFeedback, defaultStackParser, getCurrentScope, makeFetchTransport } from '@sentry/react';
import React, { useState } from 'react';
import { Button } from './common/Button';
import Text from './common/Text/Text';
const client = new BrowserClient({
dsn: 'https://ed1a50d8626ff6be35b98d7b1ec86d9d@o4508033820852224.ingest.us.sentry.io/4508033822490624',
integrations: [],
transport: makeFetchTransport,
stackParser: defaultStackParser,
});
getCurrentScope().setClient(client);
client.init();
const ReportIssueMain: React.FC = () => {
const [email, setEmail] = useState('');
const [feedback, setFeedback] = useState('');
const [isSubmitted, setIsSubmitted] = useState(false);
const submitFeedback = async () => {
if (!email || !feedback) {
throw new Error('Email and feedback are required');
}
// Here you would typically send the feedback to a server
await captureFeedback(
{
message: feedback || 'No feedback provided',
email,
tags: {
version: chrome.runtime.getManifest().version,
},
},
{
includeReplay: false,
}
);
// Reset form fields and close the dialog
setEmail('');
setFeedback('');
setIsSubmitted(true);
};
if (isSubmitted) {
return (
<div className='w-80 flex flex-col rounded-lg bg-white p-6 shadow-lg'>
<Text variant='h2' className='mb-4'>
Thank you
</Text>
<Text variant='p' className='mb-6'>
Your feedback has been submitted. You may close this window.
</Text>
<Button variant='filled' color='ut-green' className='border-0' onClick={() => window.close()}>
Done
</Button>
</div>
);
}
if (isSubmitted) {
return (
<div className='w-80 bg-white p-6'>
<h2 className='mb-4 text-2xl text-orange font-bold'>{`Hook'em Horns!`}</h2>
<p className='mb-6 text-gray-600'>Your feedback is music to our ears. Thanks for helping us improve!</p>
<button
className='w-full rounded bg-orange-600 px-4 py-2 text-white font-bold transition duration-300 hover:bg-orange-700'
onClick={() => window.close()}
>
Close
</button>
</div>
);
}
return (
<div className='w-80 bg-white p-6'>
<h2 className='mb-4 text-2xl text-ut-burntorange font-bold'>Longhorn Feedback</h2>
<p className='mb-4 text-sm text-ut-black'>Help us make UT Registration Plus even better!</p>
<form onSubmit={submitFeedback}>
<div className='mb-4'>
<label htmlFor='email' className='mb-1 block text-sm text-ut-black font-medium'>
Your @utexas.edu email
</label>
<input
type='email'
id='email'
value={email}
onChange={e => setEmail(e.target.value)}
className='w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500'
placeholder='bevo@utexas.edu'
required
/>
</div>
<div className='mb-4'>
<label htmlFor='feedback' className='mb-1 block text-sm text-ut-black font-medium'>
Your feedback
</label>
<textarea
id='feedback'
value={feedback}
onChange={e => setFeedback(e.target.value)}
className='h-24 w-full resize-none border border-gray-300 rounded-md px-3 py-2 text-sm font-sans focus:outline-none focus:ring-2 focus:ring-orange-500'
placeholder='I wish UT Registration Plus could...'
required
/>
</div>
<Button
onClick={submitFeedback}
variant='filled'
color='ut-orange'
className='w-full border-0 rounded bg-orange px-4 py-2 text-white font-bold transition duration-300 hover:bg-orange-700'
>
Send Feedback
</Button>
</form>
</div>
);
};
export default ReportIssueMain;

View File

@@ -1,5 +1,4 @@
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
import { Status } from '@shared/types/Course';
import { Button } from '@views/components/common/Button';
import CourseStatus from '@views/components/common/CourseStatus';
import Divider from '@views/components/common/Divider';

View File

@@ -9,7 +9,7 @@ let initialLoad = true;
const errorSchedule = new UserSchedule({
courses: [],
id: 'error',
name: 'An error has occurred',
name: 'No Schedule Selected',
hours: 0,
updatedAt: Date.now(),
});

View File

@@ -11,6 +11,7 @@ export const SiteSupport = {
WAITLIST: 'WAITLIST',
EXTENSION_POPUP: 'EXTENSION_POPUP',
MY_CALENDAR: 'MY_CALENDAR',
REPORT_ISSUE: 'REPORT_ISSUE',
} as const;
/**