mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-27 20:22:33 +08:00
[offers][feat] add event tracking and save to profile in submisison page (#465)
* [offers][feat] add event tracking and save to profile in form * [offers][refactor] refactor feature page * [offers][fix] fix offer table border for action column
This commit is contained in:
@ -12,7 +12,6 @@ const navigationAuthenticated: ProductNavigationItems = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
// TODO: Change this to your own GA4 measurement ID.
|
|
||||||
googleAnalyticsMeasurementID: 'G-34XRGLEVCF',
|
googleAnalyticsMeasurementID: 'G-34XRGLEVCF',
|
||||||
logo: (
|
logo: (
|
||||||
<img alt="Tech Offers Repo" className="h-8 w-auto" src="/offers-logo.svg" />
|
<img alt="Tech Offers Repo" className="h-8 w-auto" src="/offers-logo.svg" />
|
||||||
|
@ -2,6 +2,7 @@ import { useRouter } from 'next/router';
|
|||||||
import { ArrowRightIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
import { ArrowRightIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||||
import { Button, useToast } from '@tih/ui';
|
import { Button, useToast } from '@tih/ui';
|
||||||
|
|
||||||
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
import DashboardOfferCard from '~/components/offers/dashboard/DashboardOfferCard';
|
import DashboardOfferCard from '~/components/offers/dashboard/DashboardOfferCard';
|
||||||
|
|
||||||
import { formatDate } from '~/utils/offers/time';
|
import { formatDate } from '~/utils/offers/time';
|
||||||
@ -10,7 +11,6 @@ import { trpc } from '~/utils/trpc';
|
|||||||
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
|
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
|
||||||
|
|
||||||
import type { UserProfile, UserProfileOffer } from '~/types/offers';
|
import type { UserProfile, UserProfileOffer } from '~/types/offers';
|
||||||
|
|
||||||
type Props = Readonly<{
|
type Props = Readonly<{
|
||||||
profile: UserProfile;
|
profile: UserProfile;
|
||||||
}>;
|
}>;
|
||||||
@ -22,6 +22,7 @@ export default function DashboardProfileCard({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const trpcContext = trpc.useContext();
|
const trpcContext = trpc.useContext();
|
||||||
const PROFILE_URL = `/offers/profile/${id}?token=${token}`;
|
const PROFILE_URL = `/offers/profile/${id}?token=${token}`;
|
||||||
|
const { event: gaEvent } = useGoogleAnalytics();
|
||||||
const removeSavedProfileMutation = trpc.useMutation(
|
const removeSavedProfileMutation = trpc.useMutation(
|
||||||
'offers.user.profile.removeFromUserProfile',
|
'offers.user.profile.removeFromUserProfile',
|
||||||
{
|
{
|
||||||
@ -97,7 +98,14 @@ export default function DashboardProfileCard({
|
|||||||
label="Read full profile"
|
label="Read full profile"
|
||||||
size="md"
|
size="md"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => router.push(PROFILE_URL)}
|
onClick={() => {
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.view_profile_from_dashboard',
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'View profile from dashboard',
|
||||||
|
});
|
||||||
|
router.push(PROFILE_URL);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 994 KiB After Width: | Height: | Size: 994 KiB |
Before Width: | Height: | Size: 923 KiB After Width: | Height: | Size: 923 KiB |
@ -1,9 +1,14 @@
|
|||||||
// Import { useState } from 'react';
|
// Import { useState } from 'react';
|
||||||
// import { setTimeout } from 'timers';
|
// import { setTimeout } from 'timers';
|
||||||
|
import { useState } from 'react';
|
||||||
import { DocumentDuplicateIcon } from '@heroicons/react/20/solid';
|
import { DocumentDuplicateIcon } from '@heroicons/react/20/solid';
|
||||||
|
import { BookmarkSquareIcon, CheckIcon } from '@heroicons/react/24/outline';
|
||||||
import { Button, TextInput, useToast } from '@tih/ui';
|
import { Button, TextInput, useToast } from '@tih/ui';
|
||||||
|
|
||||||
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
|
|
||||||
import { copyProfileLink, getProfileLink } from '~/utils/offers/link';
|
import { copyProfileLink, getProfileLink } from '~/utils/offers/link';
|
||||||
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
type OfferProfileSaveProps = Readonly<{
|
type OfferProfileSaveProps = Readonly<{
|
||||||
profileId: string;
|
profileId: string;
|
||||||
@ -15,16 +20,39 @@ export default function OffersProfileSave({
|
|||||||
token,
|
token,
|
||||||
}: OfferProfileSaveProps) {
|
}: OfferProfileSaveProps) {
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
// Const [isSaving, setSaving] = useState(false);
|
const { event: gaEvent } = useGoogleAnalytics();
|
||||||
// const [isSaved, setSaved] = useState(false);
|
const [isSaved, setSaved] = useState(false);
|
||||||
|
|
||||||
// Const saveProfile = () => {
|
const saveMutation = trpc.useMutation(
|
||||||
// setSaving(true);
|
['offers.user.profile.addToUserProfile'],
|
||||||
// setTimeout(() => {
|
{
|
||||||
// setSaving(false);
|
onError: () => {
|
||||||
// setSaved(true);
|
showToast({
|
||||||
// }, 5);
|
title: `Failed to saved to dashboard!`,
|
||||||
// };
|
variant: 'failure',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
showToast({
|
||||||
|
title: `Saved to your repository!`,
|
||||||
|
variant: 'success',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
saveMutation.mutate({
|
||||||
|
profileId,
|
||||||
|
token: token as string,
|
||||||
|
});
|
||||||
|
setSaved(true);
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.profile_submission_save_to_profile',
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Save to profile in profile submission',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full justify-center">
|
<div className="flex w-full justify-center">
|
||||||
@ -57,24 +85,29 @@ export default function OffersProfileSave({
|
|||||||
title: `Profile edit link copied to clipboard!`,
|
title: `Profile edit link copied to clipboard!`,
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
});
|
});
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.profile_submission_copy_edit_profile_link',
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Copy Edit Profile Link in Profile Submission',
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <p className="mb-5 text-slate-900">
|
<p className="mb-5 text-slate-900">
|
||||||
If you do not want to keep the edit link, you can opt to save this
|
If you do not want to keep the edit link, you can opt to save this
|
||||||
profile under your user account. It will still only be editable by
|
profile under your account's respository. It will still only be
|
||||||
you.
|
editable by you.
|
||||||
</p>
|
</p>
|
||||||
<div className="mb-20">
|
<div className="mb-20">
|
||||||
<Button
|
<Button
|
||||||
disabled={isSaved}
|
disabled={isSaved}
|
||||||
icon={isSaved ? CheckIcon : BookmarkSquareIcon}
|
icon={isSaved ? CheckIcon : BookmarkSquareIcon}
|
||||||
isLoading={isSaving}
|
isLoading={saveMutation.isLoading}
|
||||||
label={isSaved ? 'Saved to user profile' : 'Save to user profile'}
|
label={isSaved ? 'Saved to user profile' : 'Save to user profile'}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={saveProfile}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid';
|
|||||||
import { JobType } from '@prisma/client';
|
import { JobType } from '@prisma/client';
|
||||||
import { Button } from '@tih/ui';
|
import { Button } from '@tih/ui';
|
||||||
|
|
||||||
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
import type { BreadcrumbStep } from '~/components/offers/Breadcrumb';
|
import type { BreadcrumbStep } from '~/components/offers/Breadcrumb';
|
||||||
import { Breadcrumbs } from '~/components/offers/Breadcrumb';
|
import { Breadcrumbs } from '~/components/offers/Breadcrumb';
|
||||||
import BackgroundForm from '~/components/offers/offersSubmission/submissionForm/BackgroundForm';
|
import BackgroundForm from '~/components/offers/offersSubmission/submissionForm/BackgroundForm';
|
||||||
@ -101,6 +102,7 @@ export default function OffersSubmissionForm({
|
|||||||
token: editToken,
|
token: editToken,
|
||||||
});
|
});
|
||||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||||
|
const { event: gaEvent } = useGoogleAnalytics();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pageRef = useRef<HTMLDivElement>(null);
|
const pageRef = useRef<HTMLDivElement>(null);
|
||||||
@ -215,6 +217,11 @@ export default function OffersSubmissionForm({
|
|||||||
} else {
|
} else {
|
||||||
createOrUpdateMutation.mutate({ background, offers });
|
createOrUpdateMutation.mutate({ background, offers });
|
||||||
}
|
}
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.submit_profile',
|
||||||
|
category: 'submission',
|
||||||
|
label: 'Submit profile',
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -278,7 +285,14 @@ export default function OffersSubmissionForm({
|
|||||||
icon={ArrowRightIcon}
|
icon={ArrowRightIcon}
|
||||||
label="Next"
|
label="Next"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => goToNextStep(step)}
|
onClick={() => {
|
||||||
|
goToNextStep(step);
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.profile_submission_navigate_next',
|
||||||
|
category: 'submission',
|
||||||
|
label: 'Navigate next',
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -288,7 +302,14 @@ export default function OffersSubmissionForm({
|
|||||||
icon={ArrowLeftIcon}
|
icon={ArrowLeftIcon}
|
||||||
label="Previous"
|
label="Previous"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => setStep(step - 1)}
|
onClick={() => {
|
||||||
|
setStep(step - 1);
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.profile_submission_navigation_back',
|
||||||
|
category: 'submission',
|
||||||
|
label: 'Navigate back',
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting || isSubmitSuccessful}
|
disabled={isSubmitting || isSubmitSuccessful}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
useToast,
|
useToast,
|
||||||
} from '@tih/ui';
|
} from '@tih/ui';
|
||||||
|
|
||||||
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard';
|
import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard';
|
||||||
import Tooltip from '~/components/offers/util/Tooltip';
|
import Tooltip from '~/components/offers/util/Tooltip';
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ export default function ProfileComments({
|
|||||||
const [currentReply, setCurrentReply] = useState<string>('');
|
const [currentReply, setCurrentReply] = useState<string>('');
|
||||||
const [replies, setReplies] = useState<Array<Reply>>();
|
const [replies, setReplies] = useState<Array<Reply>>();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
|
const { event: gaEvent } = useGoogleAnalytics();
|
||||||
|
|
||||||
const commentsQuery = trpc.useQuery(
|
const commentsQuery = trpc.useQuery(
|
||||||
['offers.comments.getComments', { profileId }],
|
['offers.comments.getComments', { profileId }],
|
||||||
@ -121,6 +123,11 @@ export default function ProfileComments({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyProfileLink(profileId, token);
|
copyProfileLink(profileId, token);
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.copy_profile_edit_link',
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Copy Profile Edit Link',
|
||||||
|
});
|
||||||
showToast({
|
showToast({
|
||||||
title: `Profile edit link copied to clipboard!`,
|
title: `Profile edit link copied to clipboard!`,
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
@ -140,6 +147,11 @@ export default function ProfileComments({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyProfileLink(profileId);
|
copyProfileLink(profileId);
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.copy_profile_public_link',
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Copy Profile Public Link',
|
||||||
|
});
|
||||||
showToast({
|
showToast({
|
||||||
title: `Public profile link copied to clipboard!`,
|
title: `Public profile link copied to clipboard!`,
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
|
@ -27,7 +27,7 @@ export default function OfferTableRow({
|
|||||||
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>
|
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>
|
||||||
<td
|
<td
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'sticky right-0 bg-white py-4 px-6 drop-shadow md:drop-shadow-none',
|
'sticky right-0 py-4 px-6 drop-shadow md:drop-shadow-none',
|
||||||
)}>
|
)}>
|
||||||
<Link
|
<Link
|
||||||
className="text-primary-600 dark:text-primary-500 font-medium hover:underline"
|
className="text-primary-600 dark:text-primary-500 font-medium hover:underline"
|
||||||
|
@ -2,6 +2,7 @@ import clsx from 'clsx';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { DropdownMenu, Spinner } from '@tih/ui';
|
import { DropdownMenu, Spinner } from '@tih/ui';
|
||||||
|
|
||||||
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
import OffersTablePagination from '~/components/offers/table/OffersTablePagination';
|
import OffersTablePagination from '~/components/offers/table/OffersTablePagination';
|
||||||
import {
|
import {
|
||||||
OfferTableFilterOptions,
|
OfferTableFilterOptions,
|
||||||
@ -39,6 +40,7 @@ export default function OffersTable({
|
|||||||
const [selectedFilter, setSelectedFilter] = useState(
|
const [selectedFilter, setSelectedFilter] = useState(
|
||||||
OfferTableFilterOptions[0].value,
|
OfferTableFilterOptions[0].value,
|
||||||
);
|
);
|
||||||
|
const { event: gaEvent } = useGoogleAnalytics();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPagination({
|
setPagination({
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
@ -90,13 +92,18 @@ export default function OffersTable({
|
|||||||
label={itemLabel}
|
label={itemLabel}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedTab(value);
|
setSelectedTab(value);
|
||||||
|
gaEvent({
|
||||||
|
action: `offers.table_filter_yoe_category_${value}`,
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Filter by YOE category',
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<div className="divide-x-slate-200 flex items-center space-x-4 divide-x">
|
<div className="divide-x-slate-200 flex items-center space-x-4 divide-x">
|
||||||
<div className="justify-left flex items-center space-x-2">
|
<div className="justify-left flex items-center space-x-2">
|
||||||
<span>All offers in</span>
|
<span>View all offers in</span>
|
||||||
<CurrencySelector
|
<CurrencySelector
|
||||||
handleCurrencyChange={(value: string) => setCurrency(value)}
|
handleCurrencyChange={(value: string) => setCurrency(value)}
|
||||||
selectedCurrency={currency}
|
selectedCurrency={currency}
|
||||||
|
@ -8,11 +8,11 @@ import {
|
|||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
import offersAnalysis from '~/components/offers/landing/images/offers-analysis.png';
|
import offersAnalysis from '~/components/offers/features/images/offers-analysis.png';
|
||||||
import offersBrowse from '~/components/offers/landing/images/offers-browse.png';
|
import offersBrowse from '~/components/offers/features/images/offers-browse.png';
|
||||||
import offersProfile from '~/components/offers/landing/images/offers-profile.png';
|
import offersProfile from '~/components/offers/features/images/offers-profile.png';
|
||||||
import LeftTextCard from '~/components/offers/landing/LeftTextCard';
|
import LeftTextCard from '~/components/offers/features/LeftTextCard';
|
||||||
import RightTextCard from '~/components/offers/landing/RightTextCard';
|
import RightTextCard from '~/components/offers/features/RightTextCard';
|
||||||
import { HOME_URL } from '~/components/offers/types';
|
import { HOME_URL } from '~/components/offers/types';
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
@ -38,32 +38,32 @@ const features = [
|
|||||||
|
|
||||||
const footerNavigation = {
|
const footerNavigation = {
|
||||||
social: [
|
social: [
|
||||||
{
|
// {
|
||||||
href: '#',
|
// href: '#',
|
||||||
icon: (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
// icon: (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||||
<svg fill="currentColor" viewBox="0 0 24 24" {...props}>
|
// <svg fill="currentColor" viewBox="0 0 24 24" {...props}>
|
||||||
<path
|
// <path
|
||||||
clipRule="evenodd"
|
// clipRule="evenodd"
|
||||||
d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z"
|
// d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z"
|
||||||
fillRule="evenodd"
|
// fillRule="evenodd"
|
||||||
/>
|
// />
|
||||||
</svg>
|
// </svg>
|
||||||
),
|
// ),
|
||||||
name: 'Facebook',
|
// name: 'Facebook',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
href: '#',
|
// href: '#',
|
||||||
icon: (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
// icon: (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||||
<svg fill="currentColor" viewBox="0 0 24 24" {...props}>
|
// <svg fill="currentColor" viewBox="0 0 24 24" {...props}>
|
||||||
<path
|
// <path
|
||||||
clipRule="evenodd"
|
// clipRule="evenodd"
|
||||||
d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z"
|
// d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z"
|
||||||
fillRule="evenodd"
|
// fillRule="evenodd"
|
||||||
/>
|
// />
|
||||||
</svg>
|
// </svg>
|
||||||
),
|
// ),
|
||||||
name: 'Instagram',
|
// name: 'Instagram',
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
href: 'https://github.com/yangshun/tech-interview-handbook',
|
href: 'https://github.com/yangshun/tech-interview-handbook',
|
||||||
icon: (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
icon: (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||||
@ -87,6 +87,11 @@ export default function LandingPage() {
|
|||||||
{/* Hero section */}
|
{/* Hero section */}
|
||||||
<div className="relative h-full">
|
<div className="relative h-full">
|
||||||
<div className="relative px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8">
|
<div className="relative px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8">
|
||||||
|
<img
|
||||||
|
alt="Tech Offers Repo"
|
||||||
|
className="mx-auto mb-8 w-auto"
|
||||||
|
src="/offers-logo.svg"
|
||||||
|
/>
|
||||||
<h1 className="text-center text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
|
<h1 className="text-center text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
|
||||||
<span>Choosing offers </span>
|
<span>Choosing offers </span>
|
||||||
<span className="from-primary-600 -mb-1 mr-2 bg-gradient-to-r to-purple-500 bg-clip-text pb-1 pr-4 italic text-transparent">
|
<span className="from-primary-600 -mb-1 mr-2 bg-gradient-to-r to-purple-500 bg-clip-text pb-1 pr-4 italic text-transparent">
|
||||||
@ -121,16 +126,16 @@ export default function LandingPage() {
|
|||||||
/>
|
/>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<LeftTextCard
|
<LeftTextCard
|
||||||
description="An offer profile includes not only offers that a person received in their application cycle, but also background information such as education and work experience. Use offer profiles to help you better contextualize offers."
|
description="Filter relevant offers by job title, company, submission date, salary and more."
|
||||||
icon={
|
icon={
|
||||||
<InformationCircleIcon
|
<TableCellsIcon
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="h-6 w-6 text-white"
|
className="h-6 w-6 text-white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
imageAlt="Offer table page"
|
imageAlt="Offer table page"
|
||||||
imageSrc={offersProfile}
|
imageSrc={offersBrowse}
|
||||||
title="Choosing an offer needs context"
|
title="Stay informed of recent offers"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-36">
|
<div className="mt-36">
|
||||||
@ -149,16 +154,16 @@ export default function LandingPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-36">
|
<div className="mt-36">
|
||||||
<LeftTextCard
|
<LeftTextCard
|
||||||
description="Filter relevant offers by job title, company, submission date, salary and more."
|
description="An offer profile includes not only offers that a person received in their application cycle, but also background information such as education and work experience. Use offer profiles to help you better contextualize offers."
|
||||||
icon={
|
icon={
|
||||||
<TableCellsIcon
|
<InformationCircleIcon
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="h-6 w-6 text-white"
|
className="h-6 w-6 text-white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
imageAlt="Offer table page"
|
imageAlt="Offer table page"
|
||||||
imageSrc={offersBrowse}
|
imageSrc={offersProfile}
|
||||||
title="Stay informed of recent offers"
|
title="Choosing an offer needs context"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,7 @@ import Link from 'next/link';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Banner } from '@tih/ui';
|
import { Banner } from '@tih/ui';
|
||||||
|
|
||||||
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
import OffersTable from '~/components/offers/table/OffersTable';
|
import OffersTable from '~/components/offers/table/OffersTable';
|
||||||
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
||||||
@ -9,6 +10,7 @@ import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
|||||||
export default function OffersHomePage() {
|
export default function OffersHomePage() {
|
||||||
const [jobTitleFilter, setjobTitleFilter] = useState('software-engineer');
|
const [jobTitleFilter, setjobTitleFilter] = useState('software-engineer');
|
||||||
const [companyFilter, setCompanyFilter] = useState('');
|
const [companyFilter, setCompanyFilter] = useState('');
|
||||||
|
const { event: gaEvent } = useGoogleAnalytics();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex-1 overflow-y-auto">
|
<main className="flex-1 overflow-y-auto">
|
||||||
@ -40,6 +42,11 @@ export default function OffersHomePage() {
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
if (option) {
|
if (option) {
|
||||||
setjobTitleFilter(option.value);
|
setjobTitleFilter(option.value);
|
||||||
|
gaEvent({
|
||||||
|
action: `offers.table_filter_job_title_${option.value}`,
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Filter by job title',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -50,6 +57,11 @@ export default function OffersHomePage() {
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
if (option) {
|
if (option) {
|
||||||
setCompanyFilter(option.value);
|
setCompanyFilter(option.value);
|
||||||
|
gaEvent({
|
||||||
|
action: 'offers.table_filter_company',
|
||||||
|
category: 'engagement',
|
||||||
|
label: 'Filter by company',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -70,7 +70,12 @@ export default function OffersSubmissionResult() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{getAnalysis.isLoading && (
|
{getAnalysis.isLoading && (
|
||||||
<Spinner className="m-10" display="block" size="lg" />
|
<div className="flex h-screen w-screen">
|
||||||
|
<div className="m-auto mx-auto w-screen justify-center">
|
||||||
|
<Spinner display="block" size="lg" />
|
||||||
|
<div className="text-center">Loading...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{!getAnalysis.isLoading && (
|
{!getAnalysis.isLoading && (
|
||||||
<div ref={pageRef} className="fixed h-full w-full overflow-y-scroll">
|
<div ref={pageRef} className="fixed h-full w-full overflow-y-scroll">
|
||||||
@ -98,6 +103,7 @@ export default function OffersSubmissionResult() {
|
|||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Button
|
<Button
|
||||||
|
addonPosition="start"
|
||||||
icon={ArrowLeftIcon}
|
icon={ArrowLeftIcon}
|
||||||
label="Previous"
|
label="Previous"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
Reference in New Issue
Block a user