+ {storeName} +
+{title}
+ ++ 시급 {wageAmount}원 +
+ ++ {savedAgoLabel} +
+From 0c1c33a7b85b74635af3de7dab3d177a3787825b Mon Sep 17 00:00:00 2001 From: kimtaewoo <70637743+kim3360@users.noreply.github.com> Date: Tue, 21 Jul 2026 20:57:49 +0900 Subject: [PATCH 01/15] =?UTF-8?q?fix=20:=20=EC=95=8C=EB=B0=94=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=96=89=EC=A0=95=EB=8F=99=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/job-lookup-map/common/Albabox.tsx | 6 +++--- src/features/job-lookup-map/lib/postingToAlbaboxProps.ts | 2 +- src/features/job-lookup-map/types/posting.ts | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/features/job-lookup-map/common/Albabox.tsx b/src/features/job-lookup-map/common/Albabox.tsx index 70809a5c..8228977f 100644 --- a/src/features/job-lookup-map/common/Albabox.tsx +++ b/src/features/job-lookup-map/common/Albabox.tsx @@ -10,7 +10,7 @@ export type AlbaboxProps = { wageAmount: string timeRange: string workDays: string - distance: string + town: string postedAgo: string saved: boolean likeCount?: string @@ -24,7 +24,7 @@ export function Albabox({ wageAmount, timeRange, workDays, - distance, + town, postedAgo, saved, likeCount, @@ -52,7 +52,7 @@ export function Albabox({
{storeName}
+ 지역 정보를 불러오는 중… +
+ ) : isError ? ( ++ 지역 정보를 불러오지 못했습니다. +
+ ) : options.length === 0 ? ( ++ 선택 가능한 지역이 없습니다. +
+ ) : ( ++ 직접 입력 (min / max) +
+ ++ 정렬 옵션을 불러오는 중… +
+ ) : isError ? ( ++ 정렬 옵션을 불러오지 못했습니다. +
+ ) : ( ++ 공고를 불러오는 중… +
+ ) : isError ? ( ++ 공고를 불러오지 못했습니다. +
+ ) : displayedPostings.length === 0 ? ( ++ 조건에 맞는 공고가 없습니다. +
+ ) : null} {displayedPostings.map(posting => { const base = postingToAlbaboxProps(posting) const saved = bookmarkById[posting.id] ?? posting.scrapped From e47f8cfb656a6c801f189d9211c35a0e85027b41 Mon Sep 17 00:00:00 2001 From: kimtaewoo <70637743+kim3360@users.noreply.github.com> Date: Wed, 22 Jul 2026 02:35:13 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat=20:=20=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=95=9C=20=EC=95=8C=EB=B0=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/App.tsx | 5 ++ src/features/job-lookup-map/api/posting.ts | 65 ++++++++++++++++ .../hooks/useScrappedPostings.ts | 49 ++++++++++++ .../lib/postingToAlbaboxProps.ts | 22 +++++- src/features/job-lookup-map/types/posting.ts | 21 +++++ src/pages/my/components/MenuListItem.tsx | 6 +- src/pages/my/index.tsx | 7 ++ src/pages/my/scrapped/index.tsx | 78 +++++++++++++++++++ src/shared/constants/routes.ts | 1 + 9 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 src/features/job-lookup-map/hooks/useScrappedPostings.ts create mode 100644 src/pages/my/scrapped/index.tsx diff --git a/src/app/App.tsx b/src/app/App.tsx index 20665f50..1082327f 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -40,6 +40,7 @@ import { NicknameEditPage } from '@/pages/my/profile/nickname' import { PasswordEditPage } from '@/pages/my/profile/password' import { SocialAccountPage } from '@/pages/my/profile/social' import { WithdrawPage } from '@/pages/my/withdraw' +import { ScrappedPostingsPage } from '@/pages/my/scrapped' import { ErrorPageRoute } from '@/pages/error' import { MobileLayout } from '@/shared/ui/MobileLayout' import { MobileLayoutWithDocbar } from '@/shared/ui/MobileLayoutWithDocbar' @@ -133,6 +134,10 @@ export function App() { element={+ 스크랩한 알바를 불러오지 못했습니다. +
+ ) : favorites.length === 0 ? ( ++ 스크랩한 알바가 없습니다. +
+ ) : ( ++ {storeName} +
++ 시급 {wageAmount}원 +
+ ++ {savedAgoLabel} +
+지역 정보를 불러오는 중… @@ -211,9 +253,9 @@ function RegionSelectDrawerBody({ ) : (
- 정렬 옵션을 불러오는 중… -
- ) : isError ? ( -- 정렬 옵션을 불러오지 못했습니다. -
- ) : ( -
지역 정보를 불러오는 중…
@@ -318,8 +318,8 @@ export function RegionSelectDrawer({
공고 정보를 불러오는 중…
From c969a9b24d5932e9ca3a5c6b8b0927da565cd17c Mon Sep 17 00:00:00 2001
From: kimtaewoo <70637743+kim3360@users.noreply.github.com>
Date: Mon, 3 Aug 2026 18:51:30 +0900
Subject: [PATCH 12/15] =?UTF-8?q?fix=20:=20useAddFavoritePosting=20?=
=?UTF-8?q?=ED=9B=85=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../hooks/useAddFavoritePosting.ts | 21 -------------------
1 file changed, 21 deletions(-)
delete mode 100644 src/features/job-lookup-map/hooks/useAddFavoritePosting.ts
diff --git a/src/features/job-lookup-map/hooks/useAddFavoritePosting.ts b/src/features/job-lookup-map/hooks/useAddFavoritePosting.ts
deleted file mode 100644
index 8ba5a8c6..00000000
--- a/src/features/job-lookup-map/hooks/useAddFavoritePosting.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { useMutation, useQueryClient } from '@tanstack/react-query'
-import { addFavoritePosting } from '@/features/job-lookup-map/api/posting'
-
-export function useAddFavoritePosting() {
- const queryClient = useQueryClient()
-
- return useMutation({
- mutationFn: (postingId: number) => addFavoritePosting(postingId),
- onSuccess: (_, postingId) => {
- void queryClient.invalidateQueries({
- queryKey: ['jobLookupMap', 'favoritePostings'],
- })
- void queryClient.invalidateQueries({
- queryKey: ['jobLookupMap', 'postings'],
- })
- void queryClient.invalidateQueries({
- queryKey: ['postingDetail', postingId],
- })
- },
- })
-}
From 3c44e0adce0b73b2d2864d73104e4c441bb6b8a7 Mon Sep 17 00:00:00 2001
From: kimtaewoo <70637743+kim3360@users.noreply.github.com>
Date: Mon, 10 Aug 2026 18:20:04 +0900
Subject: [PATCH 13/15] =?UTF-8?q?fix=20:=20=EC=A7=80=EC=97=AD=EC=97=90?=
=?UTF-8?q?=EC=84=9C=20=EC=B0=BE=EA=B8=B0=20=ED=95=84=ED=84=B0=EB=A7=81=20?=
=?UTF-8?q?UI=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/assets/icons/job-lookup-map/Close.svg | 3 +
src/assets/icons/job-lookup-map/Filter.svg | 6 +
.../icons/job-lookup-map/MappinMuted.svg | 11 +
.../common/AlbaFindCategoryBar.tsx | 195 +++++---
.../common/AlbaFindFilteredEmptyState.tsx | 36 ++
.../common/FilterDrawerShell.tsx | 6 +-
.../common/RegionModeFilterDrawer.tsx | 449 ++++++++++++++++++
.../job-lookup-map/lib/postingFilters.ts | 28 +-
.../job-lookup-map/lib/regionOptions.ts | 21 +
src/pages/user/job-lookup-map/index.tsx | 35 +-
10 files changed, 713 insertions(+), 77 deletions(-)
create mode 100644 src/assets/icons/job-lookup-map/Close.svg
create mode 100644 src/assets/icons/job-lookup-map/Filter.svg
create mode 100644 src/assets/icons/job-lookup-map/MappinMuted.svg
create mode 100644 src/features/job-lookup-map/common/AlbaFindFilteredEmptyState.tsx
create mode 100644 src/features/job-lookup-map/common/RegionModeFilterDrawer.tsx
diff --git a/src/assets/icons/job-lookup-map/Close.svg b/src/assets/icons/job-lookup-map/Close.svg
new file mode 100644
index 00000000..b03b00c6
--- /dev/null
+++ b/src/assets/icons/job-lookup-map/Close.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/job-lookup-map/Filter.svg b/src/assets/icons/job-lookup-map/Filter.svg
new file mode 100644
index 00000000..8042bb5d
--- /dev/null
+++ b/src/assets/icons/job-lookup-map/Filter.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/icons/job-lookup-map/MappinMuted.svg b/src/assets/icons/job-lookup-map/MappinMuted.svg
new file mode 100644
index 00000000..5fc2955f
--- /dev/null
+++ b/src/assets/icons/job-lookup-map/MappinMuted.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/features/job-lookup-map/common/AlbaFindCategoryBar.tsx b/src/features/job-lookup-map/common/AlbaFindCategoryBar.tsx
index cedbc5e7..e5002e45 100644
--- a/src/features/job-lookup-map/common/AlbaFindCategoryBar.tsx
+++ b/src/features/job-lookup-map/common/AlbaFindCategoryBar.tsx
@@ -1,11 +1,14 @@
-import { useState } from 'react'
+import { forwardRef, useImperativeHandle, useState } from 'react'
import ChevrondownIcon from '@/assets/icons/job-lookup-map/Chevrondown.svg?react'
-import { RegionSelectDrawer } from '@/features/job-lookup-map/common/RegionSelectDrawer'
+import CloseIcon from '@/assets/icons/job-lookup-map/Close.svg?react'
+import FilterIcon from '@/assets/icons/job-lookup-map/Filter.svg?react'
+import { RegionModeFilterDrawer } from '@/features/job-lookup-map/common/RegionModeFilterDrawer'
import { SalarySelectDrawer } from '@/features/job-lookup-map/common/SalarySelectDrawer'
import { SortSelectDrawer } from '@/features/job-lookup-map/common/SortSelectDrawer'
import {
DEFAULT_SORT_VALUE,
EMPTY_SALARY_FILTER,
+ countActiveFilters,
formatSalaryChipLabel,
formatSortChipLabel,
isSalaryFilterApplied,
@@ -14,7 +17,8 @@ import {
} from '@/features/job-lookup-map/lib/postingFilters'
import {
EMPTY_REGION_SELECTION,
- formatRegionLabel,
+ formatRegionChipLabel,
+ hasRegionFilterApplied,
type RegionSelection,
} from '@/features/job-lookup-map/lib/regionOptions'
@@ -31,78 +35,97 @@ type AlbaFindCategoryBarProps = {
onSortChange?: (value: string) => void
salaryFilter?: SalaryFilterSelection
onSalaryChange?: (selection: SalaryFilterSelection) => void
+ regionFilterDrawerOpen?: boolean
+ onRegionFilterDrawerOpenChange?: (open: boolean) => void
}
-const FILTER_ITEMS: { id: AlbaFindFilterId; label: string }[] = [
- { id: 'sort', label: '지역 선택' },
- { id: 'distance', label: '최신순' },
- { id: 'salary', label: '전체' },
-]
+export type AlbaFindCategoryBarRef = {
+ openFilters: () => void
+}
const FALLBACK_SORT_OPTIONS = [
{ value: 'LATEST', description: '최신순' },
{ value: 'PAY_AMOUNT', description: '급여순' },
]
-function getFilterItems(
- regionSelection: RegionSelection,
- sortValue: string,
- sortOptions: { value: string; description: string }[],
- salaryFilter: SalaryFilterSelection
+export const AlbaFindCategoryBar = forwardRef<
+ AlbaFindCategoryBarRef,
+ AlbaFindCategoryBarProps
+>(function AlbaFindCategoryBar(
+ {
+ mode,
+ onModeChange,
+ regionSelection = EMPTY_REGION_SELECTION,
+ onRegionChange,
+ sortValue = DEFAULT_SORT_VALUE,
+ onSortChange,
+ salaryFilter = EMPTY_SALARY_FILTER,
+ onSalaryChange,
+ regionFilterDrawerOpen,
+ onRegionFilterDrawerOpenChange,
+ },
+ ref
) {
- return FILTER_ITEMS.map(item => {
- if (item.id === 'sort') {
- return { ...item, label: formatRegionLabel(regionSelection) }
- }
- if (item.id === 'distance') {
- return {
- ...item,
- label: formatSortChipLabel(sortValue, sortOptions),
- }
- }
- return { ...item, label: formatSalaryChipLabel(salaryFilter) }
- })
-}
-
-export function AlbaFindCategoryBar({
- mode,
- onModeChange,
- regionSelection = EMPTY_REGION_SELECTION,
- onRegionChange,
- sortValue = DEFAULT_SORT_VALUE,
- onSortChange,
- salaryFilter = EMPTY_SALARY_FILTER,
- onSalaryChange,
-}: AlbaFindCategoryBarProps) {
const resolvedSortOptions = FALLBACK_SORT_OPTIONS
- const [isRegionDrawerOpen, setIsRegionDrawerOpen] = useState(false)
+ const [internalRegionFilterDrawerOpen, setInternalRegionFilterDrawerOpen] =
+ useState(false)
const [isSortDrawerOpen, setIsSortDrawerOpen] = useState(false)
const [isSalaryDrawerOpen, setIsSalaryDrawerOpen] = useState(false)
- const filterItems = getFilterItems(
+ const isRegionFilterDrawerOpen =
+ regionFilterDrawerOpen ?? internalRegionFilterDrawerOpen
+ const setIsRegionFilterDrawerOpen =
+ onRegionFilterDrawerOpenChange ?? setInternalRegionFilterDrawerOpen
+
+ useImperativeHandle(
+ ref,
+ () => ({
+ openFilters: () => {
+ if (mode === 'region') {
+ setIsRegionFilterDrawerOpen(true)
+ return
+ }
+ if (isSalaryFilterApplied(salaryFilter)) {
+ setIsSalaryDrawerOpen(true)
+ return
+ }
+ setIsSortDrawerOpen(true)
+ },
+ }),
+ [mode, salaryFilter, setIsRegionFilterDrawerOpen]
+ )
+
+ const activeFilterCount = countActiveFilters({
+ mode,
regionSelection,
sortValue,
- resolvedSortOptions,
- salaryFilter
- ).filter(item => mode === 'region' || item.id !== 'sort')
- const regionLabel = formatRegionLabel(regionSelection)
- const hasRegionSelected = regionLabel !== '지역 선택'
+ salaryFilter,
+ })
+ const regionChipLabel = formatRegionChipLabel(regionSelection)
const hasSortSelected = isSortFilterApplied(sortValue)
const hasSalarySelected = isSalaryFilterApplied(salaryFilter)
- const handleFilterClick = (id: AlbaFindFilterId) => {
- if (id === 'sort') {
- setIsRegionDrawerOpen(true)
- return
- }
+ const nearbyFilterItems = [
+ {
+ id: 'distance' as const,
+ label: formatSortChipLabel(sortValue, resolvedSortOptions),
+ active: hasSortSelected,
+ },
+ {
+ id: 'salary' as const,
+ label: formatSalaryChipLabel(salaryFilter),
+ active: hasSalarySelected,
+ },
+ ]
+
+ const handleNearbyFilterClick = (id: AlbaFindFilterId) => {
if (id === 'distance') {
setIsSortDrawerOpen(true)
return
}
if (id === 'salary') {
setIsSalaryDrawerOpen(true)
- return
}
}
@@ -118,7 +141,7 @@ export function AlbaFindCategoryBar({
role="tab"
aria-selected={mode === 'nearby'}
onClick={() => {
- setIsRegionDrawerOpen(false)
+ setIsRegionFilterDrawerOpen(false)
onModeChange('nearby')
}}
className={`min-h-10 flex-1 rounded-lg typography-body01-semibold transition-colors ${
@@ -144,15 +167,39 @@ export function AlbaFindCategoryBar({
+ {title}
+
+ {description}
+
+ 지역 정보를 불러오는 중…
+
+ 지역 정보를 불러오지 못했습니다.
+
+ 최소 / 최대 시급
+
- 조건에 맞는 공고가 없습니다.
-
+ 조건에 맞는 공고가 없습니다.
+
+ 최소 / 최대 시급
+
- 최소 / 최대 시급
-
- 최소 / 최대 시급
- 지역
+
+ 정렬
+ 급여
+ 정렬
+ 급여
+ 정렬
- 급여
- 정렬
- 급여
-
- 지역 정보를 불러오는 중… -
- ) : isError ? ( -- 지역 정보를 불러오지 못했습니다. -
- ) : options.length === 0 ? ( -- 선택 가능한 지역이 없습니다. -
- ) : ( -- 직접 입력 (min / max) -
- -+ 최소 / 최대 시급 +
+