diff --git a/src/app/(crew)/my-crew/hosted/page.tsx b/src/app/(crew)/my-crew/hosted/page.tsx index 4f0f330c..6e388ca6 100644 --- a/src/app/(crew)/my-crew/hosted/page.tsx +++ b/src/app/(crew)/my-crew/hosted/page.tsx @@ -4,6 +4,7 @@ import { Loader } from '@mantine/core'; import { useGetMyCrewHostedQuery } from '@/src/_queries/crew/my-crew-hosted-list-query'; import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll'; import CrewCardList from '@/src/components/common/crew-list/crew-card-list'; +import CrewSkeletonList from '@/src/components/common/skeleton/crew-skeleton-list'; export default function MyCrewHostedPage() { const { data, isLoading, error, ref, isFetchingNextPage } = useInfiniteScroll( @@ -15,9 +16,7 @@ export default function MyCrewHostedPage() {
{isLoading || isFetchingNextPage ? ( -
- -
+ ) : (
)} diff --git a/src/app/(crew)/my-crew/joined/page.tsx b/src/app/(crew)/my-crew/joined/page.tsx index e016ecae..23c47f14 100644 --- a/src/app/(crew)/my-crew/joined/page.tsx +++ b/src/app/(crew)/my-crew/joined/page.tsx @@ -4,6 +4,7 @@ import { Loader } from '@mantine/core'; import { useGetMyCrewJoinedQuery } from '@/src/_queries/crew/my-crew-joined-list-query'; import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll'; import CrewCardList from '@/src/components/common/crew-list/crew-card-list'; +import CrewSkeletonList from '@/src/components/common/skeleton/crew-skeleton-list'; export default function MyCrewJoinedPage() { const { data, isLoading, error, ref, isFetchingNextPage } = useInfiniteScroll( @@ -15,9 +16,7 @@ export default function MyCrewJoinedPage() {
{isLoading || isFetchingNextPage ? ( -
- -
+ ) : (
)} diff --git a/src/app/(crew)/my-gathering/hosted/page.tsx b/src/app/(crew)/my-gathering/hosted/page.tsx index 8fd6fd23..8a122dee 100644 --- a/src/app/(crew)/my-gathering/hosted/page.tsx +++ b/src/app/(crew)/my-gathering/hosted/page.tsx @@ -6,13 +6,14 @@ import { useGetHostedGatheringListQuery } from '@/src/_queries/my-gathering/host import { formatDateToRequest } from '@/src/utils/format-date'; import GatheringListWithDate from '@/src/app/(crew)/my-gathering/_component/gathering-list-with-date'; import PopOverCalendar from '@/src/components/common/input/pop-over-calendar'; +import MyGatheringSkeletonList from '@/src/components/common/skeleton/my-gathering-skeleton-list'; import { GatheringCardProps } from '@/src/types/gathering-data'; export default function MyGatheringHostedPage() { const [selectedDate, setSelectedDate] = useState(new Date()); const [hostedGatheringList, setHostedGatheringList] = useState(); - const { data, refetch } = useQuery( + const { data, isLoading, refetch } = useQuery( useGetHostedGatheringListQuery(formatDateToRequest(selectedDate)), ); @@ -29,6 +30,7 @@ export default function MyGatheringHostedPage() {
setSelectedDate(d)} />
+ {isLoading && } {hostedGatheringList && }
); diff --git a/src/app/(crew)/my-gathering/joined/page.tsx b/src/app/(crew)/my-gathering/joined/page.tsx index 9ba850a4..d4233a58 100644 --- a/src/app/(crew)/my-gathering/joined/page.tsx +++ b/src/app/(crew)/my-gathering/joined/page.tsx @@ -6,13 +6,14 @@ import { useGetJoinedGatheringListQuery } from '@/src/_queries/my-gathering/join import { formatDateToRequest } from '@/src/utils/format-date'; import GatheringListWithDate from '@/src/app/(crew)/my-gathering/_component/gathering-list-with-date'; import PopOverCalendar from '@/src/components/common/input/pop-over-calendar'; +import MyGatheringSkeletonList from '@/src/components/common/skeleton/my-gathering-skeleton-list'; import { GatheringCardProps } from '@/src/types/gathering-data'; export default function MyGatheringJoinedPage() { const [selectedDate, setSelectedDate] = useState(new Date()); const [joinedGatheringList, setJoinedGatheringList] = useState(); - const { data, refetch } = useQuery( + const { data, isLoading, refetch } = useQuery( useGetJoinedGatheringListQuery(formatDateToRequest(selectedDate)), ); @@ -29,6 +30,7 @@ export default function MyGatheringJoinedPage() {
setSelectedDate(d)} />
+ {isLoading && } {joinedGatheringList && }
); diff --git a/src/app/(crew)/my-page/_components/profile-card/container.tsx b/src/app/(crew)/my-page/_components/profile-card/container.tsx index 2f548646..a5ffb981 100644 --- a/src/app/(crew)/my-page/_components/profile-card/container.tsx +++ b/src/app/(crew)/my-page/_components/profile-card/container.tsx @@ -10,6 +10,7 @@ import { } from '@/src/_apis/auth/user-apis'; import { useUser } from '@/src/_queries/auth/user-queries'; import { useAuth } from '@/src/hooks/use-auth'; +import ProfileSkeleton from '@/src/components/common/skeleton/profile-skeleton'; import ProfileCardPresenter from './presenter'; export default function ProfileCard() { @@ -38,7 +39,7 @@ export default function ProfileCard() { } }, [user]); - if (userLoading || !isAuthChecked) return
로딩 중...
; + if (userLoading || !isAuthChecked) return ; if (!user) return null; const handleEdit = () => { diff --git a/src/app/(crew)/page.tsx b/src/app/(crew)/page.tsx index f7bdca76..76ed33d2 100644 --- a/src/app/(crew)/page.tsx +++ b/src/app/(crew)/page.tsx @@ -2,7 +2,7 @@ import { useRef, useState } from 'react'; import Image from 'next/image'; -import { Divider, Loader, TextInput } from '@mantine/core'; +import { Divider, Loader, Skeleton, TextInput } from '@mantine/core'; import { useGetCrewListQuery } from '@/src/_queries/crew/crew-list-queries'; import regionData from '@/src/data/region.json'; import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll'; @@ -11,6 +11,7 @@ import HeroCrew from '@/src/app/(crew)/_components/hero/hero-crew'; import CrewCardList from '@/src/components/common/crew-list/crew-card-list'; import Button from '@/src/components/common/input/button'; import DropDown from '@/src/components/common/input/drop-down'; +import CrewSkeletonList from '@/src/components/common/skeleton/crew-skeleton-list'; import IcoSearch from '@/public/assets/icons/ic-search.svg'; export default function HomePage() { @@ -133,9 +134,7 @@ export default function HomePage() {
{data && } {isLoading || isFetchingNextPage ? ( -
- -
+ ) : (
)} diff --git a/src/components/common/crew-list/crew-card-list.tsx b/src/components/common/crew-list/crew-card-list.tsx index 26bd3a53..c6482646 100644 --- a/src/components/common/crew-list/crew-card-list.tsx +++ b/src/components/common/crew-list/crew-card-list.tsx @@ -33,7 +33,7 @@ export default function CrewCardList({ data, inWhere }: CrewCardListProps) { ); return ( -
    +
      {crewDataList.map((inform) => (
    • router.push(CREWPAGE)} - className="relative mx-auto flex h-[430px] w-full animate-fade cursor-pointer flex-col overflow-hidden rounded-[14px] bg-white shadow-bg md:h-[203px] md:flex-row" + className="relative mx-auto flex w-full animate-fade cursor-pointer flex-col overflow-hidden rounded-[14px] bg-white transition-shadow hover:shadow-card md:h-[203px] md:flex-row" > {/* 썸네일 */}
      {title}
      -
      +
      @@ -66,7 +66,7 @@ export default function CrewCard({ {`현재 ${totalGatheringCount}개의 약속이 개설되어 있습니다.`}
      -
      +
      diff --git a/src/components/common/input/date-time-picker/index.tsx b/src/components/common/input/date-time-picker/index.tsx index a20ecec7..e959f59d 100644 --- a/src/components/common/input/date-time-picker/index.tsx +++ b/src/components/common/input/date-time-picker/index.tsx @@ -18,10 +18,18 @@ export default function DateTimePicker({ fullDate, onChange }: DateTimePickerPro const [minute, setMinute] = useState('분'); const handleSelect = (date: Date) => { + const newDate = new Date(selected); const isSelected = selected; + newDate.setFullYear(Number(date.getFullYear())); + newDate.setMonth(Number(date.getMonth())); + newDate.setDate(Number(date.getDate())); + + const kstDate = new Date(newDate.getTime() - newDate.getTimezoneOffset() * 60000).toISOString(); + if (isSelected) { - setSelected(date); + setSelected(newDate); + onChange(kstDate); } }; @@ -53,7 +61,7 @@ export default function DateTimePicker({ fullDate, onChange }: DateTimePickerPro })} firstDayOfWeek={0} classNames={{ - day: 'w-full aspect-square text-gray-800 data-[selected=true]:text-white flex', + day: 'w-full aspect-square text-gray-800 data-[selected=true]:text-white data-[selected=true]:rounded-xl flex', monthCell: 'w-[calc(14.285vw-5.714px)] aspect-square md:w-[85px]', monthsListCell: 'w-[calc(33.333vw-13.333px)] h-[10vw] md:w-[200px] md:h-[80px] text-gray-800', diff --git a/src/components/common/skeleton/crew-skeleton-list/index.tsx b/src/components/common/skeleton/crew-skeleton-list/index.tsx new file mode 100644 index 00000000..a5ff327f --- /dev/null +++ b/src/components/common/skeleton/crew-skeleton-list/index.tsx @@ -0,0 +1,18 @@ +import CrewSkeleton from '../crew-skeleton'; + +interface CrewSkeletonListProps { + num: number; + column?: number; +} + +export default function CrewSkeletonList({ num, column }: CrewSkeletonListProps) { + const columnStyle = column === 2 ? 'lg:grid-cols-2' : 'lg:grid-cols-1'; + return ( +
      + {[...Array(num)].map((_, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
      + ); +} diff --git a/src/components/common/skeleton/crew-skeleton/index.tsx b/src/components/common/skeleton/crew-skeleton/index.tsx new file mode 100644 index 00000000..f434225f --- /dev/null +++ b/src/components/common/skeleton/crew-skeleton/index.tsx @@ -0,0 +1,15 @@ +import { Skeleton } from '@mantine/core'; + +export default function CrewSkeleton() { + return ( +
      + +
      + + + + +
      +
      + ); +} diff --git a/src/components/common/skeleton/gathering-skeleton-list/index.tsx b/src/components/common/skeleton/gathering-skeleton-list/index.tsx new file mode 100644 index 00000000..23a18a66 --- /dev/null +++ b/src/components/common/skeleton/gathering-skeleton-list/index.tsx @@ -0,0 +1,19 @@ +import GatheringSkeleton from '../gathering-skeleton'; + +interface GatheringSkeletonListProps { + num: number; +} + +export default function GatheringSkeletonList({ num }: GatheringSkeletonListProps) { + return ( +
      + {[...Array(num)].map((_, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
      + ); +} diff --git a/src/components/common/skeleton/gathering-skeleton/index.tsx b/src/components/common/skeleton/gathering-skeleton/index.tsx new file mode 100644 index 00000000..ec23a40d --- /dev/null +++ b/src/components/common/skeleton/gathering-skeleton/index.tsx @@ -0,0 +1,16 @@ +import { Skeleton } from '@mantine/core'; + +export default function GatheringSkeleton() { + return ( +
      + +
      + + + + + +
      +
      + ); +} diff --git a/src/components/common/skeleton/my-gathering-skeleton-list/index.tsx b/src/components/common/skeleton/my-gathering-skeleton-list/index.tsx new file mode 100644 index 00000000..d6934600 --- /dev/null +++ b/src/components/common/skeleton/my-gathering-skeleton-list/index.tsx @@ -0,0 +1,30 @@ +import { Skeleton } from '@mantine/core'; +import MyGatheringSkeleton from '../my-gathering-skeleton'; + +interface MyGatheringSkeletonListProps { + num: number; +} + +export default function MyGatheringSkeletonList({ num }: MyGatheringSkeletonListProps) { + return ( +
      + {[...Array(num)].map((_, index) => ( + // eslint-disable-next-line react/no-array-index-key +
      +
      +
      + + +
      +
      +
      +
      +
      +
      + +
      +
      + ))} +
      + ); +} diff --git a/src/components/common/skeleton/my-gathering-skeleton/index.tsx b/src/components/common/skeleton/my-gathering-skeleton/index.tsx new file mode 100644 index 00000000..8ff661b6 --- /dev/null +++ b/src/components/common/skeleton/my-gathering-skeleton/index.tsx @@ -0,0 +1,17 @@ +import { Skeleton } from '@mantine/core'; + +export default function MyGatheringSkeleton() { + return ( +
      + +
      + +
      + + +
      + +
      +
      + ); +} diff --git a/src/components/common/skeleton/my-review-skeleton-list/index.tsx b/src/components/common/skeleton/my-review-skeleton-list/index.tsx new file mode 100644 index 00000000..e27836fd --- /dev/null +++ b/src/components/common/skeleton/my-review-skeleton-list/index.tsx @@ -0,0 +1,16 @@ +import MyReviewSkeleton from '../my-review-skeleton'; + +interface MyReviewSkeletonListProps { + num: number; +} + +export default function MyReviewSkeletonList({ num }: MyReviewSkeletonListProps) { + return ( +
      + {[...Array(num)].map((_, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
      + ); +} diff --git a/src/components/common/skeleton/my-review-skeleton/index.tsx b/src/components/common/skeleton/my-review-skeleton/index.tsx new file mode 100644 index 00000000..057ededf --- /dev/null +++ b/src/components/common/skeleton/my-review-skeleton/index.tsx @@ -0,0 +1,16 @@ +import { Skeleton } from '@mantine/core'; + +export default function MyReviewSkeleton() { + return ( +
      + +
      + + + + + +
      +
      + ); +} diff --git a/src/components/common/skeleton/profile-skeleton/index.tsx b/src/components/common/skeleton/profile-skeleton/index.tsx new file mode 100644 index 00000000..ef523718 --- /dev/null +++ b/src/components/common/skeleton/profile-skeleton/index.tsx @@ -0,0 +1,13 @@ +import { Skeleton } from '@mantine/core'; + +export default function ProfileSkeleton() { + return ( +
      + +
      + + +
      +
      + ); +} diff --git a/src/components/common/skeleton/reviewable-review-skeleton-list/index.tsx b/src/components/common/skeleton/reviewable-review-skeleton-list/index.tsx new file mode 100644 index 00000000..3a749f9f --- /dev/null +++ b/src/components/common/skeleton/reviewable-review-skeleton-list/index.tsx @@ -0,0 +1,16 @@ +import ReviewableReviewSkeleton from '../reviewable-review-skeleton'; + +interface ReviewableReviewSkeletonListProps { + num: number; +} + +export default function ReviewableReviewSkeletonList({ num }: ReviewableReviewSkeletonListProps) { + return ( +
      + {[...Array(num)].map((_, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
      + ); +} diff --git a/src/components/common/skeleton/reviewable-review-skeleton/index.tsx b/src/components/common/skeleton/reviewable-review-skeleton/index.tsx new file mode 100644 index 00000000..d8660d94 --- /dev/null +++ b/src/components/common/skeleton/reviewable-review-skeleton/index.tsx @@ -0,0 +1,15 @@ +import { Skeleton } from '@mantine/core'; + +export default function ReviewableReviewSkeleton() { + return ( +
      + +
      + + + + +
      +
      + ); +} diff --git a/src/components/gathering-list/liked-list-container.tsx b/src/components/gathering-list/liked-list-container.tsx index a800d8a6..6cb1a3b9 100644 --- a/src/components/gathering-list/liked-list-container.tsx +++ b/src/components/gathering-list/liked-list-container.tsx @@ -5,6 +5,7 @@ import { toast } from 'react-toastify'; import { addLike, removeLike } from '@/src/_apis/liked/liked-apis'; import { useGetLikedListQuery } from '@/src/_queries/liked/liked-queries'; import { ApiError } from '@/src/utils/api'; +import GatheringSkeletonList from '../common/skeleton/gathering-skeleton-list'; import LikedListPresenter from './liked-list-presenter'; export default function LikedList() { @@ -37,7 +38,12 @@ export default function LikedList() { setPage(newPage); }; - if (isLoading) return
      로딩중...
      ; + if (isLoading) + return ( +
      + +
      + ); // 에러 처리: error 또는 gatheringData가 undefined일 경우 if (error || gatheringData === undefined) { diff --git a/tailwind.config.ts b/tailwind.config.ts index dda96831..a90479ca 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -121,6 +121,7 @@ const config: Config = { boxShadow: { xl: '0 4px 4px 0 rgba(0,0,0,0.25)', bg: '0 4px 30px 1px rgba(0,122,255,0.04)', + card: '0 4px 15px 1px rgba(0,122,255,0.1)', xs: '0px 2px 4px 0px rgba(0, 0, 0, 0.02)', }, keyframes: {