Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 48 additions & 13 deletions src/components/features/evaluator/response.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ export function Response({ label, type, response, userId }: ResponseProps) {
<Input value={response as string} readOnly />
) : type === "booleanMap" ? (
<BooleanMap value={response as Record<string, boolean>} />
) : type === "resume" ? (
<ResumeField href={resumeLink ?? (response as string)} loading={loading} />
) : loading ? (
<Skeleton />
) : (
<LinkField href={resumeLink ?? (response as string)} />
<LinkField href={response as string} />
)}
</div>
);
Expand All @@ -64,31 +66,64 @@ const BooleanMap = ({ value }: { value?: Record<string, boolean> }) => {
);
};

const ResumeField = ({ href, loading }: { href?: string; loading: boolean }) => {
if (loading) return <Skeleton />;

const value = href ?? "";
if (!value.trim()) {
return <Input value="" readOnly placeholder="No response" />;
}

const handleClipboard = () => {
navigator.clipboard.writeText(value);
toast("Copied link to clipboard!");
};

return (
<div className="flex items-center gap-2">
<Badge>Uploaded</Badge>
<a
href={value}
target="_blank"
rel="noreferrer noopener"
className={cn(buttonVariants({ variant: "outline" }), "gap-2")}
>
View resume
<ExternalLink />
</a>
<Button size="icon" variant="outline" onClick={handleClipboard}>
<Link />
</Button>
</div>
);
};

const LinkField = ({ href }: { href?: string }) => {
const value = href ?? "";
const isUrl = /^https?:\/\//i.test(value.trim());

const handleClipboard = () => {
if (!href) return;
navigator.clipboard.writeText(href);
if (!value) return;
navigator.clipboard.writeText(value);
toast("Copied link to clipboard!");
};

const hasLink = Boolean(href);
if (!isUrl) {
return <Input value={value} readOnly placeholder={value ? undefined : "No response"} />;
}

return (
<div className="relative flex items-center gap-2">
<a
href={hasLink ? href : undefined}
href={value}
target="_blank"
rel="noreferrer noopener"
className={cn(
buttonVariants({ variant: "outline" }),
"flex-grow justify-between",
!hasLink && "pointer-events-none cursor-not-allowed opacity-50",
)}
className={cn(buttonVariants({ variant: "outline" }), "flex-grow justify-between")}
>
<div className="flex-1 truncate">{hasLink ? "Open link in new tab" : "No response"}</div>
{hasLink && <ExternalLink />}
<div className="flex-1 truncate">Open link in new tab</div>
<ExternalLink />
</a>
<Button size="icon" variant="outline" onClick={handleClipboard} disabled={!hasLink}>
<Button size="icon" variant="outline" onClick={handleClipboard}>
<Link />
</Button>
</div>
Expand Down
15 changes: 14 additions & 1 deletion src/components/features/hackerapp/hacker-app-question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import {
getEligibleSources,
} from "./hacker-app-conditions";
import type { UsedFieldsRegistry } from "./hacker-app-main";
import { LEGAL_NAME_FORM_INPUTS, isFormInputAllowedInSection } from "./hacker-app-sections";
import {
LEGAL_NAME_FORM_INPUTS,
isFormInputAllowedInSection,
isQuestionTypeAllowedInSection,
} from "./hacker-app-sections";

const QUESTION_TYPES: HackerApplicationQuestionType[] = [
"Long Answer",
Expand All @@ -44,6 +48,10 @@ const QUESTION_TYPES: HackerApplicationQuestionType[] = [
"School",
"Major",
"Country",
"Github",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These four fixed questions are available in every section, but the portal saves their answers under the selected section. For example, GitHub in Basic Info writes basicInfo.github instead of skills.github (I see in your Portal PR these are meant to be in skills section only). Could we limit these types to Skills, or otherwise ensure they always save to the intended skills.* fields?

"LinkedIn",
"Portfolio Website",
"Resume",
];

// Question types that if selected, bricks the select field and locks in the value
Expand All @@ -53,6 +61,10 @@ const QUESTION_TYPES_UNIQUE: HackerApplicationQuestionType[] = [
"School",
"Major",
"Country",
"Github",
"LinkedIn",
"Portfolio Website",
"Resume",
];

export const SHOW_FORM_INPUT: HackerApplicationQuestionType[] = [
Expand Down Expand Up @@ -132,6 +144,7 @@ export const HackerAppQuestion = memo(function HackerAppQuestion({
);
const usableQuestionTypes = QUESTION_TYPES?.filter(
(qt) =>
isQuestionTypeAllowedInSection(qt, section) &&
(qt !== "Full Legal Name" || !hasSplitLegalName) &&
(!QUESTION_TYPES_UNIQUE.includes(qt) || !usedFieldsRegistry.questionType.has(qt)),
);
Expand Down
19 changes: 19 additions & 0 deletions src/components/features/hackerapp/hacker-app-sections.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {
HackerApplicationQuestionFormInputField,
HackerApplicationQuestionType,
HackerApplicationSections,
} from "@/lib/firebase/types";

Expand Down Expand Up @@ -69,3 +70,21 @@ export const isFormInputAllowedInSection = (
const pinned = SECTION_BY_FORM_INPUT[formInput];
return pinned === undefined || pinned === section;
};

export const QUESTION_TYPE_SECTION: Partial<
Record<HackerApplicationQuestionType, HackerApplicationSections>
> = {
Portfolio: "Skills",
Github: "Skills",
LinkedIn: "Skills",
"Portfolio Website": "Skills",
Resume: "Skills",
};

export const isQuestionTypeAllowedInSection = (
questionType: HackerApplicationQuestionType,
section: HackerApplicationSections,
): boolean => {
const pinned = QUESTION_TYPE_SECTION[questionType];
return pinned === undefined || pinned === section;
};
6 changes: 5 additions & 1 deletion src/lib/firebase/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,11 @@ export type HackerApplicationQuestionType =
| "Dropdown"
| "School"
| "Major"
| "Country";
| "Country"
| "Github"
| "LinkedIn"
| "Portfolio Website"
| "Resume";
// keep in sync with portal-v2 src/lib/firebase/types/hacker-app-questions.ts, which renders these
export const FORM_INPUT_FIELDS = [
"academicYear",
Expand Down
Loading