All files / src/entities/project/model slice.ts

59.25% Statements 16/27
46.66% Branches 7/15
75% Functions 3/4
61.53% Lines 16/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111                                            25x                     1x 1x     2x           25x                                 25x               25x         25x                 193x       193x         193x   193x         193x     193x     193x       193x    
import { createAction, createAsyncThunk, type UnknownAction } from '@reduxjs/toolkit';
import { projectService } from '@/entities/project/model/project.service';
import { createAsyncEntitySlice } from '@/shared/lib/store';
import type { Project } from '@/shared/types';
import {
	ENTITY_LOADING_STATUSES,
	NAMES,
	PROJECT_SLICE_ACTIONS,
} from '@/shared/constants';
 
// ───────────────────────────────────────────────
// Base async slice
// ───────────────────────────────────────────────
export const {
	reducer: baseReducer,
	adapter: projectsAdapter,
	thunks: {
		fetchAllThunk: fetchProjects,
		createOneThunk: createProject,
		updateOneThunk: updateProject,
		deleteOneThunk: deleteProject,
	},
} = createAsyncEntitySlice<
	Project,
	{ name: string },
	{ id: string; changes: Partial<Project> }
>({
	name: NAMES.PROJECTS,
	fetchAll: projectService.getProjects,
	createOne: projectService.createProject,
	updateOne: projectService.updateProject,
 
	deleteOne: async (id: string): Promise<string> => {
		await projectService.deleteWithRelations(id);
		return id;
	},
 
	sortComparer: (a, b) => b.updatedAt - a.updatedAt,
});
 
// ───────────────────────────────────────────────
// Clear all
// ───────────────────────────────────────────────
export const clearAllProjects = createAsyncThunk(
	PROJECT_SLICE_ACTIONS.PROJECTS_CLEAR_ALL,
	async (_, { dispatch }) => {
		await projectService.clearAll();
		await dispatch(fetchProjects());
	},
);
 
// ───────────────────────────────────────────────
// State
// ───────────────────────────────────────────────
type BaseProjectsState = ReturnType<typeof baseReducer>;
 
export interface ProjectsState extends BaseProjectsState {
	activeId: string | null;
}
 
export const initialState: ProjectsState = {
	...(baseReducer(undefined, { type: '@@INIT' }) as BaseProjectsState),
	activeId: null,
};
 
// ───────────────────────────────────────────────
// Actions
// ───────────────────────────────────────────────
export const setActiveProjectId = createAction<string | null>(
	PROJECT_SLICE_ACTIONS.PROJECT_SET_ACTIVE_ID,
);
 
// add an action for direct insert a project to entities
export const upsertProject = createAction<Project>(PROJECT_SLICE_ACTIONS.PROJECT_UPSET);
 
// ───────────────────────────────────────────────
// Reducer
// ───────────────────────────────────────────────
export function projectsReducer(
	state: ProjectsState = initialState,
	action: UnknownAction,
): ProjectsState {
	Iif (setActiveProjectId.match(action)) {
		return { ...state, activeId: action.payload };
	}
 
	Iif (upsertProject.match(action)) {
		const nextState = projectsAdapter.upsertOne(state, action.payload);
		return { ...nextState, activeId: state.activeId };
	}
 
	let next = baseReducer(state, action) as ProjectsState;
 
	Iif (deleteProject.fulfilled.match(action)) {
		const deletedId = action.payload as string;
		if (next.activeId === deletedId) next = { ...next, activeId: null };
	}
 
	Iif (clearAllProjects.pending.match(action)) {
		return { ...next, loading: ENTITY_LOADING_STATUSES.PENDING };
	}
	Iif (clearAllProjects.fulfilled.match(action)) {
		return { ...next, activeId: null, loading: ENTITY_LOADING_STATUSES.SUCCEEDED };
	}
	Iif (clearAllProjects.rejected.match(action)) {
		return { ...next, loading: ENTITY_LOADING_STATUSES.FAILED };
	}
 
	return next;
}