mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-08-03 04:04:24 +08:00
[offers][fix] fix profile page mobile compatible style (#489)
* [offers][fix] fix offer profile page mobile compatibility * [offers][style] style profile offer card
This commit is contained in:
@ -128,7 +128,7 @@ export default function OfferCard({
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="mx-8 my-4 block rounded-lg bg-white py-4 shadow-md">
|
||||
<div className="mx-8 my-4 block rounded-md border-b border-gray-300 bg-white py-4">
|
||||
<UpperSection />
|
||||
<BottomSection />
|
||||
</div>
|
||||
|
@ -110,108 +110,115 @@ export default function ProfileComments({
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="m-4 h-full">
|
||||
<div className="flex-end flex justify-end space-x-4">
|
||||
{isEditable && (
|
||||
<Tooltip tooltipContent="Copy this link to edit your profile later">
|
||||
<Button
|
||||
addonPosition="start"
|
||||
disabled={isDisabled}
|
||||
icon={ClipboardDocumentIcon}
|
||||
isLabelHidden={false}
|
||||
label="Copy profile edit link"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
copyProfileLink(profileId, token);
|
||||
gaEvent({
|
||||
action: 'offers.copy_profile_edit_link',
|
||||
category: 'engagement',
|
||||
label: 'Copy Profile Edit Link',
|
||||
});
|
||||
showToast({
|
||||
title: `Profile edit link copied to clipboard!`,
|
||||
variant: 'success',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip tooltipContent="Share this profile with your friends">
|
||||
<Button
|
||||
addonPosition="start"
|
||||
disabled={isDisabled}
|
||||
icon={ShareIcon}
|
||||
isLabelHidden={false}
|
||||
label="Copy public link"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
copyProfileLink(profileId);
|
||||
gaEvent({
|
||||
action: 'offers.copy_profile_public_link',
|
||||
category: 'engagement',
|
||||
label: 'Copy Profile Public Link',
|
||||
});
|
||||
showToast({
|
||||
title: `Public profile link copied to clipboard!`,
|
||||
variant: 'success',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2>
|
||||
{isEditable || session?.user?.name ? (
|
||||
<div>
|
||||
<TextArea
|
||||
label={`Comment as ${
|
||||
isEditable ? profileName : session?.user?.name ?? 'anonymous'
|
||||
}`}
|
||||
placeholder="Type your comment here"
|
||||
value={currentReply}
|
||||
onChange={(value) => setCurrentReply(value)}
|
||||
/>
|
||||
<div className="mt-2 flex w-full justify-end">
|
||||
<div className="w-fit">
|
||||
<Button
|
||||
disabled={
|
||||
commentsQuery.isLoading ||
|
||||
!currentReply.length ||
|
||||
createCommentMutation.isLoading
|
||||
}
|
||||
display="block"
|
||||
isLabelHidden={false}
|
||||
isLoading={createCommentMutation.isLoading}
|
||||
label="Comment"
|
||||
size="sm"
|
||||
variant="primary"
|
||||
onClick={() => handleComment(currentReply)}
|
||||
/>
|
||||
<div className="bh-white h-fit px-4 md:h-[calc(100vh-4.5rem)] md:overflow-y-auto">
|
||||
<div className="bg-white pt-4 md:sticky md:top-0">
|
||||
<div className="flex justify-end">
|
||||
<div className="grid w-fit space-y-2 md:grid-cols-1 lg:grid-cols-2 lg:space-y-0 lg:space-x-4">
|
||||
<div className="col-span-1 flex justify-end">
|
||||
{isEditable && (
|
||||
<Tooltip tooltipContent="Copy this link to edit your profile later">
|
||||
<Button
|
||||
addonPosition="start"
|
||||
disabled={isDisabled}
|
||||
icon={ClipboardDocumentIcon}
|
||||
isLabelHidden={false}
|
||||
label="Copy edit link"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
copyProfileLink(profileId, token);
|
||||
gaEvent({
|
||||
action: 'offers.copy_profile_edit_link',
|
||||
category: 'engagement',
|
||||
label: 'Copy Profile Edit Link',
|
||||
});
|
||||
showToast({
|
||||
title: `Profile edit link copied to clipboard!`,
|
||||
variant: 'success',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-1 flex justify-end">
|
||||
<Tooltip tooltipContent="Share this profile with your friends">
|
||||
<Button
|
||||
addonPosition="start"
|
||||
disabled={isDisabled}
|
||||
icon={ShareIcon}
|
||||
isLabelHidden={false}
|
||||
label="Copy public link"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
copyProfileLink(profileId);
|
||||
gaEvent({
|
||||
action: 'offers.copy_profile_public_link',
|
||||
category: 'engagement',
|
||||
label: 'Copy Profile Public Link',
|
||||
});
|
||||
showToast({
|
||||
title: `Public profile link copied to clipboard!`,
|
||||
variant: 'success',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<HorizontalDivider />
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
className="mb-5"
|
||||
display="block"
|
||||
href={loginPageHref()}
|
||||
label="Sign in to join discussion"
|
||||
variant="tertiary"
|
||||
/>
|
||||
)}
|
||||
<div className="h-full overflow-y-auto">
|
||||
<div className="h-content mb-96 w-full">
|
||||
{replies?.map((reply: Reply) => (
|
||||
<ExpandableCommentCard
|
||||
key={reply.id}
|
||||
comment={reply}
|
||||
profileId={profileId}
|
||||
token={isEditable ? token : undefined}
|
||||
|
||||
<h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2>
|
||||
{isEditable || session?.user?.name ? (
|
||||
<div>
|
||||
<TextArea
|
||||
label={`Comment as ${
|
||||
isEditable ? profileName : session?.user?.name ?? 'anonymous'
|
||||
}`}
|
||||
placeholder="Type your comment here"
|
||||
value={currentReply}
|
||||
onChange={(value) => setCurrentReply(value)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-2 flex w-full justify-end">
|
||||
<div className="w-fit">
|
||||
<Button
|
||||
disabled={
|
||||
commentsQuery.isLoading ||
|
||||
!currentReply.length ||
|
||||
createCommentMutation.isLoading
|
||||
}
|
||||
display="block"
|
||||
isLabelHidden={false}
|
||||
isLoading={createCommentMutation.isLoading}
|
||||
label="Comment"
|
||||
size="sm"
|
||||
variant="primary"
|
||||
onClick={() => handleComment(currentReply)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<HorizontalDivider />
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
className="mb-5"
|
||||
display="block"
|
||||
href={loginPageHref()}
|
||||
label="Sign in to join discussion"
|
||||
variant="tertiary"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="h-full w-full">
|
||||
{replies?.map((reply: Reply) => (
|
||||
<ExpandableCommentCard
|
||||
key={reply.id}
|
||||
comment={reply}
|
||||
profileId={profileId}
|
||||
token={isEditable ? token : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -65,7 +65,7 @@ export default function ProfileHeader({
|
||||
{ profileId: offerProfileId as string, userId: session?.user?.id },
|
||||
],
|
||||
{
|
||||
onSuccess: (res) => {
|
||||
onSuccess: (res: boolean) => {
|
||||
setSaved(res);
|
||||
},
|
||||
},
|
||||
@ -233,57 +233,60 @@ export default function ProfileHeader({
|
||||
const { experiences, totalYoe, specificYoes, profileName } = background;
|
||||
|
||||
return (
|
||||
<div className="h-40 bg-white p-4">
|
||||
<div className="justify-left flex h-1/2">
|
||||
<div className="mx-4 mt-2 h-16 w-16">
|
||||
<ProfilePhotoHolder />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="justify-left flex flex-1">
|
||||
<h2 className="flex w-4/5 text-2xl font-bold">
|
||||
<div className="grid-rows-2 bg-white p-4">
|
||||
<div className="flex grid grid-cols-5 md:grid-cols-7">
|
||||
<div className="jsutify-start col-span-5 flex">
|
||||
<div className="ml-0 mr-2 mt-2 h-16 w-16 md:mx-4">
|
||||
<ProfilePhotoHolder />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="flex text-2xl font-bold">
|
||||
{profileName ?? 'anonymous'}
|
||||
</h2>
|
||||
{isEditable && (
|
||||
<div className="flex h-8 w-1/5 justify-end">
|
||||
{renderActionList()}
|
||||
{(experiences[0]?.companyName ||
|
||||
experiences[0]?.jobLevel ||
|
||||
experiences[0]?.jobTitle) && (
|
||||
<div className="flex flex-row">
|
||||
<span>
|
||||
<BuildingOffice2Icon className="mr-2.5 h-5 w-5" />
|
||||
</span>
|
||||
<p>
|
||||
<span className="mr-2 font-bold">Current:</span>
|
||||
{`${experiences[0].companyName || ''} ${
|
||||
experiences[0].jobLevel || ''
|
||||
} ${experiences[0].jobTitle || ''} ${
|
||||
experiences[0].jobType
|
||||
? `(${JobTypeLabel[experiences[0].jobType]})`
|
||||
: ''
|
||||
}`}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{(experiences[0]?.companyName ||
|
||||
experiences[0]?.jobLevel ||
|
||||
experiences[0]?.jobTitle) && (
|
||||
<div className="flex flex-row">
|
||||
<BuildingOffice2Icon className="mr-2.5 h-5" />
|
||||
<span className="mr-2 font-bold">Current:</span>
|
||||
<span>
|
||||
{`${experiences[0].companyName || ''} ${
|
||||
experiences[0].jobLevel || ''
|
||||
} ${experiences[0].jobTitle || ''} ${
|
||||
experiences[0].jobType
|
||||
? `(${JobTypeLabel[experiences[0].jobType]})`
|
||||
: ''
|
||||
}`}
|
||||
</span>
|
||||
<CalendarDaysIcon className="mr-2.5 h-5" />
|
||||
<p>
|
||||
<span className="mr-2 font-bold">YOE:</span>
|
||||
<span className="mr-4">{totalYoe}</span>
|
||||
{specificYoes &&
|
||||
specificYoes.length > 0 &&
|
||||
specificYoes.map(({ domain, yoe }) => {
|
||||
return (
|
||||
<span
|
||||
key={domain}
|
||||
className="mr-4">{`${domain}: ${yoe}`}</span>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row">
|
||||
<CalendarDaysIcon className="mr-2.5 h-5" />
|
||||
<span className="mr-2 font-bold">YOE:</span>
|
||||
<span className="mr-4">{totalYoe}</span>
|
||||
{specificYoes &&
|
||||
specificYoes.length > 0 &&
|
||||
specificYoes.map(({ domain, yoe }) => {
|
||||
return (
|
||||
<span
|
||||
key={domain}
|
||||
className="mr-4">{`${domain}: ${yoe}`}</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{isEditable && (
|
||||
<div className="col-span-2 col-end-6 flex h-8 justify-end md:col-end-8 md:pt-0">
|
||||
{renderActionList()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className="mt-4">
|
||||
<Tabs
|
||||
label="Profile Detail Navigation"
|
||||
tabs={profileDetailTabs}
|
||||
|
@ -27,7 +27,7 @@ export default function OfferTableRow({
|
||||
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>
|
||||
<td
|
||||
className={clsx(
|
||||
'sticky right-0 py-4 px-6 drop-shadow md:drop-shadow-none',
|
||||
'sticky right-0 bg-white px-6 py-4 drop-shadow lg:drop-shadow-none',
|
||||
)}>
|
||||
<Link
|
||||
className="text-primary-600 dark:text-primary-500 font-medium hover:underline"
|
||||
|
@ -76,7 +76,7 @@ export default function OffersTable({
|
||||
|
||||
function renderFilters() {
|
||||
return (
|
||||
<div className="m-4 flex items-center justify-between">
|
||||
<div className="m-4 flex grid grid-cols-1 items-center justify-between gap-6 sm:grid-cols-4">
|
||||
<DropdownMenu
|
||||
align="start"
|
||||
label={
|
||||
@ -101,7 +101,7 @@ export default function OffersTable({
|
||||
/>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
<div className="divide-x-slate-200 flex items-center space-x-4 divide-x">
|
||||
<div className="divide-x-slate-200 col-span-3 flex items-center justify-end space-x-4 divide-x">
|
||||
<div className="justify-left flex items-center space-x-2">
|
||||
<span>View all offers in</span>
|
||||
<CurrencySelector
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Pagination } from '@tih/ui';
|
||||
|
||||
import type { Paging } from '~/types/offers';
|
||||
@ -15,30 +16,36 @@ export default function OffersTablePagination({
|
||||
startNumber,
|
||||
handlePageChange,
|
||||
}: OffersTablePaginationProps) {
|
||||
const [screenWidth, setScreenWidth] = useState(0);
|
||||
useEffect(() => {
|
||||
setScreenWidth(window.innerWidth);
|
||||
}, []);
|
||||
return (
|
||||
<nav
|
||||
aria-label="Table navigation"
|
||||
className="flex items-center justify-between p-4">
|
||||
<span className="text-sm font-normal text-slate-500">
|
||||
Showing
|
||||
<span className="font-semibold text-slate-900">
|
||||
{` ${startNumber} - ${endNumber} `}
|
||||
</span>
|
||||
{`of `}
|
||||
<span className="font-semibold text-slate-900">
|
||||
{pagination.totalItems}
|
||||
</span>
|
||||
</span>
|
||||
<Pagination
|
||||
current={pagination.currentPage + 1}
|
||||
end={pagination.numOfPages}
|
||||
label="Pagination"
|
||||
pagePadding={2}
|
||||
start={1}
|
||||
onSelect={(currPage) => {
|
||||
handlePageChange(currPage - 1);
|
||||
}}
|
||||
/>
|
||||
<nav aria-label="Table navigation" className="p-4">
|
||||
<div className="flex grid grid-cols-1 items-center md:grid-cols-2">
|
||||
<div className="mb-2 text-sm font-normal text-slate-500 md:mb-0">
|
||||
Showing
|
||||
<span className="font-semibold text-slate-900">
|
||||
{` ${startNumber} - ${endNumber} `}
|
||||
</span>
|
||||
{`of `}
|
||||
<span className="font-semibold text-slate-900">
|
||||
{pagination.totalItems}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex md:justify-end">
|
||||
<Pagination
|
||||
current={pagination.currentPage + 1}
|
||||
end={pagination.numOfPages}
|
||||
label="Pagination"
|
||||
pagePadding={screenWidth > 500 ? 2 : 0}
|
||||
start={1}
|
||||
onSelect={(currPage) => {
|
||||
handlePageChange(currPage - 1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { signIn, useSession } from 'next-auth/react';
|
||||
import { useState } from 'react';
|
||||
import { Button, Spinner } from '@tih/ui';
|
||||
|
||||
import DashboardOfferCard from '~/components/offers/dashboard/DashboardProfileCard';
|
||||
import DashboardProfileCard from '~/components/offers/dashboard/DashboardProfileCard';
|
||||
|
||||
import { trpc } from '~/utils/trpc';
|
||||
|
||||
@ -69,7 +69,7 @@ export default function ProfilesDashboard() {
|
||||
</div>
|
||||
)}
|
||||
{!userProfilesQuery.isLoading && (
|
||||
<div className="mt-8 overflow-y-auto">
|
||||
<div className="overflow-y-auto py-8">
|
||||
<h1 className="mx-auto mb-4 w-3/4 text-start text-4xl font-bold text-slate-900">
|
||||
Your dashboard
|
||||
</h1>
|
||||
@ -83,7 +83,7 @@ export default function ProfilesDashboard() {
|
||||
<li
|
||||
key={profile.id}
|
||||
className="overflow-hidden bg-white px-4 py-4 shadow sm:rounded-md sm:px-6">
|
||||
<DashboardOfferCard key={profile.id} profile={profile} />
|
||||
<DashboardProfileCard key={profile.id} profile={profile} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -202,17 +202,19 @@ export default function OfferProfile() {
|
||||
</div>
|
||||
)}
|
||||
{!getProfileQuery.isLoading && !getProfileQuery.isError && (
|
||||
<div className="mb-4 flex flex h-screen w-screen items-center justify-center divide-x">
|
||||
<div className="h-full w-2/3 divide-y">
|
||||
<ProfileHeader
|
||||
background={background}
|
||||
handleDelete={handleDelete}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
selectedTab={selectedTab}
|
||||
setSelectedTab={setSelectedTab}
|
||||
/>
|
||||
<div className="h-4/5 w-full overflow-y-scroll pb-32">
|
||||
<div className="h-fuill flex grid w-full grid-cols-1 items-center justify-center divide-x overflow-y-auto md:grid-cols-3">
|
||||
<div className="col-span-1 flex h-full flex-col divide-y md:col-span-2 md:overflow-y-auto">
|
||||
<div className="h-fit md:sticky md:top-0">
|
||||
<ProfileHeader
|
||||
background={background}
|
||||
handleDelete={handleDelete}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
selectedTab={selectedTab}
|
||||
setSelectedTab={setSelectedTab}
|
||||
/>
|
||||
</div>
|
||||
<div className="pb-4">
|
||||
<ProfileDetails
|
||||
analysis={analysis}
|
||||
background={background}
|
||||
@ -224,7 +226,7 @@ export default function OfferProfile() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full w-1/3 bg-white">
|
||||
<div className="col-span-1 h-full bg-white">
|
||||
<ProfileComments
|
||||
isDisabled={deleteMutation.isLoading}
|
||||
isEditable={isEditable}
|
||||
|
Reference in New Issue
Block a user