Commit 5fef85a5 authored by Oleksii Savran's avatar Oleksii Savran

Crop: List, details, create and update functionality

path fix
service and model update
parent 07aae561
......@@ -72,6 +72,10 @@
"secondaryEmail": "Secondary Email",
"webCooperator": "Web Cooperator"
},
"Crop": {
"id": "Crop ID",
"name": "Crop"
},
"Geography": {
"geography": "Geography",
"currentGeography": "Current Valid Geography",
......
import Cooperator from '@gringlobal/client/model/gringlobal/Cooperator';
/**
* Crop
*
* GRIN-Global CE API
*/
class Crop {
public createdBy: number;
public createdDate: Date;
public modifiedBy: number;
public modifiedDate: Date;
public ownedBy: Cooperator;
public ownedDate: Date;
public id: number;
public name: string;
public note: string;
}
export default Crop;
import Cooperator from '@gringlobal/client/model/gringlobal/Cooperator';
import Crop from '@gringlobal/client/model/gringlobal/Crop';
import RepositoryFile from '@gringlobal/client/model/repository/RepositoryFile';
/**
* CropAttach
*
* GRIN-Global CE API
*/
class CropAttach {
public createdBy: number;
public createdDate: Date;
public modifiedBy: number;
public modifiedDate: Date;
public ownedBy: Cooperator;
public ownedDate: Date;
public title: string;
public virtualPath: string;
public thumbnailVirtualPath: string;
public contentType: string;
public isWebVisible: string;
public sortOrder: number;
public note: string;
public repositoryFile: RepositoryFile;
public id: number;
public attachCooperator: Cooperator;
public attachDate: Date;
public attachDateCode: string;
public categoryCode: string;
public crop: Crop;
public description: string;
}
export default CropAttach;
import CropAttach from '@gringlobal/client/model/gringlobal/CropAttach';
import RepositoryFile from '@gringlobal/client/model/repository/RepositoryFile';
/**
* CropAttachmentRequest
*
* GRIN-Global CE API
*/
class CropAttachmentRequest {
public fileMetadata: RepositoryFile;
public attachMetadata: CropAttach;
}
export default CropAttachmentRequest;
import Cooperator from '@gringlobal/client/model/gringlobal/Cooperator';
import CropAttach from '@gringlobal/client/model/gringlobal/CropAttach';
/**
* CropDetails
*
* GRIN-Global CE API
*/
class CropDetails {
public createdBy: number;
public createdDate: Date;
public modifiedBy: number;
public modifiedDate: Date;
public ownedBy: Cooperator;
public ownedDate: Date;
public id: number;
public name: string;
public note: string;
public attachments: CropAttach[];
}
export default CropDetails;
import CooperatorFilter from '@gringlobal/client/model/gringlobal/CooperatorFilter';
import DateFilter from '@gringlobal/client/model/common/DateFilter';
import StringFilter from '@gringlobal/client/model/common/StringFilter';
/**
* CropFilter
*
* GRIN-Global CE API
*/
class CropFilter {
public NOT: CropFilter;
public NULL: string[];
public NOTNULL: string[];
public id: number[];
public createdBy: number[];
public createdDate: DateFilter;
public modifiedBy: number[];
public modifiedDate: DateFilter;
public ownedBy: CooperatorFilter;
public ownedDate: DateFilter;
public name: StringFilter;
}
export default CropFilter;
......@@ -61,6 +61,10 @@
"secondaryEmail": "Secondary Email",
"webCooperator": "Web Cooperator"
},
"Crop": {
"id": "Crop ID",
"name": "Crop"
},
"Geography": {
"geography": "Geography",
"currentGeography": "Current Valid Geography",
......
import * as UrlTemplate from 'url-template';
import * as QueryString from 'query-string';
import { AxiosInstance, AxiosRequestConfig } from 'axios';
import { FilteredPage, IPageRequest, Page } from '@gringlobal/client/model/page';
import Crop from '@gringlobal/client/model/gringlobal/Crop';
import CropFilter from '@gringlobal/client/model/gringlobal/CropFilter';
import TaxonomySpeciesFilter from '@gringlobal/client/model/gringlobal/TaxonomySpeciesFilter';
import CropDetails from '@gringlobal/client/model/gringlobal/CropDetails';
const URL_REMOVE_FILE = UrlTemplate.parse('/api/v1/crop/attach/{cropId}/{attachmentId}');
const URL_UPLOAD_FILE = UrlTemplate.parse('/api/v1/crop/attach/{cropId}');
const URL_LIST_CROP_SPECIES = UrlTemplate.parse('/api/v1/crop/species/{cropId}');
const URL_CROP_DETAILS = UrlTemplate.parse('/api/v1/crop/details/{id}');
const URL_GET = UrlTemplate.parse('/api/v1/crop/{id}');
const URL_DELETE_CROP = UrlTemplate.parse('/api/v1/crop/{id}');
const URL_UPDATE_CROP = '/api/v1/crop';
const URL_CREATE_CROP = '/api/v1/crop';
const URL_LIST_CROPS = '/api/v1/crop/list';
const URL_FILTER_CROPS = '/api/v1/crop/filter';
/**
* Crop service
*
* GRIN-Global CE API
*/
class CropService {
private _axios: AxiosInstance;
public constructor(axios: AxiosInstance) {
this._axios = axios;
}
/**
* cropDetails at /api/v1/crop/details/{id}
*
* @param id undefined
* @param xhrConfig additional xhr config
*/
public cropDetails = (id: number, xhrConfig?: AxiosRequestConfig): Promise<CropDetails> => {
const apiUrl = URL_CROP_DETAILS.expand({ id });
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as CropDetails);
};
/**
* removeFile at /api/v1/crop/attach/{cropId}/{attachmentId}
*
* @param attachmentId undefined
* @param cropId undefined
* @param xhrConfig additional xhr config
*/
public removeFile = (attachmentId: number, cropId: number, xhrConfig?: AxiosRequestConfig): Promise<any>=> {
const apiUrl = URL_REMOVE_FILE.expand({ attachmentId, cropId });
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as undefined); // todo: types
}
/**
* uploadFile at /api/v1/crop/attach/{cropId}
*
* @param data Request body
* @param cropId undefined
* @param xhrConfig additional xhr config
*/
public uploadFile = (data: object, cropId: number, xhrConfig?: AxiosRequestConfig): Promise<any>=> {
const apiUrl = URL_UPLOAD_FILE.expand({ cropId });
// console.log(any`Fetching from ${apiUrl}`);
const content = { data }; // todo: change to formData
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as undefined);
};
/**
* listCropSpecies at /api/v1/crop/species/{cropId}
*
* @param data Request body
* @param cropId undefined
* @param xhrConfig additional xhr config
*/
public listCropSpecies = (data: TaxonomySpeciesFilter, cropId: number, xhrConfig?: AxiosRequestConfig): Promise<any> => {
const apiUrl = URL_LIST_CROP_SPECIES.expand({ cropId });
// console.log(`Fetching from ${apiUrl}`);
const content = { data };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as undefined);
}
/**
* get_2 at /api/v1/crop/{id}
*
* @param id undefined
* @param xhrConfig additional xhr config
*/
public get = (id: number, xhrConfig?: AxiosRequestConfig): Promise<Crop>=> {
const apiUrl = URL_GET.expand({ id });
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Crop);
}
/**
* deleteCrop at /api/v1/crop/{id}
*
* @param id undefined
* @param xhrConfig additional xhr config
*/
public deleteCrop = (id: number, xhrConfig?: AxiosRequestConfig): Promise<Crop>=> {
const apiUrl = URL_DELETE_CROP.expand({ id });
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as Crop);
}
/**
* updateCrop at /api/v1/crop
*
* @param data Request body
* @param xhrConfig additional xhr config
*/
public updateCrop = (data: Crop, xhrConfig?: AxiosRequestConfig): Promise<Crop>=> {
const apiUrl = URL_UPDATE_CROP;
// console.log(`Fetching from ${apiUrl}`);
const content = { data };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'PUT',
...content,
}).then(({ data }) => data as Crop);
}
/**
* createCrop at /api/v1/crop
*
* @param data Request body
* @param xhrConfig additional xhr config
*/
public createCrop = (data: Crop, xhrConfig?: AxiosRequestConfig): Promise<Crop>=> {
const apiUrl = URL_CREATE_CROP;
// console.log(`Fetching from ${apiUrl}`);
const content = { data };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as Crop);
}
/**
* listCrops at /api/v1/crop/list
*
* @param data Request body
* @param page undefined
* @param xhrConfig additional xhr config
*/
public listCrops = (data: CropFilter, page?: IPageRequest, xhrConfig?: AxiosRequestConfig): Promise<Page<Crop>> => {
const qs = QueryString.stringify({
p: page.page || undefined,
l: page.size || undefined,
d: page.direction ? page.direction : undefined,
s: page.properties || undefined,
}, {});
const apiUrl = URL_LIST_CROPS + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { data };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as Page<Crop>);
}
/**
* filterCrops at /api/v1/crop/filter
*
* @param data Request body
* @param f undefined
* @param page undefined
* @param xhrConfig additional xhr config
*/
public filterCrops = (data: CropFilter, f?: string, page?: IPageRequest, xhrConfig?: AxiosRequestConfig): Promise<FilteredPage<Crop>> => {
const qs = QueryString.stringify({
f: f || undefined,
p: page.page || undefined,
l: page.size || undefined,
d: page.direction ? page.direction : undefined,
s: page.properties || undefined,
}, {});
const apiUrl = URL_FILTER_CROPS + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { data };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as FilteredPage<Crop>);
}
}
export default CropService;
......@@ -14,6 +14,7 @@ import SystemStatusService from '@gringlobal/client/service/SystemStatusService'
import RepositoryService from '@gringlobal/client/service/RepositoryService';
import GeographyService from '@gringlobal/client/service/GeographyService';
import OAuthManagementService from '@gringlobal/client/service/OAuthManagementService';
import CropService from '@gringlobal/client/service/CropService';
import axios from 'axios';
import { clearCookies } from '@gringlobal/client/utilities';
import { ApiError } from '@gringlobal/client/model/common';
......@@ -101,6 +102,7 @@ const ConfiguredRepositoryService = new RepositoryService(serviceAxios);
const ConfiguredMethodService = new MethodService(serviceAxios);
const ConfiguredGeographyService = new GeographyService(serviceAxios);
const ConfiguredOAuthManagementService = new OAuthManagementService(serviceAxios);
const ConfiguredCropService = new CropService(serviceAxios);
export {
ConfiguredCooperatorService as CooperatorService,
......@@ -119,4 +121,5 @@ export {
ConfiguredMethodService as MethodService,
ConfiguredGeographyService as GeographyService,
ConfiguredOAuthManagementService as OAuthManagementService,
ConfiguredCropService as CropService,
}
......@@ -94,6 +94,10 @@
"todo": {
"title": "TODO",
"description": "Suggest a new card!"
},
"crop": {
"title": "Crops",
"description": "Browse all crops"
}
}
}
......@@ -179,6 +183,18 @@
}
}
},
"crop": {
"public": {
"p": {
"browse": {
"title": "List of crops"
},
"details": {
"title": "Crop details"
}
}
}
},
"inventory": {
"public": {
"p": {
......
......@@ -15,6 +15,8 @@ import { requestPublicSagas } from 'request/action/public';
import { kpiAdminSagas } from 'kpi/action/admin';
import { siteAdminSagas } from 'site/action/admin';
import { repositoryAdminSagas } from 'repository/action/admin';
import { cropPublicSagas } from 'crop/action/public';
import { cropAdminSagas } from 'crop/action/admin';
import { AxiosRequestConfig } from 'axios';
......@@ -29,11 +31,13 @@ export default function*() {
...inventoryPublicSagas,
...inventoryGroupPublicSagas,
...requestPublicSagas,
...cropPublicSagas,
...siteAdminSagas,
...kpiAdminSagas,
...repositoryAdminSagas,
...userAdminSagas,
...cropAdminSagas,
// if action has method
takeEvery((action) => action.type === 'API' && !! action.method, appendAxiosConfig),
......
......@@ -16,6 +16,7 @@ import request from 'request/reducer';
import site from 'site/reducer';
import kpi from 'kpi/reducer';
import repository from 'repository/reducer';
import crop from 'crop/reducer';
const rootReducer = (history?) => (combineReducers({
// express reducers
......@@ -34,6 +35,7 @@ const rootReducer = (history?) => (combineReducers({
site,
kpi,
repository,
crop,
...coreReducers(history),
}));
......
import { put, takeEvery, call, take } from 'redux-saga/effects';
// Constants
import {
RECEIVE_CROP,
ADMIN_RECEIVE_CROP,
SAGA_ADMIN_CREATE_CROP,
SAGA_ADMIN_RECEIVE_CROP,
SAGA_ADMIN_EDIT_CROP,
} from 'crop/constants';
// Model
import Crop from '@gringlobal/client/model/gringlobal/Crop';
// Service
import { CropService } from '@gringlobal/client/service';
import { sagaNavigate } from '@gringlobal/client/action/navigation';
export const cropAdminSagas = [
takeEvery(SAGA_ADMIN_CREATE_CROP, createCropSaga),
takeEvery(SAGA_ADMIN_EDIT_CROP, editCropSaga),
takeEvery(SAGA_ADMIN_RECEIVE_CROP, getCropSaga),
];
export const createCropAction = (crop: Crop) => ({
type: SAGA_ADMIN_CREATE_CROP,
payload: { crop },
});
export const editCropAction = (crop: Crop) => ({
type: SAGA_ADMIN_EDIT_CROP,
payload: { crop },
});
export const getCropActionAdmin = (id: string | number) => ({
type: SAGA_ADMIN_RECEIVE_CROP,
payload: { id },
});
function* createCropSaga(action) {
yield put({
type: 'API',
target: RECEIVE_CROP,
method: CropService.createCrop,
params: [action.payload.crop],
onSuccess: (crop: Crop) => {
return crop;
},
});
yield take(RECEIVE_CROP);
const received = yield take(RECEIVE_CROP);
if (received.payload.apiCall.data) {
console.log('navigate after creating');
yield call(sagaNavigate, `/crop/${received.payload.apiCall.data.id}`);
yield put({ type: ADMIN_RECEIVE_CROP, payload: received.payload })
}
}
function* editCropSaga(action) {
yield put({
type: 'API',
target: RECEIVE_CROP,
method: CropService.updateCrop,
params: [action.payload.crop],
onSuccess: (crop: Crop) => {
return crop;
},
});
yield take(RECEIVE_CROP);
const received = yield take(RECEIVE_CROP);
if (received.payload.apiCall.data) {
console.log('navigate after creating');
yield call(sagaNavigate, `/crop/${received.payload.apiCall.data.id}`);
yield put({ type: ADMIN_RECEIVE_CROP, payload: received.payload })
}
}
function* getCropSaga(action) {
yield put({
type: 'API',
target: ADMIN_RECEIVE_CROP,
method: CropService.get,
params: [action.payload.id],
onSuccess: (crop: Crop) => {
return crop;
},
});
}
import { put, takeEvery } from 'redux-saga/effects';
// Constants
import {
RECEIVE_CROPS,
RECEIVE_CROP,
SAGA_RECEIVE_CROPS,
SAGA_RECEIVE_CROP,
} from 'crop/constants';
// Model
import Crop from '@gringlobal/client/model/gringlobal/Crop';
import { IPageRequest, FilteredPage, Page } from '@gringlobal/client/model/page';
// Service
import { CropService } from '@gringlobal/client/service';
import { dereferenceReferences3 } from '@gringlobal/client/utilities';
import CropFilter from '@gringlobal/client/model/gringlobal/CropFilter';
export const cropPublicSagas = [
takeEvery(SAGA_RECEIVE_CROPS, listCropsSaga),
takeEvery(SAGA_RECEIVE_CROP, receiveCropSaga),
];
export const listCropsAction = (filter: Partial<CropFilter> = {}, pageR: IPageRequest = { page: 0, size: 100 }) => ({