Skip to content
20 changes: 18 additions & 2 deletions src/features/user/home/workspace/ui/WorkingStoreCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@ import { useWorkingStoreCardViewModel } from '@/features/user/home/workspace/hoo
import type { WorkingStoreItem } from '@/features/user/home/workspace/types/workingStore'
interface WorkingStoreCardProps {
store: WorkingStoreItem
onClick?: () => void
}

export function WorkingStoreCard({ store }: WorkingStoreCardProps) {
export function WorkingStoreCard({ store, onClick }: WorkingStoreCardProps) {
const { dueText, nextWorkDate } = useWorkingStoreCardViewModel(store)

return (
<div className="flex h-[72px] items-center px-6">
<div
role={onClick ? 'button' : undefined}
tabIndex={onClick ? 0 : undefined}
className={`flex h-[72px] items-center px-6${onClick ? ' cursor-pointer' : ''}`}
onClick={onClick}
onKeyDown={
onClick
? e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
onClick()
}
}
: undefined
}
>
<div className="h-12 w-12 overflow-hidden rounded-[70px] bg-bg-dark">
{store.thumbnailUrl ? (
<img
Expand Down
37 changes: 11 additions & 26 deletions src/features/user/home/workspace/ui/WorkingStoresList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useNavigate } from 'react-router-dom'
import { MoreButton } from '@/shared/ui/common/MoreButton'
import {
WorkingStoreCard,
Expand All @@ -7,52 +8,36 @@ import {

interface WorkingStoresListProps {
title?: string
sortLabelLeft?: string
sortLabelRight?: string
selectedSort?: 'left' | 'right'
stores: WorkingStoreItem[]
onMoreClick?: () => void
onJoinWorkspaceClick?: () => void
}

export function WorkingStoresList({
title = '근무중인 가게',
sortLabelLeft = '이름 순',
sortLabelRight = '근무 예정 순',
selectedSort = 'right',
stores,
onMoreClick,
onJoinWorkspaceClick,
}: WorkingStoresListProps) {
const navigate = useNavigate()
const { visibleStores } = useWorkingStoresListViewModel(stores)

return (
<section className="w-[358px] rounded-2xl bg-white py-6">
<div className="flex items-center justify-between px-6">
<h3 className="typography-headline01 text-text-100">{title}</h3>
<div className="flex items-center gap-1">
<p
className={`typography-body02-regular ${
selectedSort === 'left' ? 'text-text-90' : 'text-text-50'
}`}
>
{sortLabelLeft}
</p>
<p className="typography-body02-regular text-text-50">|</p>
<p
className={`typography-body02-semibold ${
selectedSort === 'right' ? 'text-text-90' : 'text-text-50'
}`}
>
{sortLabelRight}
</p>
</div>
</div>

<div className="mt-4">
{visibleStores.map((store, index) => (
<div key={store.workspaceId}>
<WorkingStoreCard store={store} />
<WorkingStoreCard
store={store}
onClick={() =>
navigate(`/user/workspace/${store.workspaceId}`, {
state: { businessName: store.businessName },
})
}
/>
{index < visibleStores.length - 1 ? (
<div className="mx-1 h-px bg-line-1" />
) : null}
Expand All @@ -61,7 +46,7 @@ export function WorkingStoresList({
</div>

<div className="mt-6 px-6">
<MoreButton onClick={onMoreClick} />
{stores.length >= 4 && <MoreButton onClick={onMoreClick} />}
{onJoinWorkspaceClick ? (
<button
type="button"
Expand Down
38 changes: 36 additions & 2 deletions src/pages/user/substitute-request/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import {
useLocation,
Expand Down Expand Up @@ -42,6 +42,7 @@ type SubstituteRequestLocationState = {
direction?: SubstituteDirectionTab
directionTab?: SubstituteDirectionTab
receivedDto?: ReceivedSubstituteRequestDto
workspaceId?: number
}

function parseDirectionTab(
Expand Down Expand Up @@ -129,6 +130,17 @@ export function SubstituteRequestPage() {
})

const handleOpenCreate = () => {
const preselected = locationState?.workspaceId
? workspaces.find(w => w.workspaceId === locationState.workspaceId)
: undefined
if (preselected) {
setCreateFlow({
workspaceId: preselected.workspaceId,
storeName: preselected.businessName,
session: Date.now(),
})
return
}
if (workspaces.length === 1) {
const only = workspaces[0]!
setCreateFlow({
Expand All @@ -141,6 +153,25 @@ export function SubstituteRequestPage() {
setStorePickerOpen(true)
}

const autoOpenedCreateRef = useRef(false)
useEffect(() => {
if (autoOpenedCreateRef.current) return
if (workspacesLoading || !locationState?.workspaceId) return
autoOpenedCreateRef.current = true
handleOpenCreate()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [workspacesLoading, locationState?.workspaceId])

const clearWorkspacePreselect = () => {
if (!locationState?.workspaceId) return
const rest = { ...locationState }
delete rest.workspaceId
navigate(location.pathname + location.search, {
replace: true,
state: rest,
})
}

const handleStoreConfirm = (workspaceId: number, storeName: string) => {
setStorePickerOpen(false)
setCreateFlow({
Expand Down Expand Up @@ -270,7 +301,10 @@ export function SubstituteRequestPage() {
{createFlow != null ? (
<SubstituteRequestModalFlow
key={createFlow.session}
onClose={() => setCreateFlow(null)}
onClose={() => {
setCreateFlow(null)
clearWorkspacePreselect()
}}
storeName={createFlow.storeName}
initialMonth={new Date()}
workspaceId={createFlow.workspaceId}
Expand Down
22 changes: 15 additions & 7 deletions src/pages/user/workspace-detail/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useParams, useLocation } from 'react-router-dom'
import { useParams, useLocation, useNavigate } from 'react-router-dom'
import { format, parseISO } from 'date-fns'
import { Navbar } from '@/shared/ui/common/Navbar'
import {
Expand All @@ -10,7 +10,9 @@ import {
useWorkspaceScheduleViewModel,
} from '@/features/user'
import { shouldShowInfiniteListLoadMore } from '@/shared/lib/listLoadMoreVisibility'
import { ROUTES } from '@/shared/constants/routes'
import { WorkerListItem } from '@/shared/ui/home/WorkerListItem'
import SubstituteIcon from '@/assets/icons/catppuccin_changelog.svg?react'
import { ConfirmModal } from '@/shared/ui/common/ConfirmModal'
import { useResignWorkspaceMutation } from '@/features/user/home/workspace/hooks/mutation/useResignWorkspaceMutation'
import CrownIcon from '@/assets/icons/home/crown-solid.svg'
Expand All @@ -24,6 +26,7 @@ function formatNextShift(isoDate: string | null | undefined) {
}

export function WorkspaceDetailPage() {
const navigate = useNavigate()
const { workspaceId } = useParams<{ workspaceId: string }>()
const { state } = useLocation()
const id = Number(workspaceId)
Expand Down Expand Up @@ -94,10 +97,7 @@ export function WorkspaceDetailPage() {
hasMoreManagers,
managersTotalCount
)
const showWorkersLoadMore = shouldShowInfiniteListLoadMore(
hasMoreWorkers,
workersTotalCount
)
const showWorkersLoadMore = hasMoreWorkers && workersTotalCount >= 11

return (
<>
Expand Down Expand Up @@ -144,7 +144,6 @@ export function WorkspaceDetailPage() {
role={manager.positionDescription || manager.positionType}
variant="manager"
imageUrl={manager.profileImageUrl}
onOptions={() => {}}
/>
))}
</div>
Expand Down Expand Up @@ -194,7 +193,16 @@ export function WorkspaceDetailPage() {
variant="worker"
nextWorkDate={formatNextShift(worker.nextShiftDateTime)}
imageUrl={worker.profileImageUrl}
onOptions={() => {}}
menuItems={[
{
icon: <SubstituteIcon width={20} height={20} />,
label: '대타 요청',
onClick: () =>
navigate(ROUTES.USER.SUBSTITUTE_REQUEST, {
state: { workspaceId: id },
}),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
]}
/>
))}
</div>
Expand Down
33 changes: 23 additions & 10 deletions src/shared/ui/home/WorkerListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useState } from 'react'
import { colors } from '@/shared/lib/tokens'
import { Avatar } from '@/shared/ui/common/Avatar'
import { ActionMenu, type ActionMenuItem } from '@/shared/ui/common/ActionMenu'

export type WorkerVariant = 'manager' | 'worker'

Expand All @@ -9,7 +11,7 @@ export interface WorkerListItemProps {
variant?: WorkerVariant
nextWorkDate?: string
imageUrl?: string | null
onOptions?: () => void
menuItems?: ActionMenuItem[]
}

export interface WorkerListItemData extends WorkerListItemProps {
Expand Down Expand Up @@ -39,8 +41,9 @@ export function WorkerListItem({
variant = 'worker',
nextWorkDate,
imageUrl,
onOptions,
menuItems,
}: WorkerListItemProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const badgeBg = variant === 'manager' ? 'bg-main-700' : 'bg-main-300'

return (
Expand Down Expand Up @@ -73,14 +76,24 @@ export function WorkerListItem({
</div>
</div>

<button
type="button"
onClick={onOptions}
className="shrink-0 rounded cursor-pointer"
aria-label="더보기"
>
<EllipsisIcon />
</button>
{menuItems && menuItems.length > 0 && (
<div className="relative">
<button
type="button"
onClick={() => setIsMenuOpen(true)}
className="shrink-0 rounded cursor-pointer"
aria-label="더보기"
>
<EllipsisIcon />
</button>
<ActionMenu
items={menuItems}
isOpen={isMenuOpen}
onClose={() => setIsMenuOpen(false)}
className="right-0 top-full mt-2"
/>
</div>
)}
</div>
)
}
Loading