mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 04:22:27 +10:00
feat: separate links option at section level to reduce section item height
This commit is contained in:
@ -117,16 +117,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -135,10 +136,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -205,7 +236,7 @@ const Section = <T,>({
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
|
||||
<div className="absolute left-[-4.5px] top-px hidden size-[8px] rounded-full bg-primary group-[.main]:block" />
|
||||
</div>
|
||||
@ -255,7 +286,7 @@ const Experience = () => {
|
||||
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -272,7 +303,7 @@ const Education = () => {
|
||||
<Section<Education> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
<div>{item.studyType}</div>
|
||||
@ -291,7 +322,7 @@ const Awards = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -303,11 +334,11 @@ const Certifications = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.certifications);
|
||||
|
||||
return (
|
||||
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
|
||||
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -347,7 +378,7 @@ const Publications = () => {
|
||||
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
@ -363,7 +394,7 @@ const Volunteer = () => {
|
||||
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -396,7 +427,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -414,7 +445,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -435,7 +466,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
|
||||
<div className="font-bold">{item.date}</div>
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
Experience,
|
||||
Interest,
|
||||
Language,
|
||||
Metadata,
|
||||
Profile,
|
||||
Project,
|
||||
Publication,
|
||||
@ -108,16 +109,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -126,10 +128,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -173,7 +205,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -233,7 +265,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -255,7 +287,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -279,7 +311,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -300,7 +332,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -345,7 +377,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -366,7 +398,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -403,7 +435,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -423,7 +455,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -444,7 +476,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -111,16 +111,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -129,10 +130,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -174,7 +205,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -202,7 +233,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -224,7 +255,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -280,7 +311,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -302,6 +333,7 @@ const Certifications = () => {
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -346,7 +378,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -367,7 +399,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -404,7 +436,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -424,7 +456,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -445,7 +477,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -128,16 +128,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -146,10 +147,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -195,7 +226,7 @@ const Section = <T,>({
|
||||
<div className="relative -ml-4 group-[.sidebar]:ml-0">
|
||||
<div className="pl-4 group-[.sidebar]:pl-0">
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-y-0 -left-px border-l-[4px] border-primary group-[.sidebar]:hidden" />
|
||||
@ -260,7 +291,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -282,7 +313,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -306,7 +337,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -327,7 +358,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -372,7 +403,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -393,7 +424,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -430,7 +461,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -450,7 +481,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -471,7 +502,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -106,16 +106,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -124,10 +125,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary group-[.sidebar]:text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -169,7 +200,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -229,7 +260,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -251,7 +282,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -275,7 +306,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right group-[.sidebar]:text-left">
|
||||
@ -296,7 +327,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right group-[.sidebar]:text-left">
|
||||
@ -341,7 +372,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -362,7 +393,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -399,7 +430,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -419,7 +450,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -440,7 +471,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -114,16 +114,17 @@ const Rating = ({ level }: RatingProps) => {
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -132,10 +133,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary group-[.sidebar]:text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -179,7 +210,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -207,7 +238,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -229,7 +260,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -285,7 +316,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -306,7 +337,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -351,7 +382,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -372,7 +403,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -409,7 +440,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -429,7 +460,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -450,7 +481,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -134,16 +134,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -152,10 +153,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -209,7 +240,7 @@ const Section = <T,>({
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@ -225,7 +256,7 @@ const Experience = () => {
|
||||
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -242,7 +273,7 @@ const Education = () => {
|
||||
<Section<Education> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
<div>{item.studyType}</div>
|
||||
@ -261,7 +292,7 @@ const Awards = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -277,7 +308,7 @@ const Certifications = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -317,7 +348,7 @@ const Publications = () => {
|
||||
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
@ -333,7 +364,7 @@ const Volunteer = () => {
|
||||
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -366,7 +397,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
@ -383,7 +414,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -404,7 +435,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
|
||||
@ -131,16 +131,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -149,10 +150,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -206,7 +237,7 @@ const Section = <T,>({
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@ -222,7 +253,7 @@ const Experience = () => {
|
||||
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -239,7 +270,7 @@ const Education = () => {
|
||||
<Section<Education> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
<div>{item.studyType}</div>
|
||||
@ -258,7 +289,7 @@ const Awards = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -274,7 +305,7 @@ const Certifications = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -314,7 +345,7 @@ const Publications = () => {
|
||||
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
@ -330,7 +361,7 @@ const Volunteer = () => {
|
||||
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
@ -363,7 +394,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
@ -380,7 +411,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -401,7 +432,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
|
||||
@ -110,16 +110,17 @@ const Summary = () => {
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -128,10 +129,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -184,7 +215,7 @@ const Section = <T,>({
|
||||
<div className="col-span-3 space-y-1">
|
||||
{children?.(item as T)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
@ -219,7 +250,7 @@ const Section = <T,>({
|
||||
<div key={item.id}>
|
||||
{children?.(item as T)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
@ -277,7 +308,7 @@ const Experience = () => {
|
||||
<Section<Experience> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
</div>
|
||||
@ -293,7 +324,7 @@ const Education = () => {
|
||||
<Section<Education> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.studyType}</div>
|
||||
<div>{item.score}</div>
|
||||
@ -311,7 +342,7 @@ const Awards = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@ -326,7 +357,7 @@ const Certifications = () => {
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@ -365,7 +396,7 @@ const Publications = () => {
|
||||
<Section<Publication> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -380,7 +411,7 @@ const Volunteer = () => {
|
||||
<Section<Volunteer> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
</div>
|
||||
@ -417,7 +448,7 @@ const Projects = () => {
|
||||
>
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -432,7 +463,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -453,7 +484,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
>
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
<div>{item.location}</div>
|
||||
</div>
|
||||
|
||||
@ -137,16 +137,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -155,10 +156,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -200,7 +231,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -228,7 +259,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -250,7 +281,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -274,7 +305,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -295,7 +326,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -340,7 +371,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -361,7 +392,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -398,7 +429,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -418,7 +449,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -439,7 +470,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -137,16 +137,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.summary]:text-background" />}
|
||||
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary group-[.summary]:text-background" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -155,10 +156,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary group-[.summary]:text-background" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary group-[.summary]:text-background" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -200,7 +231,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -260,7 +291,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -282,7 +313,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -306,7 +337,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right group-[.sidebar]:text-left">
|
||||
@ -327,7 +358,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right group-[.sidebar]:text-left">
|
||||
@ -372,7 +403,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -393,7 +424,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -430,7 +461,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -450,7 +481,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -471,7 +502,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -109,16 +109,17 @@ const Rating = ({ level }: RatingProps) => (
|
||||
type LinkProps = {
|
||||
url: URL;
|
||||
icon?: React.ReactNode;
|
||||
iconOnRight?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
|
||||
if (!isUrl(url.href)) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
|
||||
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
<a
|
||||
href={url.href}
|
||||
target="_blank"
|
||||
@ -127,10 +128,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
|
||||
>
|
||||
{label || url.label || url.href}
|
||||
</a>
|
||||
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type LinkedEntityProps = {
|
||||
name: string;
|
||||
url: URL;
|
||||
separateLinks: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
|
||||
if (!separateLinks && isUrl(url.href)) {
|
||||
return (
|
||||
<Link
|
||||
url={url}
|
||||
label={name}
|
||||
icon={
|
||||
<i className="ph ph-bold ph-globe text-primary" />
|
||||
}
|
||||
iconOnRight={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type SectionProps<T> = {
|
||||
section: SectionWithItem<T> | CustomSectionGroup;
|
||||
children?: (item: T) => React.ReactNode;
|
||||
@ -172,7 +203,7 @@ const Section = <T,>({
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
{url !== undefined && section.separateLinks && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
@ -232,7 +263,7 @@ const Experience = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -254,7 +285,7 @@ const Education = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
@ -278,7 +309,7 @@ const Awards = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -299,7 +330,7 @@ const Certifications = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-right">
|
||||
@ -344,7 +375,7 @@ const Publications = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
|
||||
@ -365,7 +396,7 @@ const Volunteer = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.position}</div>
|
||||
</div>
|
||||
|
||||
@ -402,7 +433,7 @@ const Projects = () => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
@ -422,7 +453,7 @@ const References = () => {
|
||||
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
@ -443,7 +474,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
{(item) => (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="text-left">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -10,10 +10,11 @@ import {
|
||||
Plus,
|
||||
TrashSimple,
|
||||
} from "@phosphor-icons/react";
|
||||
import { defaultSections, SectionKey, SectionWithItem } from "@reactive-resume/schema";
|
||||
import { defaultSections, SectionKey, sectionsSchema, SectionWithItem } from "@reactive-resume/schema";
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
@ -31,6 +32,9 @@ import { useMemo } from "react";
|
||||
|
||||
import { useDialog } from "@/client/stores/dialog";
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
import { DropdownMenuItemIndicator } from "@radix-ui/react-dropdown-menu";
|
||||
import { CheckboxIndicator } from "@radix-ui/react-checkbox";
|
||||
import { itemsEqual } from "@dnd-kit/sortable/dist/utilities";
|
||||
|
||||
type Props = { id: SectionKey };
|
||||
|
||||
@ -47,6 +51,7 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
const isCustomSection = useMemo(() => id.startsWith("custom"), [id]);
|
||||
|
||||
const onCreate = () => open("create", { id });
|
||||
const toggleSeperateLinks = (checked: boolean) => setValue(`sections.${id}.separateLinks`, checked);
|
||||
const toggleVisibility = () => setValue(`sections.${id}.visible`, !section.visible);
|
||||
const onResetName = () => setValue(`sections.${id}.name`, originalName);
|
||||
const onChangeColumns = (value: string) => setValue(`sections.${id}.columns`, Number(value));
|
||||
@ -67,7 +72,12 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
<Plus />
|
||||
<span className="ml-2">{t`Add a new item`}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuCheckboxItem
|
||||
onCheckedChange={toggleSeperateLinks}
|
||||
checked={section.separateLinks}
|
||||
>
|
||||
<span className="ml-0">{t`Separate Links`}</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -28,6 +28,7 @@ export const sampleResume: ResumeData = {
|
||||
summary: {
|
||||
name: "Summary",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "summary",
|
||||
content:
|
||||
@ -36,6 +37,7 @@ export const sampleResume: ResumeData = {
|
||||
awards: {
|
||||
name: "Awards",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "awards",
|
||||
items: [],
|
||||
@ -43,6 +45,7 @@ export const sampleResume: ResumeData = {
|
||||
certifications: {
|
||||
name: "Certifications",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "certifications",
|
||||
items: [
|
||||
@ -75,6 +78,7 @@ export const sampleResume: ResumeData = {
|
||||
education: {
|
||||
name: "Education",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "education",
|
||||
items: [
|
||||
@ -97,6 +101,7 @@ export const sampleResume: ResumeData = {
|
||||
experience: {
|
||||
name: "Experience",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "experience",
|
||||
items: [
|
||||
@ -133,6 +138,7 @@ export const sampleResume: ResumeData = {
|
||||
volunteer: {
|
||||
name: "Volunteering",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "volunteer",
|
||||
items: [],
|
||||
@ -140,6 +146,7 @@ export const sampleResume: ResumeData = {
|
||||
interests: {
|
||||
name: "Interests",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "interests",
|
||||
items: [],
|
||||
@ -147,6 +154,7 @@ export const sampleResume: ResumeData = {
|
||||
languages: {
|
||||
name: "Languages",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "languages",
|
||||
items: [],
|
||||
@ -154,6 +162,7 @@ export const sampleResume: ResumeData = {
|
||||
profiles: {
|
||||
name: "Profiles",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "profiles",
|
||||
items: [
|
||||
@ -184,6 +193,7 @@ export const sampleResume: ResumeData = {
|
||||
projects: {
|
||||
name: "Projects",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "projects",
|
||||
items: [
|
||||
@ -220,6 +230,7 @@ export const sampleResume: ResumeData = {
|
||||
publications: {
|
||||
name: "Publications",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "publications",
|
||||
items: [],
|
||||
@ -227,6 +238,7 @@ export const sampleResume: ResumeData = {
|
||||
references: {
|
||||
name: "References",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: false,
|
||||
id: "references",
|
||||
items: [
|
||||
@ -246,6 +258,7 @@ export const sampleResume: ResumeData = {
|
||||
skills: {
|
||||
name: "Skills",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
id: "skills",
|
||||
items: [
|
||||
|
||||
@ -20,6 +20,7 @@ import { volunteerSchema } from "./volunteer";
|
||||
export const sectionSchema = z.object({
|
||||
name: z.string(),
|
||||
columns: z.number().min(1).max(5).default(1),
|
||||
separateLinks: z.boolean().default(true),
|
||||
visible: z.boolean().default(true),
|
||||
});
|
||||
|
||||
@ -98,6 +99,7 @@ export type CustomSectionGroup = z.infer<typeof customSchema>;
|
||||
export const defaultSection: Section = {
|
||||
name: "",
|
||||
columns: 1,
|
||||
separateLinks: true,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user