Refactor imports and improve layout in various components; update styles for consistency and readability.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { ActionIcon, useMantineColorScheme } from '@mantine/core';
|
|
||||||
import { TbSun, TbMoon } from 'react-icons/tb';
|
|
||||||
import cx from 'clsx';
|
import cx from 'clsx';
|
||||||
|
import { TbMoon, TbSun } from 'react-icons/tb';
|
||||||
|
import { ActionIcon, useMantineColorScheme } from '@mantine/core';
|
||||||
import classes from './ColorSchemeToggle.module.css';
|
import classes from './ColorSchemeToggle.module.css';
|
||||||
|
|
||||||
export function ColorSchemeToggle() {
|
export function ColorSchemeToggle() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Container, Stack, Title, Text, Anchor } from '@mantine/core';
|
import { Anchor, Container, Stack, Text, Title } from '@mantine/core';
|
||||||
import classes from './Contact.module.css';
|
import classes from './Contact.module.css';
|
||||||
|
|
||||||
export function Contact() {
|
export function Contact() {
|
||||||
@@ -7,15 +7,19 @@ export function Contact() {
|
|||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Title className={classes.title}>Get in Touch</Title>
|
<Title className={classes.title}>Get in Touch</Title>
|
||||||
<Text className={classes.description}>
|
<Text className={classes.description}>
|
||||||
If you'd like to collaborate, have any questions, or just want to say hello, feel free to reach out!
|
If you'd like to collaborate, have any questions, or just want to say hello, feel free to
|
||||||
|
reach out!
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text>
|
||||||
Email: <Anchor href="mailto:cdmacfadyen@proton.me">cdmacfadyen@proton.me</Anchor>
|
Email: <Anchor href="mailto:cdmacfadyen@proton.me">cdmacfadyen@proton.me</Anchor>
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text>
|
||||||
LinkedIn: <Anchor href="https://www.linkedin.com/in/craig-macfadyen-9a2041197" target="_blank">linkedin.com/in/craigmacfadyen</Anchor>
|
LinkedIn:{' '}
|
||||||
|
<Anchor href="https://www.linkedin.com/in/craig-macfadyen-9a2041197" target="_blank">
|
||||||
|
linkedin.com/in/craigmacfadyen
|
||||||
|
</Anchor>
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TbAt, TbMapPin, TbPhone, TbSun } from 'react-icons/tb';
|
import { TbAt, TbSun } from 'react-icons/tb';
|
||||||
import { Box, Stack, Text } from '@mantine/core';
|
import { Box, Stack, Text } from '@mantine/core';
|
||||||
import classes from './ContactIcons.module.css';
|
import classes from './ContactIcons.module.css';
|
||||||
|
|
||||||
@@ -25,11 +25,9 @@ function ContactIcon({ icon: Icon, title, description, ...others }: ContactIconP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MOCKDATA = [
|
const MOCKDATA = [{ title: 'Email', description: 'hello@mantine.dev', icon: TbAt }];
|
||||||
{ title: 'Email', description: 'hello@mantine.dev', icon: TbAt },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function ContactIconsList() {
|
export function ContactIconsList() {
|
||||||
const items = MOCKDATA.map((item, index) => <ContactIcon key={index} {...item} />);
|
const items = MOCKDATA.map((item, index) => <ContactIcon key={index} {...item} />);
|
||||||
return <Stack>{items}</Stack>;
|
return <Stack>{items}</Stack>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-family:
|
font-family:
|
||||||
Greycliff CF,
|
"Greycliff CF",
|
||||||
var(--mantine-font-family);
|
var(--mantine-font-family);
|
||||||
color: var(--mantine-color-white);
|
color: var(--mantine-color-white);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const social = [TbBrandInstagram, TbBrandTwitter, TbBrandYoutube];
|
|||||||
export function ContactUs() {
|
export function ContactUs() {
|
||||||
const icons = social.map((Icon, index) => (
|
const icons = social.map((Icon, index) => (
|
||||||
<ActionIcon key={index} size={28} className={classes.social} variant="transparent">
|
<ActionIcon key={index} size={28} className={classes.social} variant="transparent">
|
||||||
<Icon size={22} stroke={1.5} />
|
<Icon size={22} stroke="1.5" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -63,4 +63,4 @@ export function ContactUs() {
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button, Text, Title, Container, Stack, Grid, Image, Center } from '@mantine/core';
|
|
||||||
import { Carousel } from '@mantine/carousel';
|
import { Carousel } from '@mantine/carousel';
|
||||||
|
import { Container, Grid, Image, Stack, Text, Title } from '@mantine/core';
|
||||||
import classes from './ExpertiseSection.module.css';
|
import classes from './ExpertiseSection.module.css';
|
||||||
|
|
||||||
interface ExpertiseSectionProps {
|
interface ExpertiseSectionProps {
|
||||||
@@ -15,19 +15,23 @@ const ExpertiseSection: React.FC<ExpertiseSectionProps> = ({
|
|||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
images,
|
images,
|
||||||
viewMoreLink,
|
viewMoreLink: _viewMoreLink,
|
||||||
imageOnLeft = false,
|
imageOnLeft = false,
|
||||||
}) => {
|
}) => {
|
||||||
const slides = images.map((image) => (
|
const slides = images.map((image) => (
|
||||||
<Carousel.Slide key={image.url}>
|
<Carousel.Slide key={image.url}>
|
||||||
<Image src={image.url} alt={image.alt} style={{ width: '100%', height: 'auto', objectFit: 'cover' }} />
|
<Image
|
||||||
|
src={image.url}
|
||||||
|
alt={image.alt}
|
||||||
|
style={{ width: '100%', height: 'auto', objectFit: 'cover' }}
|
||||||
|
/>
|
||||||
</Carousel.Slide>
|
</Carousel.Slide>
|
||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Grid className={classes.section}>
|
<Grid className={classes.section}>
|
||||||
<Grid.Col order={imageOnLeft ? 2:1} span={{xs: 6, md: 8}} >
|
<Grid.Col order={imageOnLeft ? 2 : 1} span={{ xs: 6, md: 8 }}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title>{title}</Title>
|
<Title>{title}</Title>
|
||||||
<Text size="lg">{content}</Text>
|
<Text size="lg">{content}</Text>
|
||||||
@@ -36,8 +40,16 @@ const ExpertiseSection: React.FC<ExpertiseSectionProps> = ({
|
|||||||
</Button> */}
|
</Button> */}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col order={imageOnLeft ? 1:2} span={{xs: 6, md: 4}}>
|
<Grid.Col order={imageOnLeft ? 1 : 2} span={{ xs: 6, md: 4 }}>
|
||||||
<Carousel slideSize={300} align="start" slideGap="md" controlsOffset="xl" controlSize={28} loop withIndicators>
|
<Carousel
|
||||||
|
slideSize={300}
|
||||||
|
align="start"
|
||||||
|
slideGap="md"
|
||||||
|
controlsOffset="xl"
|
||||||
|
controlSize={28}
|
||||||
|
loop
|
||||||
|
withIndicators
|
||||||
|
>
|
||||||
{slides}
|
{slides}
|
||||||
</Carousel>
|
</Carousel>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
@@ -46,4 +58,4 @@ const ExpertiseSection: React.FC<ExpertiseSectionProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ExpertiseSection;
|
export default ExpertiseSection;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { useState } from 'react';
|
|||||||
import { Burger, Container, Group } from '@mantine/core';
|
import { Burger, Container, Group } from '@mantine/core';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import { MantineLogo } from '@mantinex/mantine-logo';
|
import { MantineLogo } from '@mantinex/mantine-logo';
|
||||||
import classes from './HeaderSimple.module.css';
|
|
||||||
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle';
|
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle';
|
||||||
|
import classes from './HeaderSimple.module.css';
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ link: '/about', label: 'Features' },
|
{ link: '/about', label: 'Features' },
|
||||||
@@ -44,4 +44,4 @@ export function HeaderSimple() {
|
|||||||
</Container>
|
</Container>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
padding-top: 200px;
|
padding-top: 200px;
|
||||||
padding-bottom: 120px;
|
padding-bottom: 120px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
|
||||||
@media (max-width: $mantine-breakpoint-sm) {
|
@media (max-width: $mantine-breakpoint-sm) {
|
||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
padding-top: 80px;
|
padding-top: 80px;
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-family:
|
font-family:
|
||||||
Greycliff CF,
|
"Greycliff CF",
|
||||||
var(--mantine-font-family);
|
var(--mantine-font-family);
|
||||||
font-size: 62px;
|
font-size: 62px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import classes from './HeroTitle.module.css';
|
|||||||
export function HeroTitle() {
|
export function HeroTitle() {
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<Container fluid={true} size={700} className={classes.inner}>
|
<Container fluid size={700} className={classes.inner}>
|
||||||
<h1 className={classes.title}>
|
<h1 className={classes.title}>
|
||||||
{' '}
|
{' '}
|
||||||
<Text component="span" variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} inherit>
|
<Text component="span" variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} inherit>
|
||||||
@@ -14,8 +14,9 @@ export function HeroTitle() {
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<Text className={classes.description} c="dimmed">
|
<Text className={classes.description} c="dimmed">
|
||||||
Data Scientist with experience in consulting and research. Expertise in Computer Vision and Large Language Models. Take a look at what
|
Data Scientist with experience in consulting and research. Expertise in Computer Vision
|
||||||
I can do for your business and get in touch if you have any questions or you'd like to work together!
|
and Large Language Models. Take a look at what I can do for your business and get in touch
|
||||||
|
if you have any questions or you'd like to work together!
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Group className={classes.controls}>
|
<Group className={classes.controls}>
|
||||||
@@ -42,4 +43,4 @@ export function HeroTitle() {
|
|||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,58 @@
|
|||||||
import { Carousel } from '@mantine/carousel';
|
|
||||||
import { Card, Text, Avatar, Group, Stack, Title } from '@mantine/core';
|
|
||||||
import Autoplay from 'embla-carousel-autoplay';
|
import Autoplay from 'embla-carousel-autoplay';
|
||||||
|
import { Carousel } from '@mantine/carousel';
|
||||||
|
import { Avatar, Card, Group, Stack, Text, Title } from '@mantine/core';
|
||||||
import styles from './Testimonial.module.css';
|
import styles from './Testimonial.module.css';
|
||||||
|
|
||||||
const testimonials = [
|
const testimonials = [
|
||||||
{
|
{
|
||||||
text: "What set Craig apart was his ability to understand our business challenges and deliver a solution that worked for us. He didn't just build a model, he delivered a system our team could actually use.",
|
text: "What set Craig apart was his ability to understand our business challenges and deliver a solution that worked for us. He didn't just build a model, he delivered a system our team could actually use.",
|
||||||
clientName: "Steven Adair",
|
clientName: 'Steven Adair',
|
||||||
role: "Director",
|
role: 'Director',
|
||||||
business: "Managing Utilities Limited",
|
business: 'Managing Utilities Limited',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function TestimonialsCarousel() {
|
function TestimonialsCarousel() {
|
||||||
const autoplay = Autoplay();
|
const autoplay = Autoplay();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title align="center" mb="xl">
|
<Title ta="center" mb="xl">
|
||||||
Testimonials
|
Testimonials
|
||||||
</Title>
|
</Title>
|
||||||
<Carousel
|
<Carousel
|
||||||
slideSize={'100%'}
|
slideSize="100%"
|
||||||
slideGap="md"
|
slideGap="md"
|
||||||
loop
|
loop
|
||||||
align="start"
|
align="start"
|
||||||
plugins={[autoplay]}
|
plugins={[autoplay]}
|
||||||
onMouseEnter={autoplay.stop}
|
onMouseEnter={autoplay.stop}
|
||||||
onMouseLeave={autoplay.reset}
|
onMouseLeave={autoplay.reset}
|
||||||
withControls
|
withControls
|
||||||
>
|
>
|
||||||
{testimonials.map((testimonial, index) => (
|
{testimonials.map((testimonial, index) => (
|
||||||
<Carousel.Slide key={index}>
|
<Carousel.Slide key={index}>
|
||||||
<Card shadow="sm" padding="lg" radius="md" withBorder className={styles.card}>
|
<Card shadow="sm" padding="lg" radius="md" withBorder className={styles.card}>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Text size="lg">{testimonial.text}</Text>
|
<Text size="lg">{testimonial.text}</Text>
|
||||||
<Group gap="sm">
|
<Group gap="sm">
|
||||||
<Avatar radius="xl">
|
<Avatar radius="xl">{testimonial.clientName.charAt(0)}</Avatar>
|
||||||
{testimonial.clientName.charAt(0)}
|
<div>
|
||||||
</Avatar>
|
<Text size="lg" fw={700}>
|
||||||
<div>
|
{testimonial.clientName}
|
||||||
<Text size="lg" fw={700}>{testimonial.clientName}</Text>
|
</Text>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{testimonial.role}, {testimonial.business}
|
{testimonial.role}, {testimonial.business}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
</Carousel.Slide>
|
</Carousel.Slide>
|
||||||
))}
|
))}
|
||||||
</Carousel>
|
</Carousel>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TestimonialsCarousel;
|
export default TestimonialsCarousel;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
import '@mantine/carousel/styles.css';
|
import '@mantine/carousel/styles.css';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { HeroTitle } from '@/components/HeroTitle/HeroTitle';
|
|
||||||
import { Welcome } from '../components/Welcome/Welcome';
|
|
||||||
import { HeaderSimple } from '@/components/HeaderSimple/HeaderSimple';
|
|
||||||
import ExpertiseSection from '@/components/ExpertiseSection/ExpertiseSection';
|
|
||||||
import outputGif from '@/assets/output.gif';
|
|
||||||
import openWebUIGif from '@/assets/open-webui.gif';
|
|
||||||
import Testimonial from '@/components/Testimonial/Testimonial';
|
|
||||||
import { Container } from '@mantine/core';
|
import { Container } from '@mantine/core';
|
||||||
import { ContactUs } from '@/components/ContactUs/ContactUs';
|
import openWebUIGif from '@/assets/open-webui.gif';
|
||||||
|
import outputGif from '@/assets/output.gif';
|
||||||
import { Contact } from '@/components/ContactUs/Contact';
|
import { Contact } from '@/components/ContactUs/Contact';
|
||||||
|
import ExpertiseSection from '@/components/ExpertiseSection/ExpertiseSection';
|
||||||
|
import { HeaderSimple } from '@/components/HeaderSimple/HeaderSimple';
|
||||||
|
import { HeroTitle } from '@/components/HeroTitle/HeroTitle';
|
||||||
|
import Testimonial from '@/components/Testimonial/Testimonial';
|
||||||
|
|
||||||
export function HomePage() {
|
export function HomePage() {
|
||||||
return (
|
return (
|
||||||
@@ -15,27 +13,23 @@ export function HomePage() {
|
|||||||
<HeaderSimple />
|
<HeaderSimple />
|
||||||
<HeroTitle />
|
<HeroTitle />
|
||||||
<ExpertiseSection
|
<ExpertiseSection
|
||||||
title="Computer Vision"
|
title="Computer Vision"
|
||||||
content="Expertise in developing and deploying computer vision models for various applications, including object detection, image classification, and more. Ask me about state-of-the-art real-time models for your business."
|
content="Expertise in developing and deploying computer vision models for various applications, including object detection, image classification, and more. Ask me about state-of-the-art real-time models for your business."
|
||||||
images={[
|
images={[{ url: outputGif, alt: 'Video Segmentation with SAM2.' }]}
|
||||||
{ url: outputGif, alt: "Video Segmentation with SAM2." },
|
viewMoreLink="/computer-vision"
|
||||||
]}
|
imageOnLeft
|
||||||
viewMoreLink="/computer-vision"
|
/>
|
||||||
imageOnLeft={true}
|
|
||||||
/>
|
|
||||||
<ExpertiseSection
|
<ExpertiseSection
|
||||||
title="Natural Language Processing"
|
title="Natural Language Processing"
|
||||||
content="Building NLP models for tasks such as sentiment analysis, text classification, and language generation. If you need a chatbot for your business and care about privacy, or a simple text classification algorithm to understand your data, I can help."
|
content="Building NLP models for tasks such as sentiment analysis, text classification, and language generation. If you need a chatbot for your business and care about privacy, or a simple text classification algorithm to understand your data, I can help."
|
||||||
images={[
|
images={[{ url: openWebUIGif, alt: 'Locally hosted LLM using oLLama and OpenWebUI.' }]}
|
||||||
{ url: openWebUIGif, alt: "Locally hosted LLM using oLLama and OpenWebUI." },
|
viewMoreLink="/nlp"
|
||||||
]}
|
imageOnLeft={false}
|
||||||
viewMoreLink="/nlp"
|
/>
|
||||||
imageOnLeft={false}
|
<Container mt={20}>
|
||||||
/>
|
<Testimonial />
|
||||||
<Container mt={20}>
|
</Container>
|
||||||
<Testimonial />
|
<Contact />
|
||||||
</Container>
|
|
||||||
<Contact />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user