diff --git a/src/components/faqs/PlannerFaqs.tsx b/src/components/faqs/PlannerFaqs.tsx index f48a579c..a7b1fcbb 100644 --- a/src/components/faqs/PlannerFaqs.tsx +++ b/src/components/faqs/PlannerFaqs.tsx @@ -1,6 +1,7 @@ import classNames from 'classnames' import { Transition, Disclosure } from '@headlessui/react' import { ChevronUpIcon } from '@heroicons/react/24/outline' +import { AnalyticsTracker } from '../../utils/AnalyticsTracker' const PlannerFaqs = () => { const data = [ @@ -158,7 +159,14 @@ const PlannerFaqs = () => { as="div" defaultOpen={faqIdx === 0} key={`planner-faq-${faqIdx}`} - className="rounded-2xl bg-white p-3 dark:bg-dark" + className={`rounded-2xl bg-white p-3 dark:bg-dark faq-disclosure-${faqIdx}`} + onClick={() => { + const disclosure = document.querySelector(`.faq-disclosure-${faqIdx}`); + const isOpen = disclosure?.getAttribute('data-headlessui-state') !== 'open'; + if (isOpen && faq.question.type === 'span') { + AnalyticsTracker.trackFaq(faq.question.props.children) + } + }} > {({ open }) => ( <> diff --git a/src/components/planner/schedule/PrintSchedule.tsx b/src/components/planner/schedule/PrintSchedule.tsx index 75e5b8d7..671e3037 100644 --- a/src/components/planner/schedule/PrintSchedule.tsx +++ b/src/components/planner/schedule/PrintSchedule.tsx @@ -4,6 +4,7 @@ import { Button } from '../../ui/button' import { CameraIcon } from '@heroicons/react/24/outline' import { toPng } from 'html-to-image' import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '../../ui/tooltip' +import { AnalyticsTracker, Feature } from '../../../utils/AnalyticsTracker' const PrintSchedule = ({ component }) => { const { enabled } = useContext(ThemeContext) @@ -24,6 +25,8 @@ const PrintSchedule = ({ component }) => { .catch((err) => { console.log(err) }) + + AnalyticsTracker.trackFeature(Feature.SCREENSHOT) }, [component] ) diff --git a/src/components/planner/schedule/ToggleScheduleGrid.tsx b/src/components/planner/schedule/ToggleScheduleGrid.tsx index de7ee9cc..0c39f1cb 100644 --- a/src/components/planner/schedule/ToggleScheduleGrid.tsx +++ b/src/components/planner/schedule/ToggleScheduleGrid.tsx @@ -1,6 +1,7 @@ import { Button } from '../../ui/button' import { ViewColumnsIcon } from '@heroicons/react/24/outline' import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '../../ui/tooltip' +import { AnalyticsTracker, Feature } from '../../../utils/AnalyticsTracker' const ToggleScheduleGrid = ({ showGridHook }) => { const [showGrid, setShowGrid] = showGridHook @@ -12,7 +13,10 @@ const ToggleScheduleGrid = ({ showGridHook }) => { diff --git a/src/components/planner/sidebar/CoursesController/ClassSelector.tsx b/src/components/planner/sidebar/CoursesController/ClassSelector.tsx index cdae8f66..a8082405 100644 --- a/src/components/planner/sidebar/CoursesController/ClassSelector.tsx +++ b/src/components/planner/sidebar/CoursesController/ClassSelector.tsx @@ -9,6 +9,7 @@ import { teacherIdsFromCourseInfo, uniqueTeachersFromCourseInfo, getAllPickedSlo import { Button } from '../../../ui/button' import ProfessorItem from './ProfessorItem' import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '../../../ui/dropdown-menu' +import { AnalyticsTracker, Feature } from '../../../../utils/AnalyticsTracker' import ClassSelectorDropdownController from './ClassSelectorDropdownController' type Props = { @@ -58,6 +59,8 @@ const ClassSelector = ({ course }: Props) => { newMultipleOptions[selectedOption].course_options = courseOptions; setMultipleOptions(newMultipleOptions); setLocked(!locked) + + AnalyticsTracker.trackFeature(Feature.LOCK_TOGGLE); } useEffect(() => { diff --git a/src/components/planner/sidebar/OptionsController.tsx b/src/components/planner/sidebar/OptionsController.tsx index 78ef3c25..cf535d82 100644 --- a/src/components/planner/sidebar/OptionsController.tsx +++ b/src/components/planner/sidebar/OptionsController.tsx @@ -4,6 +4,7 @@ import { EllipsisHorizontalIcon } from '@heroicons/react/24/outline' import { useContext } from 'react' import MultipleOptionsContext from '../../../contexts/MultipleOptionsContext'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip'; +import { AnalyticsTracker, Feature } from '../../../utils/AnalyticsTracker'; /** * Sortable list of schedule options @@ -28,6 +29,9 @@ const OptionsController = () => { animation={200} delay={2} multiDrag + onEnd={() => { + AnalyticsTracker.trackFeature(Feature.OPTION_REORDER); + }} > {multipleOptions.map((option: Option) => ( { setMultipleOptions((prevMultipleOptions) => { const updatedMultipleOptions = prevMultipleOptions.map((item) => - item.id === multipleOptions[selectedOption].id ? { ...item, icon: newIcon } : item + item.id === multipleOptions[selectedOption].id ? { ...item, icon: newIcon.imageUrl } : item ) return updatedMultipleOptions; }) + AnalyticsTracker.trackFeature(Feature.OPTION_EMOJI); + AnalyticsTracker.emoji(newIcon.emoji); } return ( @@ -92,7 +96,7 @@ const SelectedOptionController = ({ suggestedEmojisMode={SuggestionMode.RECENT} emojiStyle={EmojiStyle.APPLE} onEmojiClick={(emojiData, e) => { - changeOptionIcon(emojiData.imageUrl) + changeOptionIcon(emojiData) setEmojiPickerOpen(false) }} /> diff --git a/src/components/planner/sidebar/selectedOptionController/CopyOption.tsx b/src/components/planner/sidebar/selectedOptionController/CopyOption.tsx index e6b8d00b..26b29ade 100644 --- a/src/components/planner/sidebar/selectedOptionController/CopyOption.tsx +++ b/src/components/planner/sidebar/selectedOptionController/CopyOption.tsx @@ -5,6 +5,8 @@ import { useToast } from '../../../ui/use-toast' import { Buffer } from 'buffer' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../../ui/tooltip' import { CourseOption } from '../../../../@types' +import { plausible } from '../../../../utils' +import { AnalyticsTracker, Feature } from '../../../../utils/AnalyticsTracker' type Props = { currentOption: CourseOption[] @@ -48,6 +50,7 @@ const CopyOption = ({ currentOption, className }: Props) => { setTimeout(() => { setIcon(false) }, 1500) + AnalyticsTracker.trackFeature(Feature.COPY); } return ( diff --git a/src/components/planner/sidebar/selectedOptionController/PasteOption.tsx b/src/components/planner/sidebar/selectedOptionController/PasteOption.tsx index b5438963..4f1115c6 100644 --- a/src/components/planner/sidebar/selectedOptionController/PasteOption.tsx +++ b/src/components/planner/sidebar/selectedOptionController/PasteOption.tsx @@ -11,6 +11,7 @@ import MultipleOptionsContext from '../../../../contexts/MultipleOptionsContext' import { convertCourseInfoToCourseOption } from '../../../../utils' import { Button } from '../../../ui/button' import { useToast } from '../../../ui/use-toast' +import { AnalyticsTracker, Feature } from '../../../../utils/AnalyticsTracker' const PasteOption = () => { const { multipleOptions, setMultipleOptions, selectedOption } = useContext(MultipleOptionsContext) @@ -97,6 +98,8 @@ const PasteOption = () => { description: 'A opção foi colada com sucesso', duration: 1500, }) + + AnalyticsTracker.trackFeature(Feature.PASTE); } /** diff --git a/src/components/planner/sidebar/selectedOptionController/RandomFill.tsx b/src/components/planner/sidebar/selectedOptionController/RandomFill.tsx index 1ff052bb..3008369e 100644 --- a/src/components/planner/sidebar/selectedOptionController/RandomFill.tsx +++ b/src/components/planner/sidebar/selectedOptionController/RandomFill.tsx @@ -8,6 +8,7 @@ import { Checkbox } from '../../../ui/checkbox' import { Separator } from '../../../ui/separator' import CourseContext from '../../../../contexts/CourseContext' import MultipleOptionsContext from '../../../../contexts/MultipleOptionsContext' +import { AnalyticsTracker, Feature } from '../../../../utils/AnalyticsTracker' type Props = { className?: string @@ -165,6 +166,8 @@ const RandomFill = ({ className }: Props) => { // Choose a random permutation const randomNumber = Math.floor(Math.random() * (newPermutations.length - 1)) applySchedule(newPermutations[randomNumber]) + + AnalyticsTracker.trackFeature(Feature.RANDOM_FILL); } const applySchedule = (classesCombinations: ClassInfo[]) => { diff --git a/src/components/planner/sidebar/sessionController/CsvExport.tsx b/src/components/planner/sidebar/sessionController/CsvExport.tsx index 490ae45c..d0b63cea 100644 --- a/src/components/planner/sidebar/sessionController/CsvExport.tsx +++ b/src/components/planner/sidebar/sessionController/CsvExport.tsx @@ -2,6 +2,7 @@ import { ArrowUpOnSquareIcon } from '@heroicons/react/24/outline' import { useContext } from 'react' import CourseContext from '../../../../contexts/CourseContext' import MultipleOptionsContext from '../../../../contexts/MultipleOptionsContext' +import { AnalyticsTracker, Feature } from '../../../../utils/AnalyticsTracker' //TODO: utils?? const csvEncode = (text: string | null | undefined) => { @@ -43,6 +44,8 @@ const CsvExport = () => { a.download = 'schedule.csv' a.click() URL.revokeObjectURL(url) + + AnalyticsTracker.trackFeature(Feature.EXPORT_TO_CSV) } return ( @@ -52,7 +55,7 @@ const CsvExport = () => { > Exportar Opções (CSV) - + ) } diff --git a/src/components/planner/sidebar/sessionController/course-picker/MajorSearchCombobox.tsx b/src/components/planner/sidebar/sessionController/course-picker/MajorSearchCombobox.tsx index 82f207be..6254a27c 100644 --- a/src/components/planner/sidebar/sessionController/course-picker/MajorSearchCombobox.tsx +++ b/src/components/planner/sidebar/sessionController/course-picker/MajorSearchCombobox.tsx @@ -6,6 +6,7 @@ import MajorContext from '../../../../../contexts/MajorContext' import { cn, plausible } from '../../../../../utils' import { Button } from '../../../../ui/button' import { Popover, PopoverContent, PopoverTrigger } from '../../../../ui/popover' +import { AnalyticsTracker } from '../../../../../utils/AnalyticsTracker' interface Props { selectedMajor: Major | null @@ -77,9 +78,7 @@ const MajorSearchCombobox = ({ selectedMajor, setSelectedMajor }: Props) => { setSelectedMajor(currentMajor.id === selectedMajor?.id ? null : currentMajor) setOpen(false) - const { trackEvent } = plausible - trackEvent('Major Selected', { props: { major: currentMajor.name } }) - trackEvent('Faculty', { props: { faculty: currentMajor.faculty_id.toUpperCase() } }) + AnalyticsTracker.majorSelected(currentMajor) }} > {`${major.name} (${major.acronym}) - ${major.faculty_id.toUpperCase()}`} diff --git a/src/utils/AnalyticsTracker.ts b/src/utils/AnalyticsTracker.ts new file mode 100644 index 00000000..7000a47b --- /dev/null +++ b/src/utils/AnalyticsTracker.ts @@ -0,0 +1,50 @@ + +import Plausible from "plausible-tracker"; +import { Major } from "../@types"; + +const plausible = Plausible({ + domain: import.meta.env.VITE_APP_PLAUSIBLE_DOMAIN, + apiHost: import.meta.env.VITE_APP_PLAUSIBLE_HOST, + trackLocalhost: !Number(import.meta.env.VITE_APP_PROD), +}) + +const { trackEvent } = plausible; + +export enum Feature { + COPY = 'Copy Schedule', + PASTE = 'Paste Schedule', + RANDOM_FILL = 'Random Fill', + + OPTION_REORDER = 'Options Order Changed', + OPTION_RENAME = 'Option Renamed', + OPTION_EMOJI = 'Schedule Emoji Changed', + + SCREENSHOT = 'Screenshot', + GRID = 'Grid View Toggle', + + EXPORT_TO_CSV = 'Export to CSV', + + LOCK_TOGGLE = 'Class Lock Toggled', +} + +export class AnalyticsTracker { + static majorSelected = (major: Major) => { + if (major) { + trackEvent('Major Selected', { props: { major: major.name } }) + trackEvent('Faculty', { props: { faculty: major.faculty_id.toUpperCase() } }) + } + } + + static trackFeature = (feature: Feature) => { + trackEvent('Feature', { props: { feature_counter: feature } }) + } + + static trackFaq = (faq: string) => { + if (faq) trackEvent('FAQ', { props: { faq } }) + } + + static emoji = (emoji: string) => { + if (emoji) trackEvent('Emoji', { props: { emoji } }) + } +} +