This commit is contained in:
Amruth Pillai
2026-02-26 22:39:05 +01:00
parent 269dbc600f
commit eeaac9a86f
11 changed files with 1065 additions and 1362 deletions
@@ -55,114 +55,129 @@ function renderItemByType(type: CustomSectionType, item: CustomSectionItem, item
.exhaustive();
}
type SectionProps = { id: string };
export function getSectionComponent(
section: "summary" | SectionType | (string & {}),
{ sectionClassName, itemClassName }: SectionComponentProps = {},
) {
return match(section)
.with("summary", () => {
const SummarySection = ({ id: _id }: { id: string }) => <PageSummary className={sectionClassName} />;
const SummarySection = (_: SectionProps) => <PageSummary className={sectionClassName} />;
return SummarySection;
})
.with("profiles", () => {
const ProfilesSection = ({ id: _id }: { id: string }) => (
const ProfilesSection = (_: SectionProps) => (
<PageSection type="profiles" className={sectionClassName}>
{(item) => <ProfilesItem {...item} className={itemClassName} />}
</PageSection>
);
return ProfilesSection;
})
.with("experience", () => {
const ExperienceSection = ({ id: _id }: { id: string }) => (
const ExperienceSection = (_: SectionProps) => (
<PageSection type="experience" className={sectionClassName}>
{(item) => <ExperienceItem {...item} className={itemClassName} />}
</PageSection>
);
return ExperienceSection;
})
.with("education", () => {
const EducationSection = ({ id: _id }: { id: string }) => (
const EducationSection = (_: SectionProps) => (
<PageSection type="education" className={sectionClassName}>
{(item) => <EducationItem {...item} className={itemClassName} />}
</PageSection>
);
return EducationSection;
})
.with("projects", () => {
const ProjectsSection = ({ id: _id }: { id: string }) => (
const ProjectsSection = (_: SectionProps) => (
<PageSection type="projects" className={sectionClassName}>
{(item) => <ProjectsItem {...item} className={itemClassName} />}
</PageSection>
);
return ProjectsSection;
})
.with("skills", () => {
const SkillsSection = ({ id: _id }: { id: string }) => (
const SkillsSection = (_: SectionProps) => (
<PageSection type="skills" className={sectionClassName}>
{(item) => <SkillsItem {...item} className={itemClassName} />}
</PageSection>
);
return SkillsSection;
})
.with("languages", () => {
const LanguagesSection = ({ id: _id }: { id: string }) => (
const LanguagesSection = (_: SectionProps) => (
<PageSection type="languages" className={sectionClassName}>
{(item) => <LanguagesItem {...item} className={itemClassName} />}
</PageSection>
);
return LanguagesSection;
})
.with("interests", () => {
const InterestsSection = ({ id: _id }: { id: string }) => (
const InterestsSection = (_: SectionProps) => (
<PageSection type="interests" className={sectionClassName}>
{(item) => <InterestsItem {...item} className={itemClassName} />}
</PageSection>
);
return InterestsSection;
})
.with("awards", () => {
const AwardsSection = ({ id: _id }: { id: string }) => (
const AwardsSection = (_: SectionProps) => (
<PageSection type="awards" className={sectionClassName}>
{(item) => <AwardsItem {...item} className={itemClassName} />}
</PageSection>
);
return AwardsSection;
})
.with("certifications", () => {
const CertificationsSection = ({ id: _id }: { id: string }) => (
const CertificationsSection = (_: SectionProps) => (
<PageSection type="certifications" className={sectionClassName}>
{(item) => <CertificationsItem {...item} className={itemClassName} />}
</PageSection>
);
return CertificationsSection;
})
.with("publications", () => {
const PublicationsSection = ({ id: _id }: { id: string }) => (
const PublicationsSection = (_: SectionProps) => (
<PageSection type="publications" className={sectionClassName}>
{(item) => <PublicationsItem {...item} className={itemClassName} />}
</PageSection>
);
return PublicationsSection;
})
.with("volunteer", () => {
const VolunteerSection = ({ id: _id }: { id: string }) => (
const VolunteerSection = (_: SectionProps) => (
<PageSection type="volunteer" className={sectionClassName}>
{(item) => <VolunteerItem {...item} className={itemClassName} />}
</PageSection>
);
return VolunteerSection;
})
.with("references", () => {
const ReferencesSection = ({ id: _id }: { id: string }) => (
const ReferencesSection = (_: SectionProps) => (
<PageSection type="references" className={sectionClassName}>
{(item) => <ReferencesItem {...item} className={itemClassName} />}
</PageSection>
);
return ReferencesSection;
})
.otherwise(() => {
// Custom section - render based on its type
const CustomSectionComponent = ({ id }: { id: string }) => {
const CustomSectionComponent = ({ id }: SectionProps) => {
const customSection = useResumeStore((state) => state.resume.data.customSections.find((s) => s.id === id));
if (!customSection) return null;
@@ -11,15 +11,10 @@ type PageSummaryProps = {
export function PageSummary({ className }: PageSummaryProps) {
const section = useResumeStore((state) => state.resume.data.summary);
if (section.hidden || !stripHtml(section.content)) return null;
return (
<section
className={cn(
"page-section page-section-summary",
section.hidden && "hidden",
!stripHtml(section.content) && "hidden",
className,
)}
>
<section className={cn("page-section page-section-summary", className)}>
<h6 className="mb-1.5 text-(--page-primary-color)">{section.title || getSectionTitle("summary")}</h6>
<div className="section-content">
+4 -12
View File
@@ -29,10 +29,6 @@ export function DitgarTemplate({ pageIndex, pageLayout }: TemplateProps) {
const isFirstPage = pageIndex === 0;
const { main, sidebar, fullWidth } = pageLayout;
const SummaryComponent = getSectionComponent("summary", {
sectionClassName: cn(sectionClassName, "px-(--page-margin-x) pt-(--page-margin-y)"),
});
return (
<div className="template-ditgar page-content">
{/* Sidebar Background */}
@@ -55,15 +51,11 @@ export function DitgarTemplate({ pageIndex, pageLayout }: TemplateProps) {
)}
<main data-layout="main" className={cn("main group z-10", !fullWidth ? "col-span-2" : "col-span-3")}>
{isFirstPage && <SummaryComponent id="summary" />}
<div className="space-y-4 px-(--page-margin-x) pt-(--page-margin-y)">
{main
.filter((section) => section !== "summary")
.map((section) => {
const Component = getSectionComponent(section, { sectionClassName });
return <Component key={section} id={section} />;
})}
{main.map((section) => {
const Component = getSectionComponent(section, { sectionClassName });
return <Component key={section} id={section} />;
})}
</div>
</main>
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
import { BetterAuthError } from "@better-auth/core/error";
import { BetterAuthError } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { betterAuth } from "better-auth/minimal";
import { apiKey, type GenericOAuthConfig, genericOAuth, openAPI, twoFactor } from "better-auth/plugins";
+7 -16
View File
@@ -153,6 +153,7 @@ export const printerService = {
// Now measure the total height (margins are now part of the DOM)
let totalHeight = 0;
for (const el of pageElements) {
const pageEl = el as HTMLElement;
// offsetHeight includes padding and border, but not margin
@@ -164,23 +165,13 @@ export const printerService = {
return Math.max(totalHeight, minPageHeight);
}
// For A4/Letter: existing behavior
// The --page-height CSS variable controls the height of each resume page.
// We need to reduce it by the PDF margins so content fits within the printable area.
// Without this, content would overflow and create empty pages.
const rootHeight = getComputedStyle(root).getPropertyValue("--page-height").trim();
const containerHeight = container
? getComputedStyle(container).getPropertyValue("--page-height").trim()
: null;
const currentHeight = containerHeight || rootHeight;
const heightValue = Math.min(Number.parseFloat(currentHeight), minPageHeight);
// For A4/Letter
const heightValue = minPageHeight;
if (!Number.isNaN(heightValue)) {
// Subtract top + bottom margins from page height
const newHeight = `${heightValue - marginY}px`;
if (container) container.style.setProperty("--page-height", newHeight);
root.style.setProperty("--page-height", newHeight);
}
// Subtract top + bottom margins from page height
const newHeight = `${heightValue - marginY}px`;
if (container) container.style.setProperty("--page-height", newHeight);
root.style.setProperty("--page-height", newHeight);
// Add page break CSS to each resume page element (identified by data-page-index attribute)
// This ensures each visual resume page starts a new PDF page
+2 -2
View File
@@ -25,9 +25,9 @@ function RouteComponent() {
queryKey: ["auth", "api-keys"],
queryFn: () => authClient.apiKey.list(),
select: ({ data }) => {
if (!data?.apiKeys) return [];
if (!data) return [];
return data.apiKeys
return data
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
.filter((key) => !!key.expiresAt && key.expiresAt.getTime() > Date.now());
},