Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/api/employer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from '@/lib/axios';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';
import { default as originAxios } from 'axios';

export async function postShop(body: Omit<RegisterFormData, 'image'>) {
Expand Down
46 changes: 46 additions & 0 deletions src/components/features/my-shop/indexModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Modal } from '@/components/ui';
import { useRouter } from 'next/router';

interface Props {
guestRedirect: boolean;
setGuestRedirect: (value: boolean) => void;
employeeRedirect: boolean;
setEmployeeRedirect: (value: boolean) => void;
}

const IndexModal = ({
guestRedirect,
setGuestRedirect,
employeeRedirect,
setEmployeeRedirect,
}: Props) => {
const router = useRouter();
return (
<>
<Modal
open={guestRedirect}
onClose={() => setGuestRedirect(false)}
variant='warning'
title='로그인이 필요합니다.'
primaryText='확인'
onPrimary={() => {
setGuestRedirect(false);
router.push('/login');
}}
/>
<Modal
open={employeeRedirect}
onClose={() => setEmployeeRedirect(false)}
variant='warning'
title='접근권한이 없습니다.'
primaryText='확인'
onPrimary={() => {
setEmployeeRedirect(false);
router.push('/');
}}
/>
</>
);
};

export default IndexModal;
2 changes: 1 addition & 1 deletion src/components/features/my-shop/registerAddress.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Dropdown, Input } from '@/components/ui';
import { ADDRESS_CODE } from '@/constants/dropdown';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';

