diff --git a/apps/duri/index.html b/apps/duri/index.html
index 0f5dcee4..51e8fa0e 100644
--- a/apps/duri/index.html
+++ b/apps/duri/index.html
@@ -9,6 +9,12 @@
crossorigin="anonymous"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.6/dist/web/static/pretendard-dynamic-subset.css"
/>
+
두리묭실
diff --git a/apps/duri/package.json b/apps/duri/package.json
index 5906899d..622a95e8 100644
--- a/apps/duri/package.json
+++ b/apps/duri/package.json
@@ -17,6 +17,7 @@
"@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.5",
"@tanstack/react-query": "^5.62.0",
+ "@tanstack/react-query-devtools": "^5.62.2",
"@tosspayments/tosspayments-sdk": "^2.3.2",
"@types/node": "^22.10.1",
"@typescript-eslint/eslint-plugin": "^8.15.0",
diff --git a/apps/duri/src/App.tsx b/apps/duri/src/App.tsx
index d721a741..27f7d055 100644
--- a/apps/duri/src/App.tsx
+++ b/apps/duri/src/App.tsx
@@ -15,13 +15,15 @@ import QuotationPage from '@pages/Quotation';
import RequestPage from '@pages/Request';
import Shop from '@pages/Shop';
+import Portfolio from './pages/Shop/Portfolio';
+import ShopDetail from './pages/Shop/ShopDetail';
+
function App() {
return (
} />
-
} />
} />
@@ -33,6 +35,8 @@ function App() {
} />
} />
} />
+ } />
+ } />
} />
diff --git a/apps/duri/src/assets/types/shop.ts b/apps/duri/src/assets/types/shop.ts
index 6d541c21..acd1c3af 100644
--- a/apps/duri/src/assets/types/shop.ts
+++ b/apps/duri/src/assets/types/shop.ts
@@ -1,12 +1,12 @@
export interface RegularShopProps {
salonIdx: string;
- salonName:string;
+ salonName: string;
salonImage: string;
salonScore?: number;
salonReviewCount?: number;
}
-export interface RecommendeShopProps extends RegularShopProps{
+export interface RecommendeShopProps extends RegularShopProps {
salonAddress: string;
salonTag: string[];
-}
\ No newline at end of file
+}
diff --git a/apps/duri/src/components/onboarding/PetPersonalityInfo.tsx b/apps/duri/src/components/onboarding/PetPersonalityInfo.tsx
index c4c8fae3..49eaedc5 100644
--- a/apps/duri/src/components/onboarding/PetPersonalityInfo.tsx
+++ b/apps/duri/src/components/onboarding/PetPersonalityInfo.tsx
@@ -47,7 +47,11 @@ const PetPersonalityInfo = ({
{name}는
어떤 성격을 가지고 있나요?
-
+
입력된 성격은 MY에서 변경가능해요.
0 || '성격에 대해 알려주세요.',
}}
render={() => (
-
+
{personalityOptions.map((value) => (
@@ -106,50 +113,22 @@ export const ShopList = forwardRef((_, ref) => {
: '17px 20px 28px 20px'
}
>
- toggleShopSelection(1)}
- />
- toggleShopSelection(2)}
- />
- toggleShopSelection(3)}
- />
- toggleShopSelection(4)}
- />
+ {/** 각 가게 정보 박스*/}
+ {nearbyShops?.map((shop) => (
+ toggleShopSelection(shop.shopId)}
+ tags={shop.tags}
+ />
+ ))}
{selectedShops.length > 0 ? (
@@ -166,14 +145,7 @@ export const ShopList = forwardRef((_, ref) => {
) : null}
- [maxHeight]}
- onDismiss={handleDismiss}
- >
+
((_, ref) => {
bg="transparent"
width="fit-content"
padding="0"
- onClick={() => setFilter('거리순')}
+ onClick={() => onFilterChange('distance')}
>
((_, ref) => {
bg="transparent"
width="fit-content"
padding="0"
- onClick={() => setFilter('별점순')}
+ onClick={() => onFilterChange('rating')}
>
((_, ref) => {
>
);
-});
-
-ShopList.displayName = 'ShopList';
+};
const ScrollFlex = styled(Flex)`
height: calc(100vh - 274.5px);
@@ -242,22 +212,3 @@ const ScrollFlex = styled(Flex)`
scrollbar-width: none;
}
`;
-
-const StyledBottomCss = css`
- position: relative;
-
- [data-rsbs-overlay],
- [data-rsbs-root]::after {
- border-radius: 16px 16px 0px 0px;
- z-index: 20;
- max-width: 480px;
-
- @media (min-width: 480px) {
- left: calc(50% - 240px);
- }
- }
-
- [data-rsbs-backdrop] {
- background-color: rgba(49, 48, 54, 0.5);
- }
-`;
diff --git a/apps/duri/src/components/shop/ShopPhotos.tsx b/apps/duri/src/components/shop/ShopPhotos.tsx
new file mode 100644
index 00000000..4b859876
--- /dev/null
+++ b/apps/duri/src/components/shop/ShopPhotos.tsx
@@ -0,0 +1,36 @@
+import { Flex } from '@duri-fe/ui';
+import styled from '@emotion/styled';
+
+export const ShopPhotos = () => {
+ const images = [
+ 'https://s3-alpha-sig.figma.com/img/1d85/dbcb/8d458ee15d53bb800b97c0da776b2909?Expires=1734307200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=bsJT2VGq6a~lRLJF9PaZBN8uBPvTrzvaZdf0vOPJfoNQ~-z7jq6hxODfjPytoebWbo2qilv6nihm3zGBKeZykT2HzA58as4Xcx0QJ26~1l6GlWr1JSmQNG2jiPDOnuzF5QYjik52HzOuB1y85zBkGKb-U1XiwOisvaSIthgJkLyZ74vG3bmO3a-VJCeFR0atgacdxDzGf6uoiX163aYu6X3R0sJ5Ux6ZpOHuWj6npG9I-eGlQeHZJmkODmqjhfjLyrmuUBilvH15pR0CkhAxwraIvSHx4qb3RK6o1K79sDldRPDQ6LaAu7fR-WmExifV~KFGVsSTiIwJOofVLqHHGQ__',
+ 'https://s3-alpha-sig.figma.com/img/1d85/dbcb/8d458ee15d53bb800b97c0da776b2909?Expires=1734307200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=bsJT2VGq6a~lRLJF9PaZBN8uBPvTrzvaZdf0vOPJfoNQ~-z7jq6hxODfjPytoebWbo2qilv6nihm3zGBKeZykT2HzA58as4Xcx0QJ26~1l6GlWr1JSmQNG2jiPDOnuzF5QYjik52HzOuB1y85zBkGKb-U1XiwOisvaSIthgJkLyZ74vG3bmO3a-VJCeFR0atgacdxDzGf6uoiX163aYu6X3R0sJ5Ux6ZpOHuWj6npG9I-eGlQeHZJmkODmqjhfjLyrmuUBilvH15pR0CkhAxwraIvSHx4qb3RK6o1K79sDldRPDQ6LaAu7fR-WmExifV~KFGVsSTiIwJOofVLqHHGQ__',
+ 'https://s3-alpha-sig.figma.com/img/1d85/dbcb/8d458ee15d53bb800b97c0da776b2909?Expires=1734307200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=bsJT2VGq6a~lRLJF9PaZBN8uBPvTrzvaZdf0vOPJfoNQ~-z7jq6hxODfjPytoebWbo2qilv6nihm3zGBKeZykT2HzA58as4Xcx0QJ26~1l6GlWr1JSmQNG2jiPDOnuzF5QYjik52HzOuB1y85zBkGKb-U1XiwOisvaSIthgJkLyZ74vG3bmO3a-VJCeFR0atgacdxDzGf6uoiX163aYu6X3R0sJ5Ux6ZpOHuWj6npG9I-eGlQeHZJmkODmqjhfjLyrmuUBilvH15pR0CkhAxwraIvSHx4qb3RK6o1K79sDldRPDQ6LaAu7fR-WmExifV~KFGVsSTiIwJOofVLqHHGQ__',
+ 'https://s3-alpha-sig.figma.com/img/1d85/dbcb/8d458ee15d53bb800b97c0da776b2909?Expires=1734307200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=bsJT2VGq6a~lRLJF9PaZBN8uBPvTrzvaZdf0vOPJfoNQ~-z7jq6hxODfjPytoebWbo2qilv6nihm3zGBKeZykT2HzA58as4Xcx0QJ26~1l6GlWr1JSmQNG2jiPDOnuzF5QYjik52HzOuB1y85zBkGKb-U1XiwOisvaSIthgJkLyZ74vG3bmO3a-VJCeFR0atgacdxDzGf6uoiX163aYu6X3R0sJ5Ux6ZpOHuWj6npG9I-eGlQeHZJmkODmqjhfjLyrmuUBilvH15pR0CkhAxwraIvSHx4qb3RK6o1K79sDldRPDQ6LaAu7fR-WmExifV~KFGVsSTiIwJOofVLqHHGQ__',
+ 'https://s3-alpha-sig.figma.com/img/1d85/dbcb/8d458ee15d53bb800b97c0da776b2909?Expires=1734307200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=bsJT2VGq6a~lRLJF9PaZBN8uBPvTrzvaZdf0vOPJfoNQ~-z7jq6hxODfjPytoebWbo2qilv6nihm3zGBKeZykT2HzA58as4Xcx0QJ26~1l6GlWr1JSmQNG2jiPDOnuzF5QYjik52HzOuB1y85zBkGKb-U1XiwOisvaSIthgJkLyZ74vG3bmO3a-VJCeFR0atgacdxDzGf6uoiX163aYu6X3R0sJ5Ux6ZpOHuWj6npG9I-eGlQeHZJmkODmqjhfjLyrmuUBilvH15pR0CkhAxwraIvSHx4qb3RK6o1K79sDldRPDQ6LaAu7fR-WmExifV~KFGVsSTiIwJOofVLqHHGQ__',
+ 'https://s3-alpha-sig.figma.com/img/1d85/dbcb/8d458ee15d53bb800b97c0da776b2909?Expires=1734307200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=bsJT2VGq6a~lRLJF9PaZBN8uBPvTrzvaZdf0vOPJfoNQ~-z7jq6hxODfjPytoebWbo2qilv6nihm3zGBKeZykT2HzA58as4Xcx0QJ26~1l6GlWr1JSmQNG2jiPDOnuzF5QYjik52HzOuB1y85zBkGKb-U1XiwOisvaSIthgJkLyZ74vG3bmO3a-VJCeFR0atgacdxDzGf6uoiX163aYu6X3R0sJ5Ux6ZpOHuWj6npG9I-eGlQeHZJmkODmqjhfjLyrmuUBilvH15pR0CkhAxwraIvSHx4qb3RK6o1K79sDldRPDQ6LaAu7fR-WmExifV~KFGVsSTiIwJOofVLqHHGQ__',
+ ];
+
+ return (
+
+
+ {images.map((src, index) => (
+
+ ))}
+
+
+ );
+};
+
+const PhotoGrid = styled.div`
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 7px;
+`;
+
+const ShopInsideImg = styled.img`
+ width: 107px;
+ height: 107px;
+ border-radius: 4px;
+ object-fit: cover;
+`;
diff --git a/apps/duri/src/components/shop/index.tsx b/apps/duri/src/components/shop/index.tsx
index f178c2f3..e4431002 100644
--- a/apps/duri/src/components/shop/index.tsx
+++ b/apps/duri/src/components/shop/index.tsx
@@ -1,3 +1,5 @@
export { MapInfo } from './MapInfo';
export { ShopList } from './ShopList';
export { ShopLine } from './ShopLine';
+export { ShopInfo } from './ShopInfo';
+export { DesignerInfo } from './DesignerInfo';
diff --git a/apps/duri/src/main.tsx b/apps/duri/src/main.tsx
index 907745c9..43ebf68f 100644
--- a/apps/duri/src/main.tsx
+++ b/apps/duri/src/main.tsx
@@ -1,19 +1,18 @@
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
-// import { worker } from './mocks/browser';
import App from './App';
import { enableMocking } from './mocks';
-// const rootElement = document.getElementById('app');
const queryClient = new QueryClient();
enableMocking().then(() => {
createRoot(document.getElementById('app')!).render(
- {/* */}
+
,
);
});
diff --git a/apps/duri/src/mocks/handler.ts b/apps/duri/src/mocks/handler.ts
index aa2e4934..0b29aaca 100644
--- a/apps/duri/src/mocks/handler.ts
+++ b/apps/duri/src/mocks/handler.ts
@@ -82,17 +82,16 @@ export const handlers = [
}),
http.get('https://api.example.com/api/v1/pet', () => {
- return HttpResponse.json(
- {
- petId:1,
- petName: "멍이",
- petImage: 'https://s3-alpha-sig.figma.com/img/be2d/15b9/6d6d9c56fd4e47388fa79cdfb8f3c4b6?Expires=1733702400&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=X1cOpSTMbxxMWxuaqTEsv4id96BVzSloxASEzxOzX87IO8neIKfYCuO8K04E5O8RniSoRCRo7JIlyuYn0AZd6Nda2W7tS8rN36WT7Ewzg-bnozvaUzH5Low6mzAGqUFhQY558k~oAoKolnCTzLEb1DSKR3Zq3Gj073fUrLrxyNnQmXqtbmMObX39c-Flw0~8kzap-ls787b58MKEnfTQ1AVHB8o-Y2~2mQWpFCMY8iKMjIsk84ToA0LhLmiGf5XkRLEtZwwWXFqsTXm0OFB2m~3uqsxFBkuW3gootLBj2ocEp400Za4j8C-IAFj1lNqPeoW9rpLdKLh0xOvoftqeaQ__',
- breed: "시츄",
- gender: "여아",
- age: "7살",
- weight: "1.4"
- },
- );
+ return HttpResponse.json({
+ petId: 1,
+ petName: '멍이',
+ petImage:
+ 'https://s3-alpha-sig.figma.com/img/be2d/15b9/6d6d9c56fd4e47388fa79cdfb8f3c4b6?Expires=1733702400&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=X1cOpSTMbxxMWxuaqTEsv4id96BVzSloxASEzxOzX87IO8neIKfYCuO8K04E5O8RniSoRCRo7JIlyuYn0AZd6Nda2W7tS8rN36WT7Ewzg-bnozvaUzH5Low6mzAGqUFhQY558k~oAoKolnCTzLEb1DSKR3Zq3Gj073fUrLrxyNnQmXqtbmMObX39c-Flw0~8kzap-ls787b58MKEnfTQ1AVHB8o-Y2~2mQWpFCMY8iKMjIsk84ToA0LhLmiGf5XkRLEtZwwWXFqsTXm0OFB2m~3uqsxFBkuW3gootLBj2ocEp400Za4j8C-IAFj1lNqPeoW9rpLdKLh0xOvoftqeaQ__',
+ breed: '시츄',
+ gender: '여아',
+ age: '7살',
+ weight: '1.4',
+ });
}),
http.get('https://api.example.com/api/v1/reservation', () => {
diff --git a/apps/duri/src/mocks/index.ts b/apps/duri/src/mocks/index.ts
index e911fe12..15a807ca 100644
--- a/apps/duri/src/mocks/index.ts
+++ b/apps/duri/src/mocks/index.ts
@@ -1,10 +1,11 @@
export async function enableMocking() {
- console.log(import.meta.env.VITE_MSW_ENABLED)
- if (import.meta.env.VITE_MSW_ENABLED === 'true') {
- const { worker } = await import('./browser');
- return await worker.start();
- }
- else {
- console.log('MSW is disabled');
- }
- }
\ No newline at end of file
+ console.log(import.meta.env.VITE_MSW_ENABLED);
+ if (import.meta.env.VITE_MSW_ENABLED === 'true') {
+ const { worker } = await import('./browser');
+ return await worker.start({
+ onUnhandledRequest: 'bypass',
+ });
+ } else {
+ console.log('MSW is disabled');
+ }
+}
diff --git a/apps/duri/src/pages/Shop/Portfolio.tsx b/apps/duri/src/pages/Shop/Portfolio.tsx
new file mode 100644
index 00000000..e6ea87d2
--- /dev/null
+++ b/apps/duri/src/pages/Shop/Portfolio.tsx
@@ -0,0 +1,60 @@
+import { useState } from 'react';
+
+import {
+ AbsoluteFlex,
+ FilledHeart,
+ Heart,
+ HeightFitFlex,
+ MobileLayout,
+ RelativeFlex,
+ Text,
+ theme,
+ WidthFitFlex,
+} from '@duri-fe/ui';
+import styled from '@emotion/styled';
+
+const Portfolio = () => {
+ const [isMarked, setIsMarked] = useState(false);
+
+ return (
+
+
+
+
+
+ 댕댕샵
+ setIsMarked(!isMarked)}
+ >
+ {isMarked ? (
+
+ ) : (
+
+ )}
+
+
+
+ 경기도 성남시 불정로 119
+
+
+
+ );
+};
+
+export default Portfolio;
+
+const MainImg = styled.img`
+ width: 100%;
+ position: relative;
+ aspect-ratio: 375 / 310;
+ object-fit: cover;
+`;
+
+const Gradation = styled.div`
+ background: linear-gradient(180deg, rgba(217, 217, 217, 0) 0%, #111 100%);
+`;
diff --git a/apps/duri/src/pages/Shop/ShopDetail.tsx b/apps/duri/src/pages/Shop/ShopDetail.tsx
new file mode 100644
index 00000000..77316508
--- /dev/null
+++ b/apps/duri/src/pages/Shop/ShopDetail.tsx
@@ -0,0 +1,16 @@
+// import { useParams } from 'react-router-dom';
+
+import { ShopInfo } from '@duri/components/shop';
+import { DuriNavbar, MobileLayout } from '@duri-fe/ui';
+
+const ShopDetail = () => {
+ // const { shopId } = useParams<{ shopId: string }>();
+ return (
+
+
+
+
+ );
+};
+
+export default ShopDetail;
diff --git a/apps/duri/src/pages/Shop/index.tsx b/apps/duri/src/pages/Shop/index.tsx
index 7acc8005..b74bb175 100644
--- a/apps/duri/src/pages/Shop/index.tsx
+++ b/apps/duri/src/pages/Shop/index.tsx
@@ -1,5 +1,4 @@
-import { useRef, useState } from 'react';
-import { BottomSheetRef } from 'react-spring-bottom-sheet';
+import { useEffect, useRef, useState } from 'react';
import { MapInfo } from '@duri/components/shop';
import { ShopList } from '@duri/components/shop/ShopList';
@@ -15,18 +14,47 @@ import {
TextField,
theme,
} from '@duri-fe/ui';
+import { ShopInfoType, useGetNearByShopInfo } from '@duri-fe/utils';
import styled from '@emotion/styled';
const Shop = () => {
const mapRef = useRef(null);
- const sheetRef = useRef(null);
const [isMap, setIsMap] = useState(true);
+ const [nearbyShops, setNearbyShops] = useState([]);
const changeMapType = () => {
setIsMap(!isMap);
};
+ const [filter, setFilter] = useState<'distance' | 'rating'>('distance');
+ const handleFilterChange = (filter: 'distance' | 'rating') => {
+ if (filter) {
+ setFilter(filter);
+ }
+ };
+
+ const { data } = useGetNearByShopInfo(
+ // location.coordinates
+ // ? {
+ // // lat: location.coordinates.lat,
+ // // long: location.coordinates.lng,
+ // // radius: 500,
+ // lat: 37.5156,
+ // lon: 127.0451005,
+ // radius: 500,
+ // }
+ // :
+ { lat: 37.5156, lon: 127.0451005, radius: 500 },
+ filter,
+ );
+
+ useEffect(() => {
+ if (data) {
+ setNearbyShops(data);
+ }
+ }, [data]);
+
return (
@@ -51,7 +79,11 @@ const Shop = () => {
>
) : (
-
+
)}
+
Vite + TS
diff --git a/apps/salon/package.json b/apps/salon/package.json
index c5ca1d80..e3bea2b4 100644
--- a/apps/salon/package.json
+++ b/apps/salon/package.json
@@ -17,6 +17,7 @@
"@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.5",
"@tanstack/react-query": "^5.62.1",
+ "@tanstack/react-query-devtools": "^5.62.2",
"@types/node": "^22.10.1",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0",
diff --git a/package.json b/package.json
index c50eef7e..3d7a6c9c 100644
--- a/package.json
+++ b/package.json
@@ -68,7 +68,7 @@
"@storybook/addons": "7.6.17",
"@storybook/manager-api": "8.4.5",
"@tanstack/react-query": "^5.62.0",
- "@tanstack/react-query-devtools": "5.61.0",
+ "@tanstack/react-query-devtools": "^5.62.2",
"@tosspayments/tosspayments-sdk": "2.3.2",
"@types/acorn": "^6.0.4",
"acorn": "^8.14.0",
diff --git a/packages/ui/src/assets/Close.tsx b/packages/ui/src/assets/Close.tsx
index d828ea66..bf1320ad 100644
--- a/packages/ui/src/assets/Close.tsx
+++ b/packages/ui/src/assets/Close.tsx
@@ -1,17 +1,55 @@
-import * as React from 'react';
-const SvgClose = (props: React.SVGProps) => (
-
-);
-export default SvgClose;
+import styled from '@emotion/styled';
+
+import { theme } from '../styles';
+
+export interface CloseProps {
+ width?: number;
+ height?: number;
+ margin?: string;
+ isOpen?: boolean;
+ toggleModal?: () => void;
+ currentColor?: string;
+}
+
+const Close = (props: CloseProps) => {
+ const { width, height, margin, toggleModal, currentColor } = props;
+
+ return (
+
+
+
+ );
+};
+
+export default Close;
+
+const CloseBox = styled.div<{
+ width?: number;
+ height?: number;
+ margin?: string;
+}>`
+ width: ${({ width }) => width ?? '17px'};
+ height: ${({ height }) => height ?? '17px'};
+ margin: ${({ margin }) => margin ?? ''};
+ &:hover {
+ cursor: pointer;
+ }
+`;
diff --git a/packages/ui/src/assets/Heart.tsx b/packages/ui/src/assets/Heart.tsx
index a5903d37..667d75f7 100644
--- a/packages/ui/src/assets/Heart.tsx
+++ b/packages/ui/src/assets/Heart.tsx
@@ -7,7 +7,7 @@ const SvgHeart = (props: React.SVGProps) => (
{...props}
>
) => (
+
+);
+export default SvgTime;
diff --git a/packages/ui/src/assets/index.tsx b/packages/ui/src/assets/index.tsx
index fb46d161..f75e657b 100644
--- a/packages/ui/src/assets/index.tsx
+++ b/packages/ui/src/assets/index.tsx
@@ -65,6 +65,7 @@ export { default as PortfolioIcon } from './PortfolioIcon';
export { default as TimetableIcon } from './TimetableIcon';
export { default as CurLocation } from './CurLocation';
export { default as Pencil } from './Pencil';
+export { default as Time } from './Time';
import Add from './Add';
import AddNew from './AddNew';
@@ -113,6 +114,7 @@ import Send from './Send';
import Shield from './Shield';
import Shopping from './Shopping';
import Star from './Star';
+import Time from './Time';
import TimetableIcon from './TimetableIcon';
import UnionDown from './UnionDown';
import UnionUp from './UnionUp';
@@ -174,6 +176,7 @@ export const icons = {
PortfolioIcon,
TimetableIcon,
Write,
+ Time,
};
export default icons;
diff --git a/packages/ui/src/assets/svgs/Time.svg b/packages/ui/src/assets/svgs/Time.svg
new file mode 100644
index 00000000..4abc17d7
--- /dev/null
+++ b/packages/ui/src/assets/svgs/Time.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/ui/src/components/Button/DefaultButton.tsx b/packages/ui/src/components/Button/DefaultButton.tsx
index ef43fedb..23d7dd44 100644
--- a/packages/ui/src/components/Button/DefaultButton.tsx
+++ b/packages/ui/src/components/Button/DefaultButton.tsx
@@ -49,4 +49,5 @@ export const FrontBtn = styled(Button)`
position: fixed;
bottom: 92px;
z-index: 3;
+ max-width: 375px;
`;
diff --git a/packages/ui/src/components/FlexBox/Flex.tsx b/packages/ui/src/components/FlexBox/Flex.tsx
index a0c6f2c3..1c282196 100644
--- a/packages/ui/src/components/FlexBox/Flex.tsx
+++ b/packages/ui/src/components/FlexBox/Flex.tsx
@@ -41,6 +41,10 @@ export const WidthFitFlex = styled(Flex)`
width: fit-content;
`;
+export const RelativeFlex = styled(Flex)`
+ position: relative;
+`;
+
export const AbsoluteFlex = styled(Flex)`
position: absolute;
top: 0;
diff --git a/packages/ui/src/components/FlexBox/index.tsx b/packages/ui/src/components/FlexBox/index.tsx
index 383f2e8a..1f705dd8 100644
--- a/packages/ui/src/components/FlexBox/index.tsx
+++ b/packages/ui/src/components/FlexBox/index.tsx
@@ -1 +1,8 @@
-export { Flex, HeightFitFlex, WidthFitFlex, AbsoluteFlex } from './Flex';
+export {
+ Flex,
+ HeightFitFlex,
+ WidthFitFlex,
+ AbsoluteFlex,
+ RelativeFlex,
+ FixedFlex,
+} from './Flex';
diff --git a/packages/ui/src/components/MobileLayout/MobileLayout.tsx b/packages/ui/src/components/MobileLayout/MobileLayout.tsx
index cdc37d41..751fe6e3 100644
--- a/packages/ui/src/components/MobileLayout/MobileLayout.tsx
+++ b/packages/ui/src/components/MobileLayout/MobileLayout.tsx
@@ -3,12 +3,6 @@ import React from 'react';
import { Flex, theme } from '@duri-fe/ui';
import styled from '@emotion/styled';
-const Container = styled(Flex)`
- max-width: 480px;
- min-height: 100vh;
- box-sizing: border-box;
-`;
-
export const MobileLayout = ({ children }: { children: React.ReactNode }) => {
return (
{
);
};
+
+const Container = styled(Flex)`
+ max-width: 375px;
+ min-height: 100vh;
+ box-sizing: border-box;
+`;
diff --git a/packages/ui/src/components/Modal/Modal.tsx b/packages/ui/src/components/Modal/Modal.tsx
new file mode 100644
index 00000000..18104830
--- /dev/null
+++ b/packages/ui/src/components/Modal/Modal.tsx
@@ -0,0 +1,82 @@
+import { forwardRef } from 'react';
+
+import { Close, Flex, Text, theme } from '@duri-fe/ui';
+import styled from '@emotion/styled';
+
+interface ModalProps {
+ title?: string;
+ isOpen: boolean;
+ toggleModal: () => void;
+ children: React.ReactNode;
+}
+
+export const Modal = forwardRef((props, ref) => {
+ const handleClickInnerModal = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ };
+
+ if (!props.isOpen) return null;
+
+ return (
+
+
+
+ {props.title && (
+
+ {props.title}
+
+ )}
+
+
+ {props.children}
+
+
+ );
+});
+
+Modal.displayName = 'Modal';
+
+const Backdrop = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(49, 48, 54, 0.5);
+ z-index: 10000;
+`;
+
+const ModalBox = styled(Flex)`
+ width: 337px;
+ height: fit-content;
+ padding: 18.5px 0;
+ position: relative;
+ background-color: ${theme.palette.White};
+ box-sizing: border-box;
+ border-radius: 8px;
+`;
+
+const ModalContent = styled.div`
+ display: flex;
+ flex-direction: column;
+ text-align: center;
+ justify-content: center;
+ align-items: center;
+`;
diff --git a/packages/ui/src/components/Modal/index.tsx b/packages/ui/src/components/Modal/index.tsx
new file mode 100644
index 00000000..8deb0a3d
--- /dev/null
+++ b/packages/ui/src/components/Modal/index.tsx
@@ -0,0 +1 @@
+export { Modal } from './Modal';
diff --git a/packages/ui/src/components/Navbar/DuriNavbar.tsx b/packages/ui/src/components/Navbar/DuriNavbar.tsx
index 0ed8807a..39099ca4 100644
--- a/packages/ui/src/components/Navbar/DuriNavbar.tsx
+++ b/packages/ui/src/components/Navbar/DuriNavbar.tsx
@@ -85,7 +85,7 @@ const Wrapper = styled(Flex)`
`;
const TabContainer = styled(Flex)`
- max-width: 480px;
+ max-width: 375px;
min-width: 360px;
position: fixed;
bottom: 0;
diff --git a/packages/ui/src/components/Navbar/SalonNavbar.tsx b/packages/ui/src/components/Navbar/SalonNavbar.tsx
index 46f547da..b49b7206 100644
--- a/packages/ui/src/components/Navbar/SalonNavbar.tsx
+++ b/packages/ui/src/components/Navbar/SalonNavbar.tsx
@@ -85,7 +85,7 @@ const Wrapper = styled(Flex)`
`;
const TabContainer = styled(Flex)`
- max-width: 480px;
+ max-width: 375px;
min-width: 360px;
position: fixed;
bottom: 0;
diff --git a/packages/ui/src/components/NavbarTemp/NavbarTemp.tsx b/packages/ui/src/components/NavbarTemp/NavbarTemp.tsx
index eaee8996..e9c2ea56 100644
--- a/packages/ui/src/components/NavbarTemp/NavbarTemp.tsx
+++ b/packages/ui/src/components/NavbarTemp/NavbarTemp.tsx
@@ -1,9 +1,9 @@
-import { theme } from "@duri-fe/ui";
-import styled from "@emotion/styled";
+import { theme } from '@duri-fe/ui';
+import styled from '@emotion/styled';
export const NavbarTemp = styled.div`
width: 100%;
- max-width: 480px;
+ max-width: 375px;
height: 50px;
background-color: ${theme.palette.Gray700};
@@ -11,4 +11,4 @@ export const NavbarTemp = styled.div`
bottom: 0;
left: 50%;
transform: translateX(-50%);
-`
\ No newline at end of file
+`;
diff --git a/packages/ui/src/components/index.tsx b/packages/ui/src/components/index.tsx
index 5384577c..c0f0c863 100644
--- a/packages/ui/src/components/index.tsx
+++ b/packages/ui/src/components/index.tsx
@@ -14,5 +14,6 @@ export * from './Tag';
export * from './Header';
export * from './Card';
export * from './Toast';
+export * from './Modal';
export * from './ProfileImage';
export * from './Quotation';
diff --git a/packages/ui/src/styles/typo.tsx b/packages/ui/src/styles/typo.tsx
index cb75e4d4..2b9219f3 100644
--- a/packages/ui/src/styles/typo.tsx
+++ b/packages/ui/src/styles/typo.tsx
@@ -21,6 +21,8 @@ export const calcRem = (px: number) => `${px / 16}rem`;
* @param Body2: 스케줄러 이름 텍스트
* @param Body3: 팝업 부가 설명 텍스트
* @param Body3: 스케줄러 담당쌤 텍스트 / 스케줄러 성별/특징 텍스트
+ *
+ * @param Letter: 피드백 텍스트
*/
export const typo = {
@@ -89,7 +91,6 @@ export const typo = {
`,
Caption3: css`
/* 박스 안에 들어가는 타이틀 밑 부가설명 텍스트 */
-
font-family: 'Pretendard';
font-size: ${calcRem(13)};
font-weight: 400;
@@ -165,4 +166,10 @@ export const typo = {
font-weight: 300;
line-height: normal;
`,
+ Letter: css`
+ font-family: 'OwnglyphRyryu';
+ font-size: ${calcRem(18)};
+ font-weight: 400;
+ line-height: normal;
+ `,
} as const;
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 929cff29..636e8f00 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -29,6 +29,7 @@
"eslint-plugin-simple-import-sort": "^12.1.1",
"msw": "^2.6.6",
"react": "^18.3.1",
+ "react-spring-bottom-sheet": "^3.4.1",
"react-toastify": "^10.0.6",
"vite": "^6.0.1",
"zustand": "^5.0.2"
diff --git a/packages/utils/src/apis/axiosConfig.ts b/packages/utils/src/apis/axiosConfig.ts
index 549bd2bf..0f9f68e0 100644
--- a/packages/utils/src/apis/axiosConfig.ts
+++ b/packages/utils/src/apis/axiosConfig.ts
@@ -51,16 +51,16 @@ export const publicInstance = axios.create({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
-})
+});
duriInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('authorization_user');
config.headers['authorization_user'] = token ? `Bearer ${token}` : '';
return config;
-})
+});
salonInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('authorization_shop');
config.headers['authorization_shop'] = token ? `Bearer ${token}` : '';
return config;
-})
+});
diff --git a/packages/utils/src/apis/duri/hooks/shop.ts b/packages/utils/src/apis/duri/hooks/shop.ts
index 39662070..ff86b1ca 100644
--- a/packages/utils/src/apis/duri/hooks/shop.ts
+++ b/packages/utils/src/apis/duri/hooks/shop.ts
@@ -1,5 +1,7 @@
import { useQuery } from '@tanstack/react-query';
+import { CenterInfoType } from '../../types';
+import { getNearByShopInfo } from '../shop';
import { a, b } from '../shop';
export const useA = () => {
@@ -8,9 +10,8 @@ export const useA = () => {
queryFn: () => a(),
staleTime: 1000 * 60 * 10,
});
-
return data;
-}; //얘네 삭제해줘잉
+};
export const useB = () => {
const { data } = useQuery({
@@ -19,4 +20,16 @@ export const useB = () => {
staleTime: 1000 * 60 * 10,
});
return data;
-}; //삭제삭제
+};
+
+export const useGetNearByShopInfo = (
+ centerInfo: CenterInfoType,
+ sortby: string,
+) => {
+ const { data, refetch, isPending } = useQuery({
+ queryKey: ['getNearByShopInfo', centerInfo],
+ queryFn: () => getNearByShopInfo(centerInfo, sortby),
+ enabled: !!centerInfo,
+ });
+ return { data, refetch, isPending };
+};
diff --git a/packages/utils/src/apis/duri/shop.ts b/packages/utils/src/apis/duri/shop.ts
index 5aea71d5..489bbcd6 100644
--- a/packages/utils/src/apis/duri/shop.ts
+++ b/packages/utils/src/apis/duri/shop.ts
@@ -1,7 +1,5 @@
-// import axios from 'axios';
-// import { HttpResponse } from 'msw';
-
-import { duriInstance } from '../axiosConfig';
+import { duriInstance, publicInstance } from '../axiosConfig';
+import { CenterInfoType, ShopInfoResponse } from '../types';
export async function a() {
try {
@@ -24,3 +22,13 @@ export async function b() {
console.error(error);
}
}
+
+export const getNearByShopInfo = async (
+ { lat, lon, radius }: CenterInfoType,
+ sortby: string,
+): Promise => {
+ const { data } = await publicInstance.get(`/shop/nearby/sort/${sortby}`, {
+ params: { lat, lon, radius },
+ });
+ return data.response;
+};
diff --git a/packages/utils/src/apis/types/index.ts b/packages/utils/src/apis/types/index.ts
index e3e74e63..0e007268 100644
--- a/packages/utils/src/apis/types/index.ts
+++ b/packages/utils/src/apis/types/index.ts
@@ -1,4 +1,5 @@
export * from './base';
export * from './auth';
+export * from './shop';
export * from './quotation';
-export * from './home';
\ No newline at end of file
+export * from './home';
diff --git a/packages/utils/src/apis/types/shop.ts b/packages/utils/src/apis/types/shop.ts
new file mode 100644
index 00000000..14d4fba6
--- /dev/null
+++ b/packages/utils/src/apis/types/shop.ts
@@ -0,0 +1,32 @@
+import { BaseResponse } from './base';
+
+export interface ShopTime {
+ hour: number;
+ minute: number;
+ second: number;
+ nano: number;
+}
+
+export interface ShopInfoType {
+ shopId: number;
+ shopName: string;
+ shopAddress: string;
+ shopLat: number;
+ shopLon: number;
+ shopPhone: string;
+ shopOpenTime: ShopTime;
+ shopCloseTime: ShopTime;
+ shopRating: number; // 별점
+ distance: number; // 거리
+ tags: string[]; // 태그 배열
+}
+
+export interface ShopInfoResponse extends BaseResponse {
+ response: ShopInfoType[];
+}
+
+export type CenterInfoType = {
+ lat: number;
+ lon: number;
+ radius: number;
+};
diff --git a/packages/utils/src/hooks/index.ts b/packages/utils/src/hooks/index.ts
index c6178651..79604240 100644
--- a/packages/utils/src/hooks/index.ts
+++ b/packages/utils/src/hooks/index.ts
@@ -1,2 +1,4 @@
export { useGeolocation } from './useGeolocation';
-export { useIsMobile } from './useIsMobile';
\ No newline at end of file
+export { useIsMobile } from './useIsMobile';
+export { useBottomSheet } from './useBottomSheet';
+export { useModal } from './useModal';
diff --git a/packages/utils/src/hooks/useBottomSheet.ts b/packages/utils/src/hooks/useBottomSheet.ts
new file mode 100644
index 00000000..7504c432
--- /dev/null
+++ b/packages/utils/src/hooks/useBottomSheet.ts
@@ -0,0 +1,55 @@
+import { useRef, useState } from 'react';
+import { BottomSheetRef } from 'react-spring-bottom-sheet';
+
+import { css } from '@emotion/react';
+
+interface UseBottomSheetProps {
+ maxHeight: number;
+ onDismiss?: () => void;
+}
+
+export const useBottomSheet = ({
+ maxHeight,
+ onDismiss,
+}: UseBottomSheetProps) => {
+ const [isOpenSheet, setIsOpenSheet] = useState(false);
+ const ref = useRef(null);
+
+ const handleDismiss = () => {
+ setIsOpenSheet(false);
+ if (onDismiss) onDismiss();
+ };
+
+ const openSheet = () => setIsOpenSheet(true);
+ const closeSheet = () => setIsOpenSheet(false);
+
+ const bottomSheetProps = {
+ open: isOpenSheet,
+ ref,
+ css: StyledBottomCss,
+ maxHeight,
+ snapPoints: ({ maxHeight }: { maxHeight: number }) => [maxHeight],
+ onDismiss: handleDismiss,
+ };
+
+ return { isOpenSheet, openSheet, closeSheet, bottomSheetProps };
+};
+
+const StyledBottomCss = css`
+ position: relative;
+
+ [data-rsbs-overlay],
+ [data-rsbs-root]::after {
+ border-radius: 16px 16px 0px 0px;
+ z-index: 20;
+ max-width: 375px;
+
+ @media (min-width: 480px) {
+ left: calc(50% - 187.5px);
+ }
+ }
+
+ [data-rsbs-backdrop] {
+ background-color: rgba(49, 48, 54, 0.5);
+ }
+`;
diff --git a/packages/utils/src/hooks/useModal.tsx b/packages/utils/src/hooks/useModal.tsx
new file mode 100644
index 00000000..784007ec
--- /dev/null
+++ b/packages/utils/src/hooks/useModal.tsx
@@ -0,0 +1,42 @@
+import { useEffect, useRef, useState } from 'react';
+
+export const useModal = () => {
+ const [isOpenModal, setIsOpenModal] = useState(false);
+ const modalRef = useRef(null);
+
+ useEffect(() => {
+ document.addEventListener('mousedown', handleOutsideClick);
+
+ return () => {
+ document.removeEventListener('mousedown', handleOutsideClick);
+ };
+ }, [isOpenModal]);
+
+ const toggleModal = () => {
+ setIsOpenModal((prevIsOpen) => {
+ if (!prevIsOpen) document.body.style.overflowY = 'hidden';
+ else document.body.style.overflowY = 'auto';
+ return !prevIsOpen;
+ });
+ };
+
+ const openModal = () => {
+ document.body.style.overflowY = 'hidden';
+ setIsOpenModal(true);
+ };
+
+ const closeModal = () => {
+ document.body.style.overflowY = 'auto';
+ setIsOpenModal(false);
+ };
+
+ const handleOutsideClick = (e: Event) => {
+ const current = modalRef.current;
+ if (isOpenModal && current && !current.contains(e.target as Node)) {
+ document.body.style.overflowY = 'auto';
+ setIsOpenModal(false);
+ }
+ };
+
+ return { isOpenModal, modalRef, toggleModal, closeModal, openModal };
+};
diff --git a/packages/utils/src/mocks/handler.ts b/packages/utils/src/mocks/handler.ts
index 4fa9f420..860f171d 100644
--- a/packages/utils/src/mocks/handler.ts
+++ b/packages/utils/src/mocks/handler.ts
@@ -1,11 +1,11 @@
import { http, HttpResponse } from 'msw';
export const handlers = [
- http.get('https://api.example.com/api/user', () => {
- return HttpResponse.json({
- id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d',
- firstName: 'John',
- lastName: 'Maverick',
- });
- }),
- ];
\ No newline at end of file
+ http.get('https://api.example.com/api/user', () => {
+ return HttpResponse.json({
+ id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d',
+ firstName: 'John',
+ lastName: 'Maverick',
+ });
+ }),
+];
diff --git a/yarn.lock b/yarn.lock
index 380062d0..574e16d4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -485,6 +485,7 @@ __metadata:
"@emotion/react": "npm:^11.13.5"
"@emotion/styled": "npm:^11.13.5"
"@tanstack/react-query": "npm:^5.62.0"
+ "@tanstack/react-query-devtools": "npm:^5.62.2"
"@tosspayments/tosspayments-sdk": "npm:^2.3.2"
"@types/navermaps": "npm:^3.7.8"
"@types/node": "npm:^22.10.1"
@@ -534,6 +535,7 @@ __metadata:
"@emotion/react": "npm:^11.13.5"
"@emotion/styled": "npm:^11.13.5"
"@tanstack/react-query": "npm:^5.62.1"
+ "@tanstack/react-query-devtools": "npm:^5.62.2"
"@types/node": "npm:^22.10.1"
"@types/react": "npm:^18"
"@types/react-dom": "npm:^18"
@@ -603,6 +605,7 @@ __metadata:
eslint-plugin-simple-import-sort: "npm:^12.1.1"
msw: "npm:^2.6.6"
react: "npm:^18.3.1"
+ react-spring-bottom-sheet: "npm:^3.4.1"
react-toastify: "npm:^10.0.6"
typescript: "npm:^5.7.2"
vite: "npm:^6.0.1"
@@ -2322,22 +2325,22 @@ __metadata:
languageName: node
linkType: hard
-"@tanstack/query-devtools@npm:5.59.20":
- version: 5.59.20
- resolution: "@tanstack/query-devtools@npm:5.59.20"
- checksum: 10c0/e90008af9c5754bacb19b78b54bbbb60b4ef4aeae8ff46349cbf8596f50a49b7a66db5b9c5fc4e5d9eba6d1fe4991f0178c1c4eacdc3bee5b8f4b48f50997174
+"@tanstack/query-devtools@npm:5.61.4":
+ version: 5.61.4
+ resolution: "@tanstack/query-devtools@npm:5.61.4"
+ checksum: 10c0/44886dbf92849d17729bf779a184a3fd304711d8ce8061c1deccc089a65d168ed8f0cf0da43b59a9a22a9afb7b25fba99de8af47fe44479ac3082d852dcaaf53
languageName: node
linkType: hard
-"@tanstack/react-query-devtools@npm:5.61.0":
- version: 5.61.0
- resolution: "@tanstack/react-query-devtools@npm:5.61.0"
+"@tanstack/react-query-devtools@npm:^5.62.2":
+ version: 5.62.2
+ resolution: "@tanstack/react-query-devtools@npm:5.62.2"
dependencies:
- "@tanstack/query-devtools": "npm:5.59.20"
+ "@tanstack/query-devtools": "npm:5.61.4"
peerDependencies:
- "@tanstack/react-query": ^5.61.0
+ "@tanstack/react-query": ^5.62.2
react: ^18 || ^19
- checksum: 10c0/be984f8d4095526dbcd249f018b16b1a69c6bdfef6dc2d2dfb80999889d397c6515fe9833cecffc9aaf5bbd81749c8ca0a03d2060b6a83833b54a88d840dc97b
+ checksum: 10c0/34a83eec53bfc14e1eac5a6dcc09e79e531a6246b476fa6737201ca42591118c936c25dfcd6d2e2d3881363a60928d35b9ca7d5b5b32b724f81c5f3e1f418abf
languageName: node
linkType: hard
@@ -4387,7 +4390,7 @@ __metadata:
"@storybook/react-vite": "npm:8.4.5"
"@storybook/test": "npm:8.4.5"
"@tanstack/react-query": "npm:^5.62.0"
- "@tanstack/react-query-devtools": "npm:5.61.0"
+ "@tanstack/react-query-devtools": "npm:^5.62.2"
"@tosspayments/tosspayments-sdk": "npm:2.3.2"
"@types/acorn": "npm:^6.0.4"
"@types/axios": "npm:0.14.4"