feat: add dining app promo (#598)
* feat: add DiningAppPromo component and integrate it into Calendar * feat: update WhatsNewPopup with new features and app download link * fix: remove outdated links * chore: run lint * chore: run prettier * feat: enhance DiningAppPromo with close button and integrate user preference for promo visibility * chore: run lint * chore: run check types * fix: correct promo visibility logic in Calendar component * feat: centralize app store URLs in appUrls.ts * chore: run lint * feat: integrate UT Dining promo image * chore: run lint * fix: update logo in WhatsNew popup to use LD icon * fix: convert URLs to URL objects for consistency * fix: update LD icon in WhatsNew popup to new version * fix: update description for Coffee Shops feature to clarify operating times * fix: rename promo state and storage key to showUTDiningPromo for clarity --------- Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import { Button } from '../common/Button';
|
||||
import { LargeLogo } from '../common/LogoIcon';
|
||||
import Text from '../common/Text/Text';
|
||||
import CalendarFooter from './CalendarFooter';
|
||||
import DiningAppPromo from './DiningAppPromo';
|
||||
|
||||
/**
|
||||
* Calendar page component
|
||||
@@ -41,6 +42,8 @@ export default function Calendar(): ReactNode {
|
||||
const [showPopup, setShowPopup] = useState<boolean>(course !== null);
|
||||
const showWhatsNewDialog = useWhatsNewPopUp();
|
||||
|
||||
const [showUTDiningPromo, setShowUTDiningPromo] = useState<boolean>(false);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { data: showSidebar, isPending: isSidebarStatePending } = useQuery({
|
||||
queryKey: ['settings', 'showCalendarSidebar'],
|
||||
@@ -82,12 +85,19 @@ export default function Calendar(): ReactNode {
|
||||
if (course) setShowPopup(true);
|
||||
}, [course]);
|
||||
|
||||
useEffect(() => {
|
||||
// Load the user's preference for the promo
|
||||
OptionsStore.get('showUTDiningPromo').then(show => {
|
||||
setShowUTDiningPromo(show);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isSidebarStatePending) return null;
|
||||
|
||||
return (
|
||||
<CalendarContext.Provider value>
|
||||
<div className='h-full w-full flex flex-col'>
|
||||
<div className='h-screen flex overflow-auto screenshot:calendar-target'>
|
||||
<div className='screenshot:calendar-target h-screen flex overflow-auto'>
|
||||
<div
|
||||
className={clsx(
|
||||
'py-spacing-6 relative h-full min-h-screen w-full flex flex-none flex-col justify-between overflow-clip whitespace-nowrap border-r border-ut-offwhite/50 shadow-[2px_0_10px,rgba(214_210_196_/_.1)] motion-safe:duration-300 motion-safe:ease-out-expo motion-safe:transition-[max-width] screenshot:hidden',
|
||||
@@ -122,8 +132,16 @@ export default function Calendar(): ReactNode {
|
||||
<CalendarSchedules />
|
||||
<Divider orientation='horizontal' size='100%' />
|
||||
<ResourceLinks />
|
||||
<Divider orientation='horizontal' size='100%' />
|
||||
{/* <TeamLinks /> */}
|
||||
<Divider orientation='horizontal' size='100%' />
|
||||
{showUTDiningPromo && (
|
||||
<DiningAppPromo
|
||||
onClose={() => {
|
||||
setShowUTDiningPromo(false);
|
||||
OptionsStore.set('showUTDiningPromo', false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className='flex flex-col gap-spacing-3'>
|
||||
<a
|
||||
href={CRX_PAGES.REPORT}
|
||||
|
||||
69
src/views/components/calendar/DiningAppPromo.tsx
Normal file
69
src/views/components/calendar/DiningAppPromo.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { AppStoreLogo, ForkKnife, X as CloseIcon } from '@phosphor-icons/react';
|
||||
import { UT_DINING_APP_STORE_URL, UT_DINING_GOOGLE_PLAY_URL } from '@shared/util/appUrls';
|
||||
import { Button } from '@views/components/common/Button';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import React from 'react';
|
||||
|
||||
interface DiningAppPromoProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotional component for the UT Dining app
|
||||
*/
|
||||
export default function DiningAppPromo({ onClose }: DiningAppPromoProps) {
|
||||
return (
|
||||
<div className='relative min-w-[16.25rem] w-full flex items-center gap-spacing-3 border border-ut-offwhite/50 rounded p-spacing-4'>
|
||||
<div className='flex items-center justify-center'>
|
||||
<ForkKnife className='h-6 w-6 text-ut-black' />
|
||||
</div>
|
||||
<div className='flex flex-col gap-spacing-1'>
|
||||
<Text as='p' variant='small' className='whitespace-normal text-ut-black'>
|
||||
Download our new{' '}
|
||||
<a
|
||||
href={UT_DINING_APP_STORE_URL}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
aria-label='UT Dining app'
|
||||
className='text-ut-burntorange underline'
|
||||
>
|
||||
UT Dining app
|
||||
</a>{' '}
|
||||
to explore all dining options on campus!
|
||||
</Text>
|
||||
<div className='mt-spacing-2 flex items-center gap-spacing-2'>
|
||||
<Text variant='mini' className='text-ut-black'>
|
||||
Available on
|
||||
</Text>
|
||||
<a
|
||||
href={UT_DINING_APP_STORE_URL}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
aria-label='Download on App Store'
|
||||
className='text-theme-black transition-colors hover:text-ut-burntorange'
|
||||
>
|
||||
<AppStoreLogo className='h-4.5 w-4.5' />
|
||||
</a>
|
||||
{/* <a
|
||||
href={UT_DINING_GOOGLE_PLAY_URL}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
aria-label='Download on Google Play'
|
||||
className='text-theme-black hover:text-ut-burntorange transition-colors'
|
||||
>
|
||||
<GooglePlayLogo className='h-4.5 w-4.5' />
|
||||
</a> */}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant='minimal'
|
||||
color='theme-black'
|
||||
onClick={onClose}
|
||||
className='absolute right-1 top-1 h-5 w-5 p-0'
|
||||
icon={CloseIcon}
|
||||
aria-label='Close dining app promo'
|
||||
title='Close'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,10 +13,10 @@ interface LinkItem {
|
||||
}
|
||||
|
||||
const links: LinkItem[] = [
|
||||
{
|
||||
text: "Spring '25 Course Schedule",
|
||||
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20252/',
|
||||
},
|
||||
// {
|
||||
// text: "Spring '25 Course Schedule",
|
||||
// url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20252/',
|
||||
// },
|
||||
{
|
||||
text: 'Course Schedule Archives',
|
||||
url: 'https://registrar.utexas.edu/schedules/archive',
|
||||
|
||||
@@ -22,10 +22,10 @@ const links: LinkItem[] = [
|
||||
text: "Summer '25 Course Schedule",
|
||||
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20256/',
|
||||
},
|
||||
{
|
||||
text: "Spring '25 Course Schedule",
|
||||
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20252/',
|
||||
},
|
||||
// {
|
||||
// text: "Spring '25 Course Schedule",
|
||||
// url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20252/',
|
||||
// },
|
||||
{
|
||||
text: 'Course Schedule Archives',
|
||||
url: 'https://registrar.utexas.edu/schedules/archive',
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function ScheduleDropdown({ defaultOpen, children }: ScheduleDrop
|
||||
{({ open }) => (
|
||||
<>
|
||||
<DisclosureButton className='w-full flex items-center border-none bg-transparent px-3.5 py-2.5 text-left'>
|
||||
<div className='flex-1 min-w-0 overflow-hidden'>
|
||||
<div className='min-w-0 flex-1 overflow-hidden'>
|
||||
<Text
|
||||
as='div'
|
||||
variant='h3'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { IconProps } from '@phosphor-icons/react';
|
||||
import { CloudX, Copy, Exam, MapPinArea, Palette } from '@phosphor-icons/react';
|
||||
import { CloudX, Coffee, ForkKnife, MapTrifold, Storefront } from '@phosphor-icons/react';
|
||||
import { ExtensionStore } from '@shared/storage/ExtensionStore';
|
||||
import { UT_DINING_PROMO_IMAGE_URL } from '@shared/util/appUrls';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import useWhatsNewPopUp from '@views/hooks/useWhatsNew';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
@@ -12,7 +13,7 @@ import React, { useEffect, useState } from 'react';
|
||||
*
|
||||
* It should be incremented every time the "What's New" popup is updated.
|
||||
*/
|
||||
const WHATSNEW_POPUP_VERSION = 1;
|
||||
const WHATSNEW_POPUP_VERSION = 2;
|
||||
|
||||
const WHATSNEW_VIDEO_URL = 'https://cdn.longhorns.dev/whats-new-v2.1.2.mp4';
|
||||
|
||||
@@ -25,35 +26,28 @@ type Feature = {
|
||||
|
||||
const NEW_FEATURES = [
|
||||
{
|
||||
id: 'custom-course-colors',
|
||||
icon: Palette,
|
||||
title: 'Custom Course Colors',
|
||||
description: 'Paint your schedule in your favorite color theme',
|
||||
id: 'dining-halls-info',
|
||||
icon: ForkKnife,
|
||||
title: 'Dining Halls Info',
|
||||
description: 'See daily menus and nutritional deets for J2, JCL, and Kins',
|
||||
},
|
||||
{
|
||||
id: 'quick-copy',
|
||||
icon: Copy,
|
||||
title: 'Quick Copy',
|
||||
description: 'Quickly copy a course unique number to your clipboard',
|
||||
id: 'coffee-shops',
|
||||
icon: Coffee,
|
||||
title: 'Coffee Shops',
|
||||
description: 'Need a Coffee Fix? Check operating times for your favorite campus cafes.',
|
||||
},
|
||||
{
|
||||
id: 'updated-grades',
|
||||
icon: Exam,
|
||||
title: 'Updated Grades',
|
||||
description: 'Fall 2024 grades are now available in the grade distribution',
|
||||
id: 'convenience-stores',
|
||||
icon: Storefront,
|
||||
title: 'Convenience Stores',
|
||||
description: 'Find hours for quick snacks and essentials on campus.',
|
||||
},
|
||||
{
|
||||
id: 'ut-map',
|
||||
icon: MapPinArea,
|
||||
title: (
|
||||
<div className='flex flex-row items-center'>
|
||||
UTRP Map
|
||||
<span className='mx-2 border border-ut-burntorange rounded px-2 py-0.5 text-xs text-ut-burntorange font-medium'>
|
||||
BETA
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
description: 'Find directions to your classes with our beta map feature in the settings page',
|
||||
id: 'microwave-map',
|
||||
icon: MapTrifold,
|
||||
title: 'Microwave Map',
|
||||
description: 'Need to heat up your lunch? Find microwaves across campus!',
|
||||
},
|
||||
] as const satisfies readonly Feature[];
|
||||
|
||||
@@ -97,14 +91,11 @@ export default function WhatsNewPopupContent(): JSX.Element {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<video
|
||||
className='h-fit w-full flex items-center justify-center border border-ut-offwhite/50 rounded object-cover'
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
>
|
||||
<source src={WHATSNEW_VIDEO_URL} type='video/mp4' onError={() => setVideoError(true)} />
|
||||
</video>
|
||||
<img
|
||||
className='h-full w-full border border-ut-offwhite/50 rounded object-cover'
|
||||
src={UT_DINING_PROMO_IMAGE_URL}
|
||||
alt='UT Dining Promo'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UT_DINING_APP_STORE_URL } from '@shared/util/appUrls';
|
||||
import { Button } from '@views/components/common/Button';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import WhatsNewPopupContent from '@views/components/common/WhatsNewPopup';
|
||||
@@ -7,6 +8,8 @@ import React from 'react';
|
||||
import { LogoIcon } from '../components/common/LogoIcon';
|
||||
import useChangelog from './useChangelog';
|
||||
|
||||
const LDIconURL = new URL('/src/assets/LD-icon-new.png', import.meta.url).href;
|
||||
|
||||
/**
|
||||
* Custom hook that provides a function to display a what's new dialog.
|
||||
*
|
||||
@@ -22,20 +25,26 @@ export default function useWhatsNewPopUp(): () => void {
|
||||
className: 'w-[830px] flex flex-col items-center gap-spacing-7 p-spacing-8',
|
||||
title: (
|
||||
<div className='flex items-center justify-between gap-4'>
|
||||
<LogoIcon width='48' height='48' />
|
||||
<img src={LDIconURL} alt='LD Icon' className='h-12 w-12 rounded-lg' />
|
||||
<Text variant='h1' className='text-theme-black'>
|
||||
What's New in UT Registration Plus
|
||||
Download our new UT Dining app!
|
||||
</Text>
|
||||
</div>
|
||||
),
|
||||
description: <WhatsNewPopupContent />,
|
||||
buttons: (
|
||||
<div className='flex flex-row items-end gap-spacing-4'>
|
||||
<Button onClick={showChangeLog} variant='minimal' color='ut-black'>
|
||||
Read Changelog v{version}
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.open(UT_DINING_APP_STORE_URL, '_blank');
|
||||
}}
|
||||
variant='minimal'
|
||||
color='ut-black'
|
||||
>
|
||||
Download UT Dining on iOS
|
||||
</Button>
|
||||
<Button onClick={close} color='ut-burntorange'>
|
||||
Get started
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user