diff --git a/src/assets/svg/ToggleOffIcon.tsx b/src/assets/svg/ToggleOffIcon.tsx deleted file mode 100644 index 71232374..00000000 --- a/src/assets/svg/ToggleOffIcon.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import type { SVGProps } from 'react'; -const SvgToggleOffIcon = (props: SVGProps) => ( - - - - -); -export default SvgToggleOffIcon; diff --git a/src/assets/svg/ToggleOnIcon.tsx b/src/assets/svg/ToggleOnIcon.tsx deleted file mode 100644 index daab4825..00000000 --- a/src/assets/svg/ToggleOnIcon.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import type { SVGProps } from 'react'; -const SvgToggleOnIcon = (props: SVGProps) => ( - - - - -); -export default SvgToggleOnIcon; diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts index d2ed5fed..8758eb10 100644 --- a/src/assets/svg/index.ts +++ b/src/assets/svg/index.ts @@ -58,8 +58,6 @@ export { default as SettingIcon } from './SettingIcon'; export { default as SettingIconSelected } from './SettingIconSelected'; export { default as ShareIcon } from './ShareIcon'; export { default as SixDotsIcon } from './SixDotsIcon'; -export { default as ToggleOffIcon } from './ToggleOffIcon'; -export { default as ToggleOnIcon } from './ToggleOnIcon'; export { default as UncheckedIcon } from './UncheckedIcon'; export { default as UncheckedIcon2 } from './UncheckedIcon2'; export { default as UnlinkedIcon } from './UnlinkedIcon'; diff --git a/src/components/ShiftBadge/index.tsx b/src/components/ShiftBadge/index.tsx index 82de04d5..41f9a7eb 100644 --- a/src/components/ShiftBadge/index.tsx +++ b/src/components/ShiftBadge/index.tsx @@ -1,3 +1,4 @@ +import useUIConfig from '@hooks/useUIConfig'; import { Ref } from 'react'; import { twMerge } from 'tailwind-merge'; @@ -9,6 +10,10 @@ interface Props } function ShiftBadge({ shiftType, className, forwardRef, isOnlyRequest, ...props }: Props) { + const { + state: { shiftTypeColorStyle }, + } = useUIConfig(); + return (
{shiftType ? shiftType.shortName : '-'} diff --git a/src/components/Toggle/index.tsx b/src/components/Toggle/index.tsx new file mode 100644 index 00000000..0aaa8d46 --- /dev/null +++ b/src/components/Toggle/index.tsx @@ -0,0 +1,27 @@ +interface ToggleProps { + isOn: boolean; + setIsOn: (isOn: boolean) => void; +} + +const Toggle = ({ isOn, setIsOn }: ToggleProps) => { + const handleToggle = () => { + setIsOn(!isOn); + }; + + return ( +
+
+
+ ); +}; + +export default Toggle; diff --git a/src/hooks/shift/useEditShift/index.ts b/src/hooks/shift/useEditShift/index.ts index 7e04d10d..62ef82ce 100644 --- a/src/hooks/shift/useEditShift/index.ts +++ b/src/hooks/shift/useEditShift/index.ts @@ -107,6 +107,7 @@ const useEditShift = (activeEffect = false) => { if (data === null) return; if ( + !foldedLevels || !oldCurrentShiftTeamId || (oldCurrentShiftTeamId && oldCurrentShiftTeamId !== currentShiftTeam?.shiftTeamId) ) { diff --git a/src/hooks/useUIConfig/index.ts b/src/hooks/useUIConfig/index.ts new file mode 100644 index 00000000..9e5c4186 --- /dev/null +++ b/src/hooks/useUIConfig/index.ts @@ -0,0 +1,30 @@ +import { shallow } from 'zustand/shallow'; +import { useUIConfigStore } from './store'; + +const useUIConfig = () => { + const [separateWeekendColor, shiftTypeColorStyle, setState] = useUIConfigStore( + (state) => [state.separateWeekendColor, state.shiftTypeColorStyle, state.setState], + shallow + ); + + const handleChangeSeparateWeekendColor = (value: boolean) => { + setState('separateWeekendColor', value); + }; + + const handleShiftTypeColorStyle = (value: typeof shiftTypeColorStyle) => { + setState('shiftTypeColorStyle', value); + }; + + return { + state: { + separateWeekendColor, + shiftTypeColorStyle, + }, + actions: { + handleChangeSeparateWeekendColor, + handleShiftTypeColorStyle, + }, + }; +}; + +export default useUIConfig; diff --git a/src/hooks/useUIConfig/store.ts b/src/hooks/useUIConfig/store.ts new file mode 100644 index 00000000..dad3c2e8 --- /dev/null +++ b/src/hooks/useUIConfig/store.ts @@ -0,0 +1,43 @@ +import { create } from 'zustand'; +import { devtools, persist } from 'zustand/middleware'; +import { produce } from 'immer'; + +interface State { + separateWeekendColor: boolean; + shiftTypeColorStyle: 'background' | 'text'; +} + +interface Store extends State { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + setState: (key: keyof State, value: any) => void; + initState: () => void; +} + +const initialState: State = { + separateWeekendColor: false, + shiftTypeColorStyle: 'background', +}; + +export const useUIConfigStore = create()( + devtools( + persist( + (set, get) => ({ + ...initialState, + setState: (state, value) => + set( + produce(get(), (draft) => { + draft[state] = value as never; + }) + ), + initState: () => set(initialState), + }), + { + name: 'useUIConfigStore', + partialize: ({ separateWeekendColor, shiftTypeColorStyle }: Store) => ({ + separateWeekendColor, + shiftTypeColorStyle, + }), + } + ) + ) +); diff --git a/src/libs/api/shiftType.ts b/src/libs/api/shiftType.ts index 681ae265..996fa42c 100644 --- a/src/libs/api/shiftType.ts +++ b/src/libs/api/shiftType.ts @@ -5,7 +5,7 @@ const getShiftTypes = async (wardId: number) => export type CreateShiftTypeDTO = Pick< WardShiftType, - 'name' | 'shortName' | 'color' | 'startTime' | 'endTime' | 'isOff' | 'isDefault' + 'name' | 'shortName' | 'color' | 'startTime' | 'endTime' | 'isOff' | 'isDefault' | 'isCounted' >; const createShiftType = async (wardId: number, createShiftTypeDTO: CreateShiftTypeDTO) => (await axiosInstance.post(`/wards/${wardId}/shift-types`, createShiftTypeDTO)) diff --git a/src/main.tsx b/src/main.tsx index d19fa786..3e46bdd0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,7 +8,15 @@ import { Toaster } from 'react-hot-toast'; import App from 'App'; import './index.css'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + refetchOnMount: 'always', + staleTime: 1000 * 10, + }, + }, +}); const container = document.getElementById('root') as HTMLElement; const element = ( diff --git a/src/pages/MakeShiftPage/components/CountDutyByDay.tsx b/src/pages/MakeShiftPage/components/CountDutyByDay.tsx index 1cc6a2c8..52ab4f6c 100644 --- a/src/pages/MakeShiftPage/components/CountDutyByDay.tsx +++ b/src/pages/MakeShiftPage/components/CountDutyByDay.tsx @@ -1,49 +1,59 @@ import useEditShift from '@hooks/shift/useEditShift'; +import useUIConfig from '@hooks/useUIConfig'; function CountDutyByDay() { const { state: { focus, shift }, } = useEditShift(); + const { + state: { shiftTypeColorStyle }, + } = useUIConfig(); return ( shift && (
- {shift.wardShiftTypes.map((wardShiftType, index) => ( -
+ {shift.wardShiftTypes + .filter((x) => x.isCounted) + .map((wardShiftType, index) => (
+
x.isCounted).length - 1 && + 'rounded-bl-[1.25rem]' + } `} - style={{ - backgroundColor: wardShiftType.color, - color: 'white', - }} - > - {wardShiftType.shortName} -
-
- {shift.days.map((_date, i) => ( -

- { - shift.divisionShiftNurses - .flatMap((rows) => rows) - .filter((row) => row.wardShiftList[i] === wardShiftType.wardShiftTypeId) - .length - } -

- ))} + style={ + shiftTypeColorStyle === 'background' + ? { backgroundColor: wardShiftType.color, color: 'white' } + : { color: wardShiftType.color, backgroundColor: 'white' } + } + > + {wardShiftType.shortName} +
+
+ {shift.days.map((_date, i) => ( +

+ { + shift.divisionShiftNurses + .flatMap((rows) => rows) + .filter((row) => row.wardShiftList[i] === wardShiftType.wardShiftTypeId) + .length + } +

+ ))} +
-
- ))} + ))}
) ); diff --git a/src/pages/MakeShiftPage/components/Panel.tsx b/src/pages/MakeShiftPage/components/Panel.tsx index d9d117ef..98e190d1 100644 --- a/src/pages/MakeShiftPage/components/Panel.tsx +++ b/src/pages/MakeShiftPage/components/Panel.tsx @@ -20,7 +20,7 @@ function Panel() { open && 'absolute bottom-[1.25rem] right-[1.25rem] h-[300%] max-h-[50vh]' )} style={{ - width: `${(shift.wardShiftTypes.length + 1) * 2 + 1.25}rem`, + width: `${(shift.wardShiftTypes.filter((x) => x.isCounted).length + 1) * 2 + 1.25}rem`, }} >
diff --git a/src/pages/MakeShiftPage/components/ShiftCalendar.tsx b/src/pages/MakeShiftPage/components/ShiftCalendar.tsx index f3dabb0e..2223a749 100644 --- a/src/pages/MakeShiftPage/components/ShiftCalendar.tsx +++ b/src/pages/MakeShiftPage/components/ShiftCalendar.tsx @@ -9,6 +9,7 @@ import { event, sendEvent } from 'analytics'; import useEditShift from '@hooks/shift/useEditShift'; import useEditShiftTeam from '@hooks/ward/useEditShiftTeam'; import { DragDropContext, DropResult, Droppable, Draggable } from 'react-beautiful-dnd'; +import useUIConfig from '@hooks/useUIConfig'; export default function ShiftCalendar() { const { @@ -30,6 +31,9 @@ export default function ShiftCalendar() { state: { shiftTeams }, actions: { selectNurse, moveNurseOrder, editDivision }, } = useEditShiftTeam(); + const { + state: { separateWeekendColor, shiftTypeColorStyle }, + } = useUIConfig(); const focusedCellRef = useRef(null); const containerRef = useRef(null); @@ -113,6 +117,10 @@ export default function ShiftCalendar() { } }, [focus]); + useEffect(() => { + console.log(shift, foldedLevels, wardShiftTypeMap, currentShiftTeam); + }, [shift, foldedLevels, wardShiftTypeMap, currentShiftTeam]); + return shift && foldedLevels && wardShiftTypeMap && currentShiftTeam ? (
@@ -133,19 +141,23 @@ export default function ShiftCalendar() { {shift.days.map((item, j) => (

- {shift.wardShiftTypes.map((shiftType, index) => ( -
- {shiftType.shortName} -
- ))} + {shift.wardShiftTypes + .filter((x) => x.isCounted) + .map((shiftType, index) => ( +
+ {shiftType.shortName} +
+ ))}
x.name === '오프')?.color, - color: 'white', - }} + style={ + shiftTypeColorStyle === 'background' + ? { + backgroundColor: shift.wardShiftTypes.find((x) => x.name === '오프')?.color, + color: 'white', + } + : { + color: shift.wardShiftTypes.find((x) => x.name === '오프')?.color, + backgroundColor: 'white', + } + } > WO
@@ -315,7 +340,15 @@ export default function ShiftCalendar() {
{!readonly && showLayer.fault && fault && ( @@ -368,18 +401,20 @@ export default function ShiftCalendar() { className="relative flex shrink-0 items-center gap-2 px-[1.5625rem] text-center" key={rowIndex} > - {shift?.wardShiftTypes.map((wardShiftType, index) => ( -
- { - row.wardShiftList.filter( - (current) => current === wardShiftType.wardShiftTypeId - ).length - } -
- ))} + {shift?.wardShiftTypes + .filter((x) => x.isCounted) + .map((wardShiftType, index) => ( +
+ { + row.wardShiftList.filter( + (current) => current === wardShiftType.wardShiftTypeId + ).length + } +
+ ))}
{ row.wardShiftList.filter( @@ -417,7 +452,7 @@ export default function ShiftCalendar() { : level !== shift.divisionShiftNurses.length - 1 && !readonly && (
{ e.stopPropagation(); editDivision( diff --git a/src/pages/MakeShiftPage/components/Toolbar.tsx b/src/pages/MakeShiftPage/components/Toolbar.tsx index cca06b2e..57995da0 100644 --- a/src/pages/MakeShiftPage/components/Toolbar.tsx +++ b/src/pages/MakeShiftPage/components/Toolbar.tsx @@ -25,6 +25,7 @@ import SetConstraint from './editWard/SetConstraint'; import SetShiftType from './editWard/SetShiftType'; import Select from '@components/Select'; import { createPortal } from 'react-dom'; +import SetDesignTheme from './editWard/SetDesignTheme'; function Toolbar() { const { @@ -40,7 +41,9 @@ function Toolbar() { } = useEditShift(); const [openInfo, setOpenInfo] = useState(false); - const [currentSetup, setCurrentSetup] = useState<'constraint' | 'shiftType' | null>(null); + const [currentSetup, setCurrentSetup] = useState< + 'constraint' | 'shiftType' | 'designTheme' | null + >(null); return (
@@ -83,7 +86,7 @@ function Toolbar() { {currentSetup !== null && createPortal( sendEvent(event.move_edit_modal)}> -
+
제약 조건
- setCurrentSetup(null)} - />
근무 형태
+
setCurrentSetup('designTheme')} + > + 디자인 테마 +
+ setCurrentSetup(null)} + />
{currentSetup === 'constraint' && } {currentSetup === 'shiftType' && } + {currentSetup === 'designTheme' && }
, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/src/pages/MakeShiftPage/components/editWard/CreateShiftModal.tsx b/src/pages/MakeShiftPage/components/editWard/CreateShiftModal.tsx index 6d8a1223..d25687cc 100644 --- a/src/pages/MakeShiftPage/components/editWard/CreateShiftModal.tsx +++ b/src/pages/MakeShiftPage/components/editWard/CreateShiftModal.tsx @@ -23,6 +23,7 @@ function CreateShiftModal({ open, shiftType, close, onSubmit, onDelete }: Props) isOff: false, isDefault: false, shortName: '', + isCounted: true, }; const [writeShift, setWriteShift] = useState(initialValue); diff --git a/src/pages/MakeShiftPage/components/editWard/SetConstraint.tsx b/src/pages/MakeShiftPage/components/editWard/SetConstraint.tsx index 4a759e26..f7d38a51 100644 --- a/src/pages/MakeShiftPage/components/editWard/SetConstraint.tsx +++ b/src/pages/MakeShiftPage/components/editWard/SetConstraint.tsx @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { ArrowDownIcon, ToggleOffIcon, ToggleOnIcon } from '@assets/svg'; +import { ArrowDownIcon } from '@assets/svg'; +import Toggle from '@components/Toggle'; import useEditShift from '@hooks/shift/useEditShift'; import { event, sendEvent } from 'analytics'; import { match } from 'ts-pattern'; @@ -36,7 +37,7 @@ const SetConstraint = () => { return ( checkFaultOptions && wardConstraint && ( -
+
{Object.entries(checkFaultOptions).map(([key, { label, value, isActive }]) => (
{
)) .otherwise(() => null)} - {isActive ? ( -
{ +
+ { updateConstraint({ ...wardConstraint, - [key]: false, + [key]: isActive ? false : true, }); - sendEvent(event.inactive_constraint); + sendEvent(isActive ? event.inactive_constraint : event.active_constraint); }} - > - + /> + {isActive ? (

근무표 적용

-
- ) : ( -
{ - updateConstraint({ - ...wardConstraint, - [key]: true, - }); - sendEvent(event.active_constraint); - }} - > - + ) : (

근무표 미적용

-
- )} + )} +
))}
diff --git a/src/pages/MakeShiftPage/components/editWard/SetDesignTheme.tsx b/src/pages/MakeShiftPage/components/editWard/SetDesignTheme.tsx new file mode 100644 index 00000000..1b478e09 --- /dev/null +++ b/src/pages/MakeShiftPage/components/editWard/SetDesignTheme.tsx @@ -0,0 +1,57 @@ +import Toggle from '@components/Toggle'; +import useUIConfig from '@hooks/useUIConfig'; + +const SetDesignTheme = () => { + const { + state: { separateWeekendColor, shiftTypeColorStyle }, + actions: { handleChangeSeparateWeekendColor, handleShiftTypeColorStyle }, + } = useUIConfig(); + + return ( +
+
+

토/일 배경색 구분

+
+ { + handleChangeSeparateWeekendColor(separateWeekendColor ? false : true); + }} + /> + {separateWeekendColor ? ( +

근무표 적용

+ ) : ( +

근무표 미적용

+ )} +
+
+
+

토/일 배경색 구분

+
+
handleShiftTypeColorStyle('background')} + > + 배경 +
+
handleShiftTypeColorStyle('text')} + > + 글자 +
+
+
+
+ ); +}; + +export default SetDesignTheme; diff --git a/src/pages/MakeShiftPage/components/editWard/SetShiftType.tsx b/src/pages/MakeShiftPage/components/editWard/SetShiftType.tsx index 85753194..8343d1fa 100644 --- a/src/pages/MakeShiftPage/components/editWard/SetShiftType.tsx +++ b/src/pages/MakeShiftPage/components/editWard/SetShiftType.tsx @@ -29,13 +29,14 @@ function SetShiftType() { }; return ( -
+

근무 명

약자

근무 시간

색상

유형

+

카운트

수정

{ward?.wardShiftTypes.map((shiftType, index) => ( @@ -107,6 +108,19 @@ function SetShiftType() { {shiftType.isOff ? '휴가' : '근무'}
+
+
{ + editShiftType(shiftType.wardShiftTypeId, { + ...shiftType, + isCounted: !shiftType.isCounted, + }); + }} + > + {shiftType.isCounted ? 'On' : 'Off'} +
+
{
x.isCounted).length * 2.5 + 2.5}rem` + : '0', }} >