Skip to content

Commit

Permalink
Move project label state to projects slice
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanielrindlaub committed Jan 26, 2024
1 parent 0d36f0a commit ccc5db1
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 179 deletions.
2 changes: 1 addition & 1 deletion src/components/ErrorAlerts.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
} from '../features/upload/uploadSlice';
import getErrorContent from '../content/Errors';
import { selectManageUserErrors, dismissManageUsersError } from '../features/projects/usersSlice';
import { selectManageLabelsErrors, dismissManageLabelsError } from '../features/filters/filtersSlice';
import { selectManageLabelsErrors, dismissManageLabelsError } from '../features/projects/projectsSlice';

// TODO: add updateAutomationRules errors

Expand Down
5 changes: 2 additions & 3 deletions src/features/filters/LabelFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import Checkbox from '../../components/Checkbox.jsx';
import NoneFoundAlert from '../../components/NoneFoundAlert.jsx';
import { CheckboxLabel } from '../../components/CheckboxLabel.jsx';
import { CheckboxWrapper } from '../../components/CheckboxWrapper.jsx';
import { selectLabelsLoading, checkboxOnlyButtonClicked } from './filtersSlice.js';
import { checkboxOnlyButtonClicked } from './filtersSlice.js';

const LabelFilter = () => {
const availLabels = useSelector(selectAvailLabels);
const activeFilters = useSelector(selectActiveFilters);
const activeLabels = activeFilters.labels;
const labelsLoading = useSelector(selectLabelsLoading);
const dispatch = useDispatch();

const handleCheckboxChange = (e) => {
Expand All @@ -38,7 +37,7 @@ const LabelFilter = () => {
selectedCount={activeLabels ? activeLabels.length : availLabels.options.length}
expandedDefault={false}
>
{labelsLoading.noneFound && <NoneFoundAlert>no labels found</NoneFoundAlert>}
{availLabels.options.length === 0 && <NoneFoundAlert>no labels found</NoneFoundAlert>}
{availLabels.options.length > 0 &&
<>
<BulkSelectCheckbox
Expand Down
160 changes: 19 additions & 141 deletions src/features/filters/filtersSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
getProjectsFailure,
setSelectedProjAndView,
editDeploymentsSuccess,
createProjectLabelSuccess,
updateProjectLabelSuccess,
selectProjectsLoading,
selectProjectLabelsLoading,
} from '../projects/projectsSlice';
import {
normalizeFilters,
Expand All @@ -20,15 +23,7 @@ const initialState = {
availFilters: {
cameras: { ids: [] },
deployments: { ids: [] },
labels: {
options: [],
loadingState: {
isLoading: false,
operation: null,
errors: null,
noneFound: false,
},
}
labels: { options: [] }
},
activeFilters: {
cameras: null,
Expand Down Expand Up @@ -135,77 +130,10 @@ export const filtersSlice = createSlice({
[filterCat]
);
},

createProjectLabelStart: (state) => {
const ls = { isLoading: true, operation: 'creating', errors: null };
state.availFilters.labels.loadingState = ls;
},

createProjectLabelSuccess: (state, { payload }) => {
const ls = {
isLoading: false,
operation: null,
errors: null
};
state.availFilters.labels.loadingState = ls;

state.availFilters.labels.options = [
...state.availFilters.labels.options,
payload.label
]
},

createProjectLabelFailure: (state, { payload }) => {
const ls = { isLoading: false, operation: null, errors: payload, stateMsg: null };
state.availFilters.labels.loadingState = ls;
},

updateProjectLabelStart: (state) => {
const ls = { isLoading: true, operation: 'updating', errors: null };
state.availFilters.labels.loadingState = ls;
},

updateProjectLabelSuccess: (state, { payload }) => {
const ls = {
isLoading: false,
operation: null,
errors: null
};
state.availFilters.labels.loadingState = ls;

state.availFilters.labels.options = state.availFilters.labels.options.map((label) => {
if (label._id === payload.label._id) {
return payload.label;
} else {
return label;
}
});
},

updateLabelFailure: (state, { payload }) => {
const ls = { isLoading: false, operation: null, errors: payload, stateMsg: null };
state.availFilters.labels.loadingState = ls;
},

dismissManageLabelsError: (state, { payload }) => {
const index = payload;
state.availFilters.labels.loadingState.errors.splice(index, 1);
},
},

extraReducers: (builder) => {
builder
.addCase(getProjectsStart, (state, { payload }) => {
let loadingState = state.availFilters.labels.loadingState;
loadingState.isLoading = true;
loadingState.operation = 'fetching';
})
.addCase(getProjectsFailure, (state, { payload }) => {
let loadingState = state.availFilters.labels.loadingState;
loadingState.isLoading = false;
loadingState.operation = null;
loadingState.errors = payload;
})
.addCase(setSelectedProjAndView, (state, { payload }) => {
const { cameraConfigs, labels } = payload.project;
updateAvailDepFilters(state, cameraConfigs);
Expand All @@ -214,6 +142,20 @@ export const filtersSlice = createSlice({
// set all filters to new selected view? We're currently handling this
// by dispatching setActiveFilters from setSelectedProjAndViewMiddleware
})
.addCase(createProjectLabelSuccess, (state, { payload }) => {
const labels = [...state.availFilters.labels.options, payload.label];
updateAvailLabelFilters(state, labels);
})
.addCase(updateProjectLabelSuccess, (state, { payload }) => {
const labels = state.availFilters.labels.options.map((label) => {
if (label._id === payload.label._id) {
return payload.label;
} else {
return label;
}
});
updateAvailLabelFilters(state, labels);
})
.addCase(registerCameraSuccess, (state, { payload }) => {
const { cameraConfigs } = payload.project;
updateAvailDepFilters(state, cameraConfigs);
Expand Down Expand Up @@ -270,77 +212,14 @@ export const {
setActiveFilters,
bulkSelectToggled,
checkboxOnlyButtonClicked,

createProjectLabelStart,
createProjectLabelSuccess,
createProjectLabelFailure,

updateProjectLabelStart,
updateProjectLabelSuccess,
updateLabelFailure,
dismissManageLabelsError
} = filtersSlice.actions;

// TODO: maybe use createAsyncThunk for these?
// https://redux-toolkit.js.org/api/createAsyncThunk

export const updateProjectLabel = (payload) => {
return async (dispatch, getState) => {
try {
dispatch(updateProjectLabelStart());
const currentUser = await Auth.currentAuthenticatedUser();
const token = currentUser.getSignInUserSession().getIdToken().getJwtToken();
const projects = getState().projects.projects;
const selectedProj = projects.find((proj) => proj.selected);
const projId = selectedProj._id;

if (token && selectedProj) {
const res = await call({
projId,
request: 'updateProjectLabel',
input: payload
});
dispatch(updateProjectLabelSuccess({ projId, label: res.updateProjectLabel.label }));
}
} catch (err) {
console.log(`error attempting to update label: `, err);
dispatch(updateLabelFailure(err));
}
};
};

export const createProjectLabel = (payload) => {
return async (dispatch, getState) => {
try {
dispatch(createProjectLabelStart());
const currentUser = await Auth.currentAuthenticatedUser();
const token = currentUser.getSignInUserSession().getIdToken().getJwtToken();
const projects = getState().projects.projects;
const selectedProj = projects.find((proj) => proj.selected);
const projId = selectedProj._id;

if (token && selectedProj) {
const res = await call({
projId,
request: 'createProjectLabel',
input: payload
});
dispatch(createProjectLabelSuccess({ projId, label: res.createProjectLabel.label}));
}
} catch (err) {
console.log(`error attempting to create label: `, err);
dispatch(createProjectLabelFailure(err));
}
};
};

// Selectors
export const selectActiveFilters = state => state.filters.activeFilters;
export const selectAvailFilters = state => state.filters.availFilters;
export const selectAvailCameras = state => state.filters.availFilters.cameras;
export const selectAvailDeployments = state => state.filters.availFilters.deployments;
export const selectAvailLabels = state => state.filters.availFilters.labels;
export const selectLabelsLoading = state => state.filters.availFilters.labels.loadingState;
export const selectReviewed = state => state.filters.activeFilters.reviewed;
export const selectNotReviewed = state => state.filters.activeFilters.notReviewed;
export const selectCustomFilter = state => state.filters.activeFilters.custom;
Expand All @@ -353,12 +232,11 @@ export const selectDateCreatedFilter = state => ({
end: state.filters.activeFilters.createdEnd,
});
export const selectFiltersReady = createSelector(
[selectProjectsLoading, selectLabelsLoading],
[selectProjectsLoading, selectProjectLabelsLoading],
(projectsLoading, labelsLoading) => {
const dependencies = [projectsLoading, labelsLoading];
return !dependencies.some(d => d.isLoading || d.errors);
}
);
export const selectManageLabelsErrors = state => state.filters.availFilters.labels.loadingState.errors;

export default filtersSlice.reducer;
9 changes: 1 addition & 8 deletions src/features/filters/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,7 @@ const updateAvailCamFilters = (state, camConfigs) => {

const updateAvailLabelFilters = (state, labels) => {
state.availFilters.labels.options = labels;
const noneFound = (labels.length === 0);
state.availFilters.labels.loadingState = {
isLoading: false,
operation: null,
errors: null,
noneFound,
};
}
};

export {
normalizeFilters,
Expand Down
6 changes: 3 additions & 3 deletions src/features/projects/ManageLabelsModal/EditLabelForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux";
import { Formik } from 'formik';
import * as Yup from 'yup';

import { updateProjectLabel, selectAvailLabels } from "../../filters/filtersSlice.js";
import { updateProjectLabel } from "../projectsSlice.js";
import LabelPill from "../../../components/LabelPill";
import IconButton from '../../../components/IconButton.jsx';
import { Pencil1Icon } from '@radix-ui/react-icons';
Expand All @@ -14,7 +14,7 @@ import {
LabelActions,
} from './components';

const EditLabelForm = ({ _id, name, color }) => {
const EditLabelForm = ({ _id, name, color, source, labels }) => {
const dispatch = useDispatch();
const [ showForm, setShowForm ] = useState(false);

Expand All @@ -24,7 +24,7 @@ const EditLabelForm = ({ _id, name, color }) => {
setShowForm(false);
}, []);

const labelsNames = useSelector(selectAvailLabels).options.map(({ name }) => name.toLowerCase());
const labelsNames = labels.map(({ name }) => name.toLowerCase());
const schema = (initialName) => {
return Yup.object().shape({
name: Yup.string()
Expand Down
6 changes: 3 additions & 3 deletions src/features/projects/ManageLabelsModal/NewLabelForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux";
import { Formik } from 'formik';
import * as Yup from 'yup';

import { createProjectLabel, selectAvailLabels } from "../../filters/filtersSlice.js";
import { createProjectLabel } from "../projectsSlice.js";
import LabelPill from "../../../components/LabelPill";
import Button from "../../../components/Button";
import LabelForm from './LabelForm';
Expand All @@ -14,7 +14,7 @@ import {
} from './components';
import { getRandomColor } from "../../../app/utils.js";

const NewLabelForm = () => {
const NewLabelForm = ({ labels }) => {
const dispatch = useDispatch();
const [ showNewLabelForm, setShowNewLabelForm ] = useState(false);

Expand All @@ -25,7 +25,7 @@ const NewLabelForm = () => {
resetForm();
});

const labelsNames = useSelector(selectAvailLabels).options.map(({ name }) => name.toLowerCase());
const labelsNames = labels.map(({ name }) => name.toLowerCase());
const schema = useMemo(() => {
return Yup.object().shape({
name: Yup.string()
Expand Down
17 changes: 12 additions & 5 deletions src/features/projects/ManageLabelsModal/index.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useSelector } from "react-redux";
import { selectAvailLabels, selectLabelsLoading } from '../../filters/filtersSlice.js';
import { selectSelectedProject, selectProjectLabelsLoading } from '../projectsSlice.js';
import { SimpleSpinner, SpinnerOverlay } from '../../../components/Spinner';
import { LabelList } from './components';
import NewLabelForm from "./NewLabelForm";
import EditLabelForm from "./EditLabelForm";

const ManageLabelsModal = () => {
const labels = useSelector(selectAvailLabels).options;
const labels = useSelector(selectSelectedProject).labels;
const sortedLabels = [...labels].sort((labelA, labelB) => {
return labelA.name.toLowerCase() > labelB.name.toLowerCase() ? 1 : -1;
});
const { isLoading } = useSelector(selectLabelsLoading);
const { isLoading } = useSelector(selectProjectLabelsLoading);

return (
<>
Expand All @@ -21,10 +21,17 @@ const ManageLabelsModal = () => {
}
<LabelList>
{sortedLabels.map(({ _id, name, color, source }) => (
<EditLabelForm key={_id} _id={_id} name={name} color={color} source={source} />
<EditLabelForm
key={_id}
_id={_id}
name={name}
color={color}
source={source}
labels={sortedLabels}
/>
))}
</LabelList>
<NewLabelForm />
<NewLabelForm labels={sortedLabels} />
</>
)
}
Expand Down
Loading

0 comments on commit ccc5db1

Please sign in to comment.