interface Props {
formData: RegisterFormData;
Expand Down
2 changes: 1 addition & 1 deletion src/components/features/my-shop/registerDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';

interface Props {
formData: RegisterFormData;
Expand Down
2 changes: 1 addition & 1 deletion src/components/features/my-shop/registerName.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Dropdown, Input } from '@/components/ui';
import { CATEGORY_CODE } from '@/constants/dropdown';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';

interface Props {
formData: RegisterFormData;
Expand Down
2 changes: 1 addition & 1 deletion src/components/features/my-shop/registerWage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Input } from '@/components/ui';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';
import { ChangeEvent } from 'react';

interface Props {
Expand Down
2 changes: 1 addition & 1 deletion src/components/features/my-shop/shopForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import RegisterName from '@/components/features/my-shop/registerName';
import RegisterWage from '@/components/features/my-shop/registerWage';
import { Container } from '@/components/layout';
import { Button, Icon } from '@/components/ui';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';
import { ChangeEvent, useEffect, useState } from 'react';

interface ShopFromProps {
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Link from 'next/link';
const Footer = () => {
return (
<footer>
<Container className='max-w-full bg-gray-100'>
<Container className='max-w-full bg-gray-200'>
<div className='mx-auto flex flex-wrap justify-between gap-10 px-5 py-8 desktop:w-[964px]'>
<div className='order-3 flex-grow text-caption font-normal text-gray-500 tablet:order-1 tablet:flex-grow-0 tablet:text-body-m'>
©codeit - 2023
Expand Down
2 changes: 1 addition & 1 deletion src/pages/my-shop/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ShopForm from '@/components/features/my-shop/shopForm';
import { Header, Wrapper } from '@/components/layout';
import useAuth from '@/hooks/useAuth';
import { NextPageWithLayout } from '@/pages/_app';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';
import { useEffect, useState } from 'react';

const Edit: NextPageWithLayout = () => {
Expand Down
163 changes: 126 additions & 37 deletions src/pages/my-shop/index.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,117 @@
import { getNotice, getShop } from '@/api/employer';
import { Frame } from '@/components/layout';
import { Button, Notice } from '@/components/ui';
import IndexModal from '@/components/features/my-shop/indexModal';
import { Container, Frame } from '@/components/layout';
import { Button, Notice, Post } from '@/components/ui';
import useAuth from '@/hooks/useAuth';
import { NoticeItem, NoticeResponse, ShopItem } from '@/types/myShop';
import Link from 'next/link';
import { useEffect, useState } from 'react';

interface ShopItem {
id: string;
title?: string;
description?: string;
}
import { useCallback, useEffect, useRef, useState } from 'react';

const Myshop = () => {
const { user } = useAuth();
const [shopData, setShopData] = useState({});
const [shopNotice, setShopNotice] = useState({});
const { user, bootstrapped } = useAuth();
const [shopData, setShopData] = useState<ShopItem>({
id: '',
name: '',
category: '',
address1: '',
imageUrl: '',
originalHourlyPay: 0,
description: '',
});
const [shopNotice, setShopNotice] = useState<NoticeItem[]>([]);
const [nextOffset, setNextOffset] = useState<number | null>(0);
const [loading, setLoading] = useState(false);
const [guestRedirect, setGuestRedirect] = useState(false);
const [employeeRedirect, setEmployeeRedirect] = useState(false);
const observerRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
if (!bootstrapped) return;
if (user === null) {
setGuestRedirect(true);
return;
}
if (user?.type === 'employee') {
setEmployeeRedirect(true);
return;
}
}, [user, bootstrapped]);

useEffect(() => {
if (!user?.shop) return;
const shopId = user.shop.item.id;

const get = async () => {
if (!user?.shop) return;
try {
const [shopRes, noticeRes] = await Promise.all([
getShop(user.shop.item.id),
getNotice(user.shop.item.id, { offset: 0, limit: 6 }),
]);

const shopRes = await getShop(shopId);
const { description, ...rest } = shopRes.item;
const formattedShopData = { ...rest, shopDescription: description };
setShopData(formattedShopData);
setShopNotice(noticeRes);
//console.log('공고 조회:', noticeRes);
setShopData({ ...rest, shopDescription: description });
setShopNotice([]);
setNextOffset(0);
loadMoreNotice();
} catch (error) {
alert(error);
}
};
get();
}, [user]);

const loadMoreNotice = useCallback(async () => {
if (!user?.shop || nextOffset === null || loading) return;
setLoading(true);
try {
const noticeRes: NoticeResponse = await getNotice(user.shop.item.id, {
offset: nextOffset,
limit: 6,
});
setShopNotice(prevShopNotice => {
const newItems = noticeRes.items.map(i => i.item);
const merged = [...prevShopNotice, ...newItems];
const unique = merged.filter(
(item, index, self) => index === self.findIndex(i => i.id === item.id)
);
return unique;
});
setNextOffset(noticeRes.hasNext ? nextOffset + noticeRes.items.length : null);
} catch (error) {
alert(error);
} finally {
setLoading(false);
}
}, [user?.shop, nextOffset, loading]);

useEffect(() => {
if (!observerRef.current) return;
const observer = new IntersectionObserver(
entries => {
if (entries[0].isIntersecting) {
loadMoreNotice();
}
},
{ threshold: 0.5 }
);
observer.observe(observerRef.current);
return () => observer.disconnect();
}, [loadMoreNotice]);

return (
<>
{user?.shop ? (
<IndexModal
guestRedirect={guestRedirect}
setGuestRedirect={setGuestRedirect}
employeeRedirect={employeeRedirect}
setEmployeeRedirect={setEmployeeRedirect}
/>
{!user?.shop ? (
<Frame
title='내 가게'
content='내 가게를 소개하고 공고도 등록해 보세요.'
buttonText='가게 등록하기'
href='/my-shop/register'
/>
) : (
<>
<Notice notice={shopData} variant='shop'>
<Notice notice={shopData} variant='shop' className='mt-5 tablet:mt-8'>
<div className='flex gap-2'>
<Button
as={Link}
Expand All @@ -60,20 +130,39 @@ const Myshop = () => {
</Button>
</div>
</Notice>
<Frame
title='등록한 공고'
content='공고를 등록해 보세요.'
buttonText='공고 등록하기'
href='/employer/shops/${user.shop.item.id}/notices/register'
/>
<section className='mt-10 max-w-full bg-gray-100 tablet:mt-20'>
{shopNotice.length > 0 ? (
<Container as='section' isPage className='pt-0'>
<div className='mt-7 tablet:mt-0'>
<h2 className='mb-4 text-heading-l font-bold tablet:mb-8'>내가 등록한 공고</h2>
<div className='grid grid-cols-2 gap-x-4 gap-y-8 desktop:grid-cols-3'>
{shopNotice.map(item => {
const mergedNotice = {
...item,
imageUrl: shopData.imageUrl,
name: shopData.name,
address1: shopData.address1,
shopId: shopData.id,
originalHourlyPay: shopData.originalHourlyPay,
};
return <Post key={item.id} notice={mergedNotice} />;
})}
</div>
<div ref={observerRef} className='flex h-12 items-center justify-center'>
{loading && <p className='text-gray-500'>불러오는 중...</p>}
</div>
</div>
</Container>
) : (
<Frame
title='등록한 공고'
content='공고를 등록해 보세요.'
buttonText='공고 등록하기'
href={`/employer/shops/${user.shop.item.id}/notices/register`}
/>
)}
</section>
</>
) : (
<Frame
title='내 가게'
content='내 가게를 소개하고 공고도 등록해 보세요.'
buttonText='가게 등록하기'
href='/my-shop/register'
/>
)}
</>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/my-shop/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { postPresignedUrl, postShop, uploadImage } from '@/api/employer';
import ShopForm from '@/components/features/my-shop/shopForm';
import { Header, Wrapper } from '@/components/layout';
import { NextPageWithLayout } from '@/pages/_app';
import RegisterFormData from '@/types/myShop';
import { RegisterFormData } from '@/types/myShop';

const Register: NextPageWithLayout = () => {
const handleRegister = async (formData: RegisterFormData) => {
Expand Down
32 changes: 30 additions & 2 deletions src/types/myShop.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스크린샷 2025-10-18 오후 2 30 40 해당부분 export 변경되면서 다른파일에서 모듈을 읽지못하고 있어서 버셀 에러가 발생했습니다 수정부탁드립니다!

파일 : ./src/api/employer.ts:2:8
내용 : Type error: Module '"/vercel/path0/src/types/myShop"' has no default export. Did you mean to use 'import { RegisterFormData } from "/vercel/path0/src/types/myShop"' instead?
위치 : import RegisterFormData from '@/types/myShop';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 수정했습니다!

Copy link
Contributor

@sohyun0 sohyun0 Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

./src/components/features/my-shop/registerAddress.tsx:3:8
Type error: Module '"/vercel/path0/src/types/myShop"'
import RegisterFormData from '@/types/myShop';
부분도 수정부탁드립니다!

스크린샷 2025-10-19 오전 2 32 59

이렇게 검색되어서 나오는 부분은 전부 export 수정해주셔야 합니다!

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
interface RegisterFormData {
export interface RegisterFormData {
name: string;
category?: string;
address1?: string;
Expand All @@ -9,4 +9,32 @@ interface RegisterFormData {
imageUrl?: string;
}

export default RegisterFormData;
export interface ShopItem {
id: string;
name: string;
category: string;
address1: string;
imageUrl: string;
originalHourlyPay: number;
description: string;
}

export interface ShopResponse {
item: ShopItem;
}

export interface NoticeItem {
id: string;
hourlyPay: number;
startsAt: string;
workhour: number;
closed: boolean;
}

export interface NoticeResponse {
offset: number;
limit: number;
count: number;
hasNext: boolean;
items: { item: NoticeItem }[];
}