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