Skip to content
This repository was archived by the owner on Jul 3, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions app/pages/about/AboutText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type ReactElement } from 'react'
import { H1, P } from '~/components/ui/typography'

export function AboutText({ text }: { text: string }): ReactElement {
return (
<div className="max-w-7xl mx-auto text-left">
<H1 className="text-4xl font-bold mb-6 text-center">About us</H1>
{text.split('\n\n').map((paragraph, index) => (
<P key={index} className="text-lg text-muted-foreground mb-6">
{paragraph}
</P>
))}
</div>
)
}
10 changes: 10 additions & 0 deletions app/pages/about/GithubLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { ReactElement } from 'react'

export function GithubLink({ url }: { url: string }): ReactElement {
return (
<a href={url} target="_blank" className="flex items-center gap-2 hover:opacity-80 transition" rel="noreferrer">
<img src="https://raw.github.com/zhuravel17/rsschool-cv/main/github-icon.png" alt="GitHub" className="w-5 h-5" />
<span className="text-base text-[#24292f] hover:underline font-semibold">{url.split('/').pop()}</span>
</a>
)
}
15 changes: 15 additions & 0 deletions app/pages/about/SchoolLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type ReactElement } from 'react'

export function SchoolLogo(): ReactElement {
return (
<div className="flex justify-center mt-8">
<a href="https://rs.school/" target="_blank" title="RSSchool">
<img
src="https://raw.githubusercontent.com/zhuravel17/rsschool-cv/main/rs_school_logo-light.ef179aecce62c8d7532aee6bdc69ef42.svg"
alt="RSSchool logo"
className="h-12 transition-all hover:brightness-90"
/>
</a>
</div>
)
}
58 changes: 58 additions & 0 deletions app/pages/about/TeamMember.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { type ReactElement } from 'react'
import { H2, P } from '~/components/ui/typography'
import { GithubLink } from './GithubLink'

interface TeamMemberProperties {
name: string
location?: string
education?: string
courses: string
about: string
github: string
imageUrl: string
reverse?: boolean
}

function InfoLine({ label, text }: { label: string; text?: string }): ReactElement | undefined {
if (!text) return undefined
return (
<P className="!mt-1">
<span className="font-bold">{label}:</span> {text}
</P>
)
}

export function TeamMember({
name,
location,
education,
courses,
about,
github,
imageUrl,
reverse = false
}: TeamMemberProperties): ReactElement {
return (
<div
className={`max-w-7xl mx-auto flex flex-col ${
reverse ? 'md:flex-row-reverse' : 'md:flex-row'
} gap-8 items-center`}
>
<img
src={imageUrl}
width={300}
height={500}
className="rounded-2xl md:w-[300px] md:h-auto object-cover"
alt={name}
/>
<div className="flex flex-col text-left self-start">
<H2>{name}</H2>
<InfoLine label="Location" text={location} />
<InfoLine label="Education" text={education} />
<InfoLine label="Courses" text={courses} />
<InfoLine label="About me" text={about} />
<GithubLink url={github} />
</div>
</div>
)
}
38 changes: 38 additions & 0 deletions app/pages/about/aboutData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export const teamMembers = [
{
name: 'MIKHAIL',
courses: 'RSSchool JavaScript/Front-end, NodeJS',
about:
"I'm a frontend developer with a background in full-stack development, currently sharpening my JavaScript skills through RS School. I'm passionate about frontend technologies, enjoy exploring frameworks, and enjoy learning through community-driven collaboration.",
github: 'https://github.com/bitbybit',
imageUrl: 'https://live.staticflickr.com/65535/54588004201_88d9183d76_c.jpg',
reverse: false
},
{
name: 'MERU',
location: 'Aktau, Kazakhstan',
education: 'Al-Farabi Kazakh National University, accounting and auditing',
courses: 'RSSchool JavaScript/Front-end Pre-school, JavaScript/Front-end, NodeJS',
about:
'Wherever I worked before, I always felt drawn to programming. To pursue this goal, I’m currently finishing a course at RS School. Through tons of practice and great learning projects, I’m gaining the experience I need to become a frontend developer. I use Javascript, Typescript, React, and Node.js in my projects.',
github: 'https://github.com/merucoding',
imageUrl: 'https://raw.githubusercontent.com/merucoding/school-project-pictures/main/IMG_1996_1.jpeg',
reverse: true
},
{
name: 'KATE',
location: 'Tbilisi, Georgia',
education: 'Samara University. Applied mathematics and computer science',
courses: 'RSSchool JavaScript/Front-end',
about:
"I'm deeply interested in frontend development and love the process of turning ideas into interactive, user-friendly interfaces. Through RSSchool, I’ve been actively improving my skills in JavaScript, TypeScript, and React - and I genuinely enjoy exploring how all the parts of a web app come together.",
github: 'https://github.com/zhuravel17',
imageUrl: 'https://raw.github.com/zhuravel17/rsschool-cv/main/kate.jpg',
reverse: false
}
]
export const aboutUsText = `Our team put a lot of effort into building this final project, working together in a coordinated and thoughtful way. Each team member made a significant contribution, taking ownership of key parts of the application and actively participating in the collaborative development process.

Mikhail laid the technical foundation of the project from the start: he set up the repository, organized the task board, and ensured a solid structure for our teamwork. He developed the registration page and user profile page, and also worked on the cart page together with Meru. In addition to his development work, Mikhail shared his experience through code reviews — his feedback and suggestions helped improve code quality and deepen our understanding of the implementation. Meru focused on key user-facing pages — she implemented the login page and the product catalog. Together with Mikhail, she also contributed to the development of the cart page, ensuring both its functionality and consistency with the rest of the UI. Kate worked on the overall interface and layout. She created the header and footer, the main page, the detailed product page, and the “About Us” page. Her contribution brought coherence, accessibility, and a polished visual style to the application.

Each of us not only worked on specific components but also actively participated in discussions, testing, and refinement. As a result, we were able to deliver a cohesive, functional, and well-crafted web product.`
18 changes: 16 additions & 2 deletions app/pages/about/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { type ReactElement } from 'react'
import { useTitle } from '~/hooks/useTitle'
import { teamMembers, aboutUsText } from './aboutData'
import { AboutText } from './AboutText'
import { SchoolLogo } from './SchoolLogo'
import { TeamMember } from './TeamMember'

