Commit 4814f592 authored by Dominik Petrovic's avatar Dominik Petrovic

Add site config sagas

parent 01165843
import { action } from '~/core/util/helpers';
import { GET_SITE_CONFIG, SET_SITE_CONFIG } from './types';
export const getSiteConfig = () => action(GET_SITE_CONFIG);
export const setSiteConfig = () => action(SET_SITE_CONFIG);
import { selectVersionStatus } from '~/core/redux/selectors';
import { Op, Query } from 'contensis-delivery-api';
export function getSiteConfigQuery(state) {
const versionStatus = selectVersionStatus(state);
let expressions = [
...getDefaultExpressions(versionStatus),
...getContentTypeExpression(['siteSettings']),
];
const query = new Query(...expressions);
return query;
}
function getDefaultExpressions(versionStatus) {
return [
Op.equalTo('sys.versionStatus', versionStatus),
// Op.or(
// Op.and(
// Op.exists('sys.metadata.includeInSearch', true),
// Op.equalTo('sys.metadata.includeInSearch', true)
// ),
// Op.exists('sys.metadata.includeInSearch', false)
// ),
];
}
function getContentTypeExpression(contentTypeIds, dataFormats) {
if (
contentTypeIds &&
contentTypeIds.length > 0 &&
dataFormats &&
dataFormats.length > 0
) {
return [
Op.or(
Op.in('sys.contentTypeId', ...contentTypeIds),
Op.in('sys.dataFormat', ...dataFormats)
),
];
}
if (
contentTypeIds &&
contentTypeIds.length > 0 &&
(!dataFormats || (dataFormats && dataFormats.length == 0))
) {
return [Op.in('sys.contentTypeId', ...contentTypeIds)];
}
if (
dataFormats &&
dataFormats.length > 0 &&
(!contentTypeIds || (contentTypeIds && contentTypeIds.length == 0))
) {
return [Op.in('sys.dataFormat', ...dataFormats)];
}
return [Op.in('sys.contentTypeId', 'null')];
}
import { SET_SITE_CONFIG, SET_ERROR, GET_SITE_CONFIG } from './types';
import { Map } from 'immutable';
const defaultState = Map({
siteConfiguration: null,
loading: false,
error: '',
});
export const configReducer = (state = defaultState, action) => {
switch (action.type) {
case GET_SITE_CONFIG: {
return state.set('loading', true);
}
case SET_SITE_CONFIG: {
return state
.set(
'siteSettings',
action.payload.length > 0 ? action.payload[0] : null
)
.set('loading', false);
}
case SET_ERROR: {
return state.set('error', action.payload);
}
default:
return state;
}
};
/* eslint-disable no-console */
import { takeEvery, select, put } from 'redux-saga/effects';
import { deliveryApi } from '~/core/util/ContensisDeliveryApi';
import { GET_SITE_CONFIG, SET_ERROR, SET_SITE_CONFIG } from './types';
import { getSiteConfigQuery } from './expressions';
export const configSagas = [takeEvery(GET_SITE_CONFIG, getSiteConfigSaga)];
function constructSagaErrorMessage(name) {
return `Saga error: Unable to retrieve ${name}. The API call probably failed. Please check the console for possible clues`;
}
export function* getSiteConfigSaga() {
const state = yield select();
const query = getSiteConfigQuery(state);
query.pageSize = 1;
let payload;
try {
payload = yield deliveryApi.search(query, 2);
if (payload && payload.items) {
yield put({ type: SET_SITE_CONFIG, payload: payload.items });
} else {
yield put({
type: SET_ERROR,
payload: constructSagaErrorMessage('Site Config'),
});
}
} catch (ex) {
console.log(ex);
yield put({
type: SET_ERROR,
payload: ex.toString(),
});
}
}
import { safeGet } from '~/core/util/safeGet';
export const selectSiteConfig = state => {
return safeGet(state, ['config', 'siteSettings'], null);
};
export const selectSiteConfigLoading = state => {
return state.getIn(['config', 'loading'], false);
};
export const selectSiteConfigError = state => {
return state.getIn(['config', 'error'], '');
};
// Site configuration
export const selectSiteNavigation = state => {
return safeGet(
state,
['config', 'siteSettings', 'siteNavigation', 'links'],
null
);
};
export const selectFooterLinks = state => {
return safeGet(
state,
['config', 'siteSettings', 'footerLinks', 'links'],
null
);
};
export const selectCopyrightText = state => {
return safeGet(state, ['config', 'siteSettings', 'copyrightText'], null);
};
const ACTION_PREFIX = '@CONFIG/';
export const GET_SITE_CONFIG = `${ACTION_PREFIX}GET_SITE_CONFIG`;
export const SET_SITE_CONFIG = `${ACTION_PREFIX}SET_SITE_CONFIG`;
export const SET_ERROR = `${ACTION_PREFIX}SET_ERROR`;
......@@ -4,12 +4,14 @@ import UIReducers from './custom/ui/reducers';
import { config } from '~/features/search';
import userCustomReducer from '~/features/user/redux/reducers';
import { reducer as FormsReducer } from 'zengenti-forms-package';
import { configReducer } from '~/core/redux/custom/siteSettings/reducers';
const featureReducers = {
ui: UIReducers,
search: SearchReducer(config),
form: FormsReducer,
user_custom: userCustomReducer,
config: configReducer,
};
export default { ...featureReducers };
......@@ -3,12 +3,14 @@ import { sagas as searchSagas } from '@zengenti/contensis-react-base/search';
import { sagas as formSagas } from 'zengenti-forms-package';
import { UISagas } from './custom/ui/sagas';
import { userCustomSagas } from '~/features/user/redux/sagas';
import { configSagas } from '~/core/redux/custom/siteSettings/sagas';
const featureSagas = [
...UISagas,
...searchSagas,
...formSagas,
...userCustomSagas,
...configSagas,
];
export default featureSagas;
import { put, call } from 'redux-saga/effects';
import { put, call, select } from 'redux-saga/effects';
import { setRouteFilters } from '@zengenti/contensis-react-base/search';
import { queryParams, routeParams } from '../util/navigation';
// import { GET_SITE_CONFIG } from '~/core/redux/siteConfig/types';
import { ROUTE_HAS_LOADED, ROUTE_WILL_LOAD } from './types';
import transformations from '~/features/search/transformations';
import { selectSiteConfig } from './custom/siteSettings/selectors';
import { getSiteConfigSaga } from './custom/siteSettings/sagas';
export default {
onRouteLoad: function* onRouteLoad({
......@@ -16,10 +18,10 @@ export default {
// Set params for routing saga
return {
customNavigation: {
ancestors: false,
ancestors: true,
children: false,
siblings: false,
tree: true,
tree: 4,
},
};
},
......@@ -47,6 +49,9 @@ export default {
});
}
let siteConfig = yield select(selectSiteConfig);
if (!siteConfig) yield call(getSiteConfigSaga);
yield put({ type: ROUTE_HAS_LOADED, path, entry });
// const siteConfig = yield select(hasSiteConfig);
......
import React from 'react';
import PropTypes from 'prop-types';
//import debounce from '~/core/utils/debounce';
const FilterClear = ({ className, label, update }) => {
return (
<button className={className} onClick={() => update(-1, -1, true)}>
{label}
</button>
);
};
FilterClear.propTypes = {
className: PropTypes.string,
label: PropTypes.string,
update: PropTypes.func,
};
export default FilterClear;
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
const FilterDate = ({ className, id, label, startYear, update, value }) => {
const [date, updateDate] = useState(
value && {
from: value.from,
to: value.to,
}
);
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
if (!startYear) startYear = currentYear;
useEffect(() => {
if (!value) updateDate(null);
}, [value]);
useEffect(() => {
update(id, date, true);
}, [date]);
const parseWithPreceedingZero = val => {
val = parseInt(val);
return val && val < 10 ? `0${val}` : `${val}`;
};
const parseDateObj = dateArr => {
if (!dateArr) return null;
const year = dateArr[0];
const month = dateArr.length > 1 && dateArr[1];
const day = dateArr.length > 2 && dateArr[2];
if (month) dateArr[1] = parseWithPreceedingZero(month);
if (day) dateArr[2] = parseWithPreceedingZero(day);
const dateFrom = dateArr.join('-');
let dateTo = null;
let newDate = new Date(year, month || '01', day || '01');
if (day) {
newDate.setDate(newDate.getDate() + 1);
newDate = newDate.toISOString();
dateTo = newDate.split('T')[0];
} else if (month) {
newDate.setMonth(newDate.getMonth() + 1);
newDate = newDate.toISOString();
newDate = newDate.split('T')[0].split('-');
newDate.pop();
dateTo = newDate.join('-');
} else {
dateTo = `${parseInt(year) + 1}`;
}
return {
from: dateFrom,
to: dateTo,
};
};
const handleOnChange = dateString => {
let dateArr = dateString.split('-');
if (date && date.from.indexOf(dateString) > -1) {
if (dateArr.length === 1) {
dateArr = null;
} else {
dateArr.pop();
}
}
updateDate(parseDateObj(dateArr));
};
const years = [];
let y = startYear;
for (y; y <= currentYear; y++) {
years.push(y.toString());
}
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'November',
'December',
];
return (
<div className={className}>
<div className="fdLabel">{label}</div>
{years && (
<ul className="fdYears">
{years.map((year, idx) => {
const yearIsChecked =
date && date.from.split('-')[0] === year ? true : false;
return (
<li className="fdYearItem" key={idx}>
<input
className="fdYearInput"
id={year}
type="checkbox"
checked={yearIsChecked}
onChange={() => handleOnChange(year)}
/>
<label className="fdYearLabel" htmlFor={year}>
{year}
</label>
{yearIsChecked && (
<ul className="fdMonths">
{months.map((month, i) => {
let monthInt = parseWithPreceedingZero(i + 1);
const monthIsChecked =
date && date.from.split('-')[1] === monthInt
? true
: false;
const monthID = `${year}-${monthInt}`;
return (
<li className="fdMonthItem" key={i}>
<input
className="fdMonthInput"
id={monthID}
type="checkbox"
checked={monthIsChecked}
onChange={() => handleOnChange(monthID)}
/>
<label className="fdMonthLabel" htmlFor={monthID}>
{month}
</label>
</li>
);
})}
</ul>
)}
</li>
);
})}
</ul>
)}
</div>
);
};
FilterDate.propTypes = {
className: PropTypes.string,
entries: PropTypes.arrayOf(PropTypes.object),
id: PropTypes.string,
label: PropTypes.string,
startYear: PropTypes.string,
update: PropTypes.func,
value: PropTypes.object,
};
export default FilterDate;
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
const FilterDate = ({ className, id, label, update, value }) => {
const [date, updateDate] = useState(value || null);
useEffect(() => {
if (!value) updateDate(null);
}, [value]);
useEffect(() => {
update(id, date, true);
}, [date]);
const handleOnChange = dateString => {
updateDate(dateString);
};
return (
<div className={`${className} fdContainer`}>
<div className="fdLabel">{label}</div>
<input
className="fdInput"
type="date"
pattern="([12]\d{3}/(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01]))"
onChange={e => {
handleOnChange(e.target.value);
}}
/>
</div>
);
};
FilterDate.propTypes = {
className: PropTypes.string,
id: PropTypes.string,
label: PropTypes.string,
update: PropTypes.func,
value: PropTypes.string,
};
export default FilterDate;
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
const FilterDateTabs = ({ className, id, update, value }) => {
const [selectedTab, updateTabs] = useState(value || '');
useEffect(() => {
if (!value) updateTabs('');
else updateTabs(value);
}, [value]);
useEffect(() => {
update(id, selectedTab, true);
}, [selectedTab]);
return (
<div className={className}>
<button
className="fkInput"
id="thisMonth"
type="button"
onClick={e => updateTabs(e.target.value)}
value="thisMonth"
>
This month
</button>
<button
className="fkInput"
id="thisMonth"
type="button"
onClick={e => updateTabs(e.target.value)}
value="thisYear"
>
This year
</button>
</div>
);
};
FilterDateTabs.propTypes = {
className: PropTypes.string,
id: PropTypes.string,
update: PropTypes.func,
value: PropTypes.string,
};
export default FilterDateTabs;
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
//import debounce from '~/core/utils/debounce';
const FilterEntry = ({
className,
entries,
id,
label,
multiselect = false,
type = 'entry',
update,
value,
}) => {
const idField = type === 'entry' ? 'sys.id' : 'value';
const titleField = type === 'entry' ? 'entryTitle' : 'key';
const getObjectValue = (obj, path) =>
path.split('.').reduce((value, el) => value[el], obj);
const [selected, updateSelected] = useState(value || []);
useEffect(() => {
if (!value) updateSelected([]);
}, [value]);
useEffect(() => {
update(id, selected, true);
}, [selected]);
const handleOnChange = entryId => {
if (selected.indexOf(entryId) > -1) {
selected.splice(selected.indexOf(entryId), 1);
updateSelected([...selected]);
} else {
if (!multiselect) {
updateSelected([entryId]);
} else {
updateSelected([...selected, entryId]);
}
}
};
return (
<div className={className}>
<div className="feLabel">{label}</div>
{entries && (
<ul className="feList">
{type === 'entry' && (
<li className="feItem">
<input
className="feItemInput"
id={'all'}
type="checkbox"
checked={!selected || selected.length < 1}
onChange={() => handleOnChange(null)}
/>
<label className="feItemLabel" htmlFor={'all'}>
{'All'}
</label>
</li>
)}
{entries.map((entry, idx) => {
const entryValue = getObjectValue(entry, idField);
const entryTitle = entry[titleField];
return (
<li className="feItem" key={idx}>
<input
className="feItemInput"
id={entryValue}
type="checkbox"
checked={selected.includes(entryValue)}
onChange={() => handleOnChange(entryValue)}
/>
<label className="feItemLabel" htmlFor={entryValue}>
{entryTitle}
</label>
</li>