-
Notifications
You must be signed in to change notification settings - Fork 2
feat: 홈 API 연동 및 인증/캐러셀 개선 #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
64c1610
89b511a
40933da
3696852
0079a59
a13bdfa
8225e6b
902c964
f10dc5b
310b0d5
6d1d78b
9a04747
34f0061
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { api } from '@/lib/api-client'; | ||
| import { Advertisement } from '@/types/domain/advertisement'; | ||
| import { CarouselWithDots } from './carousel-with-dots'; | ||
|
|
||
| export default async function BannerCarouselContainer() { | ||
| const advertisements = await api.get<Advertisement[]>('/advertisements/home'); | ||
|
|
||
| return <CarouselWithDots advertisements={advertisements} />; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,66 @@ | ||||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||
| import Image from 'next/image'; | ||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||
| Carousel, | ||||||||||||||||||||||||||||||||||
| CarouselContent, | ||||||||||||||||||||||||||||||||||
| CarouselItem, | ||||||||||||||||||||||||||||||||||
| type CarouselApi, | ||||||||||||||||||||||||||||||||||
| } from '@/components/ui/carousel'; | ||||||||||||||||||||||||||||||||||
| import { useCarouselDots } from './use-carousel-dots'; | ||||||||||||||||||||||||||||||||||
| import { DotNavigation } from './dot-navigation'; | ||||||||||||||||||||||||||||||||||
| import Autoplay from 'embla-carousel-autoplay'; | ||||||||||||||||||||||||||||||||||
| import { Advertisement } from '@/types/domain/advertisement'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| interface CarouselWithDotsProps { | ||||||||||||||||||||||||||||||||||
| advertisements: Advertisement[]; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| export function CarouselWithDots({ advertisements }: CarouselWithDotsProps) { | ||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||
| const [api, setApi] = useState<CarouselApi>(); | ||||||||||||||||||||||||||||||||||
| const { current, count, scrollTo } = useCarouselDots(api); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. React Rules of Hooks 위반 — early return이 Line 23의 early return이 Line 25의 early return을 hooks 호출 이후로 이동시켜야 합니다. 제안 export default function CarouselWithDots({
advertisements,
}: CarouselWithDotsProps) {
- if (advertisements.length === 0) return null;
-
const [api, setApi] = useState<CarouselApi>();
const { current, count, scrollTo } = useCarouselDots(api);
+ if (advertisements.length === 0) return null;
+
return (Based on learnings: "ALWAYS use 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||
| <div className="w-screen max-w-200"> | ||||||||||||||||||||||||||||||||||
| <Carousel | ||||||||||||||||||||||||||||||||||
| setApi={setApi} | ||||||||||||||||||||||||||||||||||
| opts={{ | ||||||||||||||||||||||||||||||||||
| loop: true, | ||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||
| plugins={[ | ||||||||||||||||||||||||||||||||||
| Autoplay({ | ||||||||||||||||||||||||||||||||||
| delay: 3000, | ||||||||||||||||||||||||||||||||||
| stopOnInteraction: false, | ||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||
| ]} | ||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||
| <CarouselContent className="m-0 max-h-125 w-full"> | ||||||||||||||||||||||||||||||||||
| {advertisements.map((item) => ( | ||||||||||||||||||||||||||||||||||
| <CarouselItem key={item.id} className="p-0"> | ||||||||||||||||||||||||||||||||||
| <div className="relative aspect-380/275 h-full w-full overflow-hidden"> | ||||||||||||||||||||||||||||||||||
| <Image | ||||||||||||||||||||||||||||||||||
| src={item.imageUrl} | ||||||||||||||||||||||||||||||||||
| alt={item.title} | ||||||||||||||||||||||||||||||||||
| fill | ||||||||||||||||||||||||||||||||||
| className="object-cover" | ||||||||||||||||||||||||||||||||||
| priority | ||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||
| <div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" /> | ||||||||||||||||||||||||||||||||||
| <div className="absolute bottom-0 left-0 right-0 p-6 text-white"> | ||||||||||||||||||||||||||||||||||
| <h3 className="mb-2 text-2xl font-black break-keep"> | ||||||||||||||||||||||||||||||||||
| {item.title} | ||||||||||||||||||||||||||||||||||
| </h3> | ||||||||||||||||||||||||||||||||||
| <p className="text-lg font-medium break-keep opacity-90"> | ||||||||||||||||||||||||||||||||||
| {item.description} | ||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||
| </CarouselItem> | ||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||
| </CarouselContent> | ||||||||||||||||||||||||||||||||||
| </Carousel> | ||||||||||||||||||||||||||||||||||
| <DotNavigation count={count} current={current} onDotClick={scrollTo} /> | ||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,97 +1 @@ | ||
| 'use client'; | ||
|
|
||
| import { useState } from 'react'; | ||
| import Link from 'next/link'; // Link 태그 추가 | ||
| import { | ||
| Carousel, | ||
| CarouselContent, | ||
| CarouselItem, | ||
| type CarouselApi, | ||
| } from '@/components/ui/carousel'; | ||
| import { useCarouselDots } from './use-carousel-dots'; | ||
| import { DotNavigation } from './dot-navigation'; | ||
| import Autoplay from 'embla-carousel-autoplay'; | ||
|
|
||
| export function CarouselWithDots() { | ||
| const [api, setApi] = useState<CarouselApi>(); | ||
| const { current, count, scrollTo } = useCarouselDots(api); | ||
|
|
||
| const bannerContent = [ | ||
| { | ||
| title: '“프롬프트 엔지니어링 (aka 구걸)”', | ||
| desc: '"제발 한 번만 돌아가게 해줘"라고 정중하게 부탁해서 얻어낸 코드입니다. AI는 예의 바른 사람을 좋아하거든요.', | ||
| handle: '@bk-git-hub', | ||
| link: 'https://github.com/bk-git-hub', | ||
| }, | ||
| { | ||
| title: '“버그는 죽지 않는다, 다만 숨을 뿐”', | ||
| desc: '버그는 우리와 함께 살아가는 동반자입니다. 죽이려 하지 마세요. 그냥 다른 곳으로 옮기세요.', | ||
| handle: '@Seoje1405', | ||
| link: 'https://github.com/Seoje1405', | ||
| }, | ||
| { | ||
| title: '“할루시네이션도 하나의 기능입니다”', | ||
| desc: 'AI가 존재하지 않는 라이브러리를 사용했다고요? 그건 저희가 앞으로 만들겠다는 미래지향적 비전입니다.', | ||
| handle: '@bk-git-hub', | ||
| link: 'https://github.com/bk-git-hub', | ||
| }, | ||
| { | ||
| title: '“커밋 메시지 ‘fix’는 기도의 한 종류”', | ||
| desc: '아무것도 안 고쳤지만 제발 이번엔 배포가 성공하길 바라는 개발자의 간절한 염원입니다.', | ||
| handle: 'Seoje1405', | ||
| link: 'https://github.com/Seoje1405', | ||
| }, | ||
|
|
||
| { | ||
| title: '“완벽한 코드는 배포하지 않은 코드”', | ||
| desc: '세상에 돌아가는 쓰레기는 있어도 완벽한 코드는 없습니다. 돌아가면 일단 퇴근하십시오.', | ||
| handle: '@bk-git-hub', | ||
| link: 'https://github.com/bk-git-hub', | ||
| }, | ||
| ]; | ||
|
|
||
| return ( | ||
| <div className="w-screen max-w-200"> | ||
| <Carousel | ||
| setApi={setApi} | ||
| opts={{ | ||
| loop: true, | ||
| }} | ||
| plugins={[ | ||
| Autoplay({ | ||
| delay: 3000, | ||
| stopOnInteraction: false, | ||
| }), | ||
| ]} | ||
| > | ||
| <CarouselContent className="m-0 max-h-125 w-full"> | ||
| {bannerContent.map((item, i) => ( | ||
| <CarouselItem key={i} className="p-0"> | ||
| <Link | ||
| href={item.link} | ||
| target="_blank" | ||
| className="block h-full w-full transition-transform outline-none active:scale-95" | ||
| > | ||
| <div className="bg-card text-card-foreground hover:bg-accent flex aspect-380/275 h-full w-full flex-col items-center justify-center border-4 p-6 text-center transition-colors"> | ||
| <span className="mb-2 text-xs font-bold tracking-widest uppercase opacity-60"> | ||
| {item.handle} | ||
| </span> | ||
| <h3 className="mb-4 text-2xl font-black break-keep italic underline decoration-yellow-400 underline-offset-4"> | ||
| {item.title} | ||
| </h3> | ||
| <p className="text-lg font-medium break-keep opacity-90"> | ||
| {item.desc} | ||
| </p> | ||
| <span className="mt-4 text-[10px] font-bold opacity-40 group-hover:opacity-100"> | ||
| CLICK TO VISIT GITHUB | ||
| </span> | ||
| </div> | ||
| </Link> | ||
| </CarouselItem> | ||
| ))} | ||
| </CarouselContent> | ||
| </Carousel> | ||
| <DotNavigation count={count} current={current} onDotClick={scrollTo} /> | ||
| </div> | ||
| ); | ||
| } | ||
| export { default } from './banner-carousel-container'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| 'use client'; | ||
|
|
||
| import { useState } from 'react'; | ||
| import { BrandWithProducts } from '@/types/domain/brand'; | ||
| import RecommendedBrandHeader from './recommended-brand-header'; | ||
| import RecommendedBrandGridCard from './recommended-brand-grid-card'; | ||
|
|
||
| interface RecommendedBrandClientProps { | ||
| brands: BrandWithProducts[]; | ||
| } | ||
|
|
||
| export default function RecommendedBrandClient({ | ||
| brands, | ||
| }: RecommendedBrandClientProps) { | ||
| const [selectedIndex, setSelectedIndex] = useState<number>(0); | ||
|
|
||
| const currentBrand = brands[selectedIndex]; | ||
| const currentProducts = currentBrand?.products || []; | ||
|
|
||
| return ( | ||
| <div className="flex w-full flex-col p-5"> | ||
| <RecommendedBrandHeader | ||
| brands={brands} | ||
| onClick={setSelectedIndex} | ||
| selectedIndex={selectedIndex} | ||
| /> | ||
|
|
||
| <div className="mt-4"> | ||
| <div className="mt-2 w-full"> | ||
| {currentProducts.length > 0 ? ( | ||
| <ul className="grid w-full grid-cols-2 justify-items-center gap-4"> | ||
| {currentProducts.map((product) => ( | ||
| <li key={product.id}> | ||
| <RecommendedBrandGridCard product={product} /> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| ) : ( | ||
| <p>No products available for this brand.</p> | ||
| )} | ||
|
Comment on lines
+42
to
+44
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 하드코딩된 영어 문구 — i18n 고려 필요. 한국어 서비스 기준으로 🤖 Prompt for AI Agents |
||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.