export default function Routes(): ReactElement {
export default function About(): ReactElement {
useTitle('About')

return <>About</>
return (
<section className="w-full px-6 md:px-8 py-16 bg-background text-foreground ">
<AboutText text={aboutUsText} />
<div className="flex flex-col max-w-7xl gap-8 mx-auto">
{teamMembers.map((member) => (
<TeamMember key={member.name} {...member} />
))}
</div>
<SchoolLogo />
</section>
)
}
50 changes: 50 additions & 0 deletions app/pages/product/ProductDetail/CardButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { type ReactElement, useState, type MouseEvent } from 'react'
import { type ProductProjection } from '@commercetools/platform-sdk'
import { useAppDispatch, useAppSelector } from '~/store/hooks'
import { addProduct, removeProduct, selectIsInCart } from '~/store/cart'
import { Button } from '~/components/ui/Button'
import { toast } from 'sonner'
import { P } from '~/components/ui/typography'

type CartToggleButtonProperties = { product: ProductProjection }

export function CartToggleButton({ product }: CartToggleButtonProperties): ReactElement {
const dispatch = useAppDispatch()
const isInCart = useAppSelector(selectIsInCart(product))
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | undefined>()

const handleClick = async (event: MouseEvent<HTMLButtonElement>): Promise<void> => {
event.stopPropagation()
setIsLoading(true)
setError(undefined)
try {
if (isInCart) {
await dispatch(removeProduct({ productId: product.id, quantity: 1 })).unwrap()
toast('❌ Removed from Cart')
} else {
await dispatch(addProduct({ productId: product.id, quantity: 1 })).unwrap()
toast('🛒 Added to Cart')
}
} catch (error) {
setError('An error occurred. Please try again.')
toast(error instanceof Error ? error.message : '❌ Failed to update the cart')
} finally {
setIsLoading(false)
}
}

return (
<>
<Button
variant={isInCart ? 'secondary' : 'default'}
disabled={isLoading}
onClick={(event) => void handleClick(event)}
className="w-[220px] h-10 text-base"
>
{isLoading ? 'Loading...' : isInCart ? 'Remove from Cart' : 'Add to Cart'}
</Button>
{error && <P className="text-red-500 mt-2 text-sm">{error}</P>}
</>
)
}
2 changes: 1 addition & 1 deletion app/pages/product/ProductDetail/ProductDetailBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function ProductDetailBody({
<Card className="w-full flex-grow rounded-xl border border-muted bg-card shadow-sm overflow-hidden">
<CardContent className="flex flex-wrap items-start p-0">
<ProductImages images={images} />
<ProductInfo name={name} description={description} price={price} discount={discount} />
<ProductInfo name={name} description={description} price={price} discount={discount} product={product} />
</CardContent>
</Card>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/pages/product/ProductDetail/ProductImages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function ProductImages({ images = [] }: { images?: Image[] }): ReactEleme
<ImageSwiper
images={images}
onClick={setStartIndex}
swiperClassName="w-[300px] max-w-full sm:w-[500px] rounded-lg"
swiperClassName="w-[90vw] max-w-[500px] rounded-lg"
showPagination
/>
</div>
Expand Down
8 changes: 6 additions & 2 deletions app/pages/product/ProductDetail/ProductInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { type LocalizedString } from '@commercetools/platform-sdk'
import { type LocalizedString, type ProductProjection } from '@commercetools/platform-sdk'
import { type ReactElement } from 'react'
import { H1, H3, P } from '~/components/ui/typography'
import { ProductPrice } from '~/components/product/ProductPrice'
import { CartToggleButton } from './CardButton'

const LANG = 'en-US'

export function ProductInfo({
name,
description,
price,
discount
discount,
product
}: {
name: LocalizedString
description: LocalizedString
price: number
discount?: number
product: ProductProjection
}): ReactElement {
return (
<div className="flex-1 flex flex-col justify-start gap-4 text-left mx-6">
Expand All @@ -23,6 +26,7 @@ export function ProductInfo({
<H3>
<ProductPrice startPrice={price} discountPrice={discount} />
</H3>
<CartToggleButton product={product} />
</div>
)
}