-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add travel-schedule slice (#14)
* feat: Add type declarations related to travel schedule * fix: Remove index file in features * chore: Modify Lint and Format Github actions (#9) * chore: Update scripts in package.json * chore: Update lint-format.yml * chore: Update lint-format.yml * chore: Add uuid * feat: Add type declarations related to travel schedule * feat: Add actions and slices related to travel schedule * feat: Add `TravelScheduleProvider` and apply * comment: Remove console.log * test: Add test for travel schedule logic * chore: Create run-test.yml (#13) * chore: Create run-test.yml * chore: Update package.json * chore: Update lint-format.yml * chore: auto-fix linting and formatting issues --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix: Modify parameter types in DaySchedule methods * feat: Add `updateDestination()` action * chore: Update eslint.config.mjs --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
fa5c6c3
commit 3543669
Showing
11 changed files
with
367 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { | ||
createTravelScheduleStore, | ||
Destination, | ||
} from '@/features/travel-schedule'; | ||
|
||
describe('여행 관련 일정 로직 테스트', () => { | ||
test('초기 상태는 반드시 비어 있는 배열이어야 합니다', () => { | ||
const store = createTravelScheduleStore(); | ||
|
||
const schedules = store.getState().schedules; | ||
|
||
expect(schedules.length).toEqual(0); | ||
}); | ||
|
||
test('여행 일자를 추가하였을 때, 정상적으로 추가되어야 합니다', () => { | ||
const store = createTravelScheduleStore(); | ||
|
||
const { schedules, addDaySchedule } = store.getState(); | ||
addDaySchedule(); | ||
|
||
expect(store.getState().schedules.length).not.toEqual(schedules.length); | ||
}); | ||
|
||
test('목적지를 추가하였을 때, 정상적으로 추가되어야 합니다', () => { | ||
const store = createTravelScheduleStore(); | ||
|
||
const destination: Destination = { | ||
id: 'test-id', | ||
name: 'test-name', | ||
timeToDestination: -1, | ||
startDate: new Date(), | ||
endDate: new Date(), | ||
}; | ||
const { addDaySchedule, addDestination } = store.getState(); | ||
addDaySchedule(); | ||
addDestination({ | ||
day: 0, | ||
destination, | ||
}); | ||
|
||
const target = store | ||
.getState() | ||
.schedules[0].destinations.find(({ id }) => id === 'test-id'); | ||
expect(target).not.toBeUndefined(); | ||
}); | ||
|
||
test('목적지를 삭제하였을 때, 정상적으로 제거되어야 합니다', () => { | ||
const store = createTravelScheduleStore(); | ||
|
||
const destination: Destination = { | ||
id: 'test-id', | ||
name: 'test-name', | ||
timeToDestination: -1, | ||
startDate: new Date(), | ||
endDate: new Date(), | ||
}; | ||
const { addDaySchedule, addDestination, removeDestination } = | ||
store.getState(); | ||
addDaySchedule(); | ||
addDestination({ | ||
day: 0, | ||
destination, | ||
}); | ||
removeDestination({ day: 0, destination }); | ||
|
||
const target = store | ||
.getState() | ||
.schedules[0].destinations.find(({ id }) => id === 'test-id'); | ||
expect(target).toBeUndefined(); | ||
}); | ||
|
||
test('목적지를 수정했을 때, 정상적으로 변경되어야 합니다', () => { | ||
const store = createTravelScheduleStore(); | ||
|
||
const destination: Destination = { | ||
id: 'test-id', | ||
name: 'test-name', | ||
timeToDestination: -1, | ||
startDate: new Date(), | ||
endDate: new Date(), | ||
}; | ||
const { addDaySchedule, addDestination, updateDestination } = | ||
store.getState(); | ||
addDaySchedule(); | ||
addDestination({ | ||
day: 0, | ||
destination, | ||
}); | ||
updateDestination({ | ||
day: 0, | ||
target: destination, | ||
updateValue: { | ||
...destination, | ||
name: 'changed-name', | ||
}, | ||
}); | ||
|
||
const target = store | ||
.getState() | ||
.schedules[0].destinations.find(({ id }) => id === 'test-id'); | ||
expect(target).not.toEqual(destination); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './travel-schedule.model'; | ||
export * from './travel-schedule.slice'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
export interface Destination { | ||
id: string; | ||
name: string; | ||
timeToDestination: number; | ||
startDate: Date; | ||
endDate: Date; | ||
} | ||
|
||
export class DaySchedule { | ||
destinations: Destination[]; | ||
|
||
constructor({ destinations }: Pick<DaySchedule, 'destinations'>) { | ||
this.destinations = destinations; | ||
} | ||
|
||
addDestination({ destination }: { destination: Destination }) { | ||
this.destinations.push(destination); | ||
this.sortByStartTime(); | ||
} | ||
|
||
removeDestination({ destinationId }: { destinationId: Destination['id'] }) { | ||
this.destinations = this.destinations.filter( | ||
({ id }) => id !== destinationId, | ||
); | ||
} | ||
|
||
updateDestination({ | ||
destinationId, | ||
updateValue, | ||
}: { | ||
destinationId: Destination['id']; | ||
updateValue: Partial<Destination>; | ||
}) { | ||
this.destinations = this.destinations.map((destination) => | ||
destination.id === destinationId | ||
? { ...destination, ...updateValue } | ||
: destination, | ||
); | ||
this.sortByStartTime(); | ||
} | ||
|
||
sortByStartTime() { | ||
this.destinations.sort((lhs, rhs) => { | ||
const [lhsStartTime, rhsStartTime] = [ | ||
lhs.startDate.getTime(), | ||
rhs.startDate.getTime(), | ||
]; | ||
if (lhsStartTime < rhsStartTime) return -1; | ||
else if (lhsStartTime === rhsStartTime) return 0; | ||
return 1; | ||
}); | ||
} | ||
} | ||
|
||
export interface TravelSchedule { | ||
schedules: DaySchedule[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { createStore } from 'zustand'; | ||
|
||
import { | ||
DaySchedule, | ||
Destination, | ||
TravelSchedule, | ||
} from './travel-schedule.model'; | ||
|
||
export type TravelScheduleState = TravelSchedule; | ||
|
||
export interface TravelScheduleActions { | ||
addDaySchedule: () => void; | ||
|
||
addDestination: (params: { day: number; destination: Destination }) => void; | ||
|
||
removeDestination: (params: { | ||
day: number; | ||
destination: Destination; | ||
}) => void; | ||
|
||
updateDestination: (params: { | ||
day: number; | ||
target: Destination; | ||
updateValue: Destination; | ||
}) => void; | ||
|
||
reset: () => void; | ||
} | ||
|
||
export type TravelScheduleStore = TravelScheduleState & TravelScheduleActions; | ||
|
||
export const defaultInitState: TravelScheduleState = { | ||
schedules: [], | ||
}; | ||
|
||
type Setter = ( | ||
partial: | ||
| TravelScheduleStore | ||
| Partial<TravelScheduleStore> | ||
| (( | ||
state: TravelScheduleStore, | ||
) => TravelScheduleStore | Partial<TravelScheduleStore>), | ||
replace?: boolean | undefined, | ||
) => void; | ||
|
||
const addDaySchedule = ({ set }: { set: Setter }) => | ||
set((state) => ({ | ||
schedules: [...state.schedules, new DaySchedule({ destinations: [] })], | ||
})); | ||
|
||
const addDestination = ({ | ||
day, | ||
destination, | ||
set, | ||
}: { | ||
day: number; | ||
destination: Destination; | ||
set: Setter; | ||
}) => | ||
set((state) => { | ||
const result = [...state.schedules]; | ||
result[day].addDestination({ destination }); | ||
return { schedules: result }; | ||
}); | ||
|
||
const removeDestination = ({ | ||
day, | ||
destination, | ||
set, | ||
}: { | ||
day: number; | ||
destination: Destination; | ||
set: Setter; | ||
}) => | ||
set((state) => { | ||
const result = [...state.schedules]; | ||
result[day].removeDestination({ destinationId: destination.id }); | ||
return { schedules: result }; | ||
}); | ||
|
||
const updateDestination = ({ | ||
day, | ||
target, | ||
updateValue, | ||
set, | ||
}: { | ||
day: number; | ||
target: Destination; | ||
updateValue: Destination; | ||
set: Setter; | ||
}) => | ||
set((state) => { | ||
const result = [...state.schedules]; | ||
result[day].updateDestination({ | ||
destinationId: target.id, | ||
updateValue: updateValue, | ||
}); | ||
return { schedules: result }; | ||
}); | ||
|
||
export const createTravelScheduleStore = ( | ||
initState: TravelScheduleState = defaultInitState, | ||
) => { | ||
return createStore<TravelScheduleStore>()((set) => ({ | ||
...initState, | ||
addDaySchedule: () => addDaySchedule({ set }), | ||
addDestination: ({ day, destination }) => | ||
addDestination({ day, destination, set }), | ||
removeDestination: ({ day, destination }) => | ||
removeDestination({ day, destination, set }), | ||
updateDestination: ({ day, target, updateValue }) => | ||
updateDestination({ day, target, updateValue, set }), | ||
reset: () => set(defaultInitState), | ||
})); | ||
}; |
Oops, something went wrong.