mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-30 05:34:33 +08:00
[resumes][feat] Add sign in buttons on browse page (#350)
* [resumes][fix] Add gap between sections in resume list item * [resumes][refactor] Abstract out sign in button * [resumes][feat] Add sign in buttons on browse page
This commit is contained in:
24
apps/portal/src/components/resumes/SignInButton.tsx
Normal file
24
apps/portal/src/components/resumes/SignInButton.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { signIn } from 'next-auth/react';
|
||||||
|
|
||||||
|
type Props = Readonly<{
|
||||||
|
text: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export default function SignInButton({ text }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center pt-4">
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
className="text-primary-800 hover:text-primary-500"
|
||||||
|
href="/api/auth/signin"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
signIn();
|
||||||
|
}}>
|
||||||
|
Sign in
|
||||||
|
</a>{' '}
|
||||||
|
{text}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -14,7 +14,7 @@ type Props = Readonly<{
|
|||||||
export default function BrowseListItem({ href, resumeInfo }: Props) {
|
export default function BrowseListItem({ href, resumeInfo }: Props) {
|
||||||
return (
|
return (
|
||||||
<Link href={href}>
|
<Link href={href}>
|
||||||
<div className="grid grid-cols-8 border-b border-slate-200 p-4">
|
<div className="grid grid-cols-8 gap-4 border-b border-slate-200 p-4">
|
||||||
<div className="col-span-4">
|
<div className="col-span-4">
|
||||||
{resumeInfo.title}
|
{resumeInfo.title}
|
||||||
<div className="mt-2 flex items-center justify-start text-xs text-indigo-500">
|
<div className="mt-2 flex items-center justify-start text-xs text-indigo-500">
|
||||||
|
@ -19,17 +19,15 @@ export default function ResumeListItems({ isLoading, resumes }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="col-span-10 pr-8">
|
<ul role="list">
|
||||||
<ul role="list">
|
{resumes.map((resumeObj: Resume) => (
|
||||||
{resumes.map((resumeObj: Resume) => (
|
<li key={resumeObj.id}>
|
||||||
<li key={resumeObj.id}>
|
<ResumseListItem
|
||||||
<ResumseListItem
|
href={`resumes/${resumeObj.id}`}
|
||||||
href={`resumes/${resumeObj.id}`}
|
resumeInfo={resumeObj}
|
||||||
resumeInfo={resumeObj}
|
/>
|
||||||
/>
|
</li>
|
||||||
</li>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
|
import { useSession } from 'next-auth/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Tabs } from '@tih/ui';
|
import { Tabs } from '@tih/ui';
|
||||||
|
import { Button } from '@tih/ui';
|
||||||
|
|
||||||
import { trpc } from '~/utils/trpc';
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
import CommentListItems from './CommentListItems';
|
import CommentListItems from './CommentListItems';
|
||||||
import CommentsListButton from './CommentsListButton';
|
|
||||||
import { COMMENTS_SECTIONS } from './constants';
|
import { COMMENTS_SECTIONS } from './constants';
|
||||||
|
import SignInButton from '../SignInButton';
|
||||||
|
|
||||||
type CommentsListProps = Readonly<{
|
type CommentsListProps = Readonly<{
|
||||||
resumeId: string;
|
resumeId: string;
|
||||||
@ -16,13 +18,27 @@ export default function CommentsList({
|
|||||||
resumeId,
|
resumeId,
|
||||||
setShowCommentsForm,
|
setShowCommentsForm,
|
||||||
}: CommentsListProps) {
|
}: CommentsListProps) {
|
||||||
|
const { data: sessionData } = useSession();
|
||||||
const [tab, setTab] = useState(COMMENTS_SECTIONS[0].value);
|
const [tab, setTab] = useState(COMMENTS_SECTIONS[0].value);
|
||||||
|
|
||||||
const commentsQuery = trpc.useQuery(['resumes.reviews.list', { resumeId }]);
|
const commentsQuery = trpc.useQuery(['resumes.reviews.list', { resumeId }]);
|
||||||
|
const renderButton = () => {
|
||||||
|
if (sessionData === null) {
|
||||||
|
return <SignInButton text="to join discussion" />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
display="block"
|
||||||
|
label="Add your review"
|
||||||
|
variant="tertiary"
|
||||||
|
onClick={() => setShowCommentsForm(true)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<CommentsListButton setShowCommentsForm={setShowCommentsForm} />
|
{renderButton()}
|
||||||
<Tabs
|
<Tabs
|
||||||
label="comments"
|
label="comments"
|
||||||
tabs={COMMENTS_SECTIONS}
|
tabs={COMMENTS_SECTIONS}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import { signIn, useSession } from 'next-auth/react';
|
|
||||||
import { Button } from '@tih/ui';
|
|
||||||
|
|
||||||
type CommentsListButtonProps = {
|
|
||||||
setShowCommentsForm: (show: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function CommentsListButton({
|
|
||||||
setShowCommentsForm,
|
|
||||||
}: CommentsListButtonProps) {
|
|
||||||
const { data: session, status } = useSession();
|
|
||||||
const isSessionLoading = status === 'loading';
|
|
||||||
|
|
||||||
// Don't render anything
|
|
||||||
if (isSessionLoading) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not signed in
|
|
||||||
if (session == null) {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<p>
|
|
||||||
<a
|
|
||||||
className="text-primary-800 hover:text-primary-500"
|
|
||||||
href="/api/auth/signin"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
signIn();
|
|
||||||
}}>
|
|
||||||
Sign in
|
|
||||||
</a>{' '}
|
|
||||||
to join discussion
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signed in. Return Add review button
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
display="block"
|
|
||||||
label="Add your review"
|
|
||||||
variant="tertiary"
|
|
||||||
onClick={() => setShowCommentsForm(true)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -23,6 +23,7 @@ import {
|
|||||||
import FilterPill from '~/components/resumes/browse/FilterPill';
|
import FilterPill from '~/components/resumes/browse/FilterPill';
|
||||||
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
||||||
import ResumeReviewsTitle from '~/components/resumes/ResumeReviewsTitle';
|
import ResumeReviewsTitle from '~/components/resumes/ResumeReviewsTitle';
|
||||||
|
import SignInButton from '~/components/resumes/SignInButton';
|
||||||
|
|
||||||
import { trpc } from '~/utils/trpc';
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
@ -52,29 +53,44 @@ export default function ResumeHomePage() {
|
|||||||
const [tabsValue, setTabsValue] = useState(BROWSE_TABS_VALUES.ALL);
|
const [tabsValue, setTabsValue] = useState(BROWSE_TABS_VALUES.ALL);
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const [resumes, setResumes] = useState<Array<Resume>>([]);
|
const [resumes, setResumes] = useState<Array<Resume>>([]);
|
||||||
|
const [renderSignInButton, setRenderSignInButton] = useState(false);
|
||||||
|
const [signInButtonText, setSignInButtonText] = useState('');
|
||||||
|
|
||||||
const allResumesQuery = trpc.useQuery(['resumes.resume.findAll'], {
|
const allResumesQuery = trpc.useQuery(['resumes.resume.findAll'], {
|
||||||
enabled: tabsValue === BROWSE_TABS_VALUES.ALL,
|
enabled: tabsValue === BROWSE_TABS_VALUES.ALL,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setResumes(data);
|
setResumes(data);
|
||||||
|
setRenderSignInButton(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const starredResumesQuery = trpc.useQuery(
|
const starredResumesQuery = trpc.useQuery(
|
||||||
['resumes.resume.user.findUserStarred'],
|
['resumes.resume.user.findUserStarred'],
|
||||||
{
|
{
|
||||||
enabled: tabsValue === BROWSE_TABS_VALUES.STARRED,
|
enabled: tabsValue === BROWSE_TABS_VALUES.STARRED,
|
||||||
|
onError: () => {
|
||||||
|
setResumes([]);
|
||||||
|
setRenderSignInButton(true);
|
||||||
|
setSignInButtonText('to view starred resumes');
|
||||||
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setResumes(data);
|
setResumes(data);
|
||||||
},
|
},
|
||||||
|
retry: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const myResumesQuery = trpc.useQuery(
|
const myResumesQuery = trpc.useQuery(
|
||||||
['resumes.resume.user.findUserCreated'],
|
['resumes.resume.user.findUserCreated'],
|
||||||
{
|
{
|
||||||
enabled: tabsValue === BROWSE_TABS_VALUES.MY,
|
enabled: tabsValue === BROWSE_TABS_VALUES.MY,
|
||||||
|
onError: () => {
|
||||||
|
setResumes([]);
|
||||||
|
setRenderSignInButton(true);
|
||||||
|
setSignInButtonText('to view your submitted resumes');
|
||||||
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setResumes(data);
|
setResumes(data);
|
||||||
},
|
},
|
||||||
|
retry: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -270,14 +286,17 @@ export default function ResumeHomePage() {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ResumeListItems
|
<div className="col-span-10 pr-8">
|
||||||
isLoading={
|
{renderSignInButton && <SignInButton text={signInButtonText} />}
|
||||||
allResumesQuery.isFetching ||
|
<ResumeListItems
|
||||||
starredResumesQuery.isFetching ||
|
isLoading={
|
||||||
myResumesQuery.isFetching
|
allResumesQuery.isFetching ||
|
||||||
}
|
starredResumesQuery.isFetching ||
|
||||||
resumes={resumes}
|
myResumesQuery.isFetching
|
||||||
/>
|
}
|
||||||
|
resumes={resumes}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user