2019-02-05 19:30:31 -08:00
|
|
|
import { pick } from 'lodash';
|
2018-10-30 09:35:47 -07:00
|
|
|
import { store } from 'react-easy-state';
|
|
|
|
import axios, { AxiosError } from 'axios';
|
2019-02-05 19:30:31 -08:00
|
|
|
import {
|
|
|
|
User,
|
|
|
|
Proposal,
|
2019-02-06 09:47:47 -08:00
|
|
|
Contribution,
|
|
|
|
ContributionArgs,
|
2019-02-05 19:30:31 -08:00
|
|
|
RFP,
|
|
|
|
RFPArgs,
|
|
|
|
EmailExample,
|
|
|
|
PageQuery,
|
2019-02-06 09:47:47 -08:00
|
|
|
PageData,
|
2019-02-05 19:30:31 -08:00
|
|
|
} from './types';
|
2018-10-30 09:35:47 -07:00
|
|
|
|
|
|
|
// API
|
|
|
|
const api = axios.create({
|
|
|
|
baseURL: process.env.BACKEND_URL + '/api/v1',
|
|
|
|
withCredentials: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
async function login(username: string, password: string) {
|
|
|
|
const { data } = await api.post('/admin/login', {
|
|
|
|
username,
|
|
|
|
password,
|
|
|
|
});
|
|
|
|
return data.isLoggedIn;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function logout() {
|
|
|
|
const { data } = await api.get('/admin/logout');
|
|
|
|
return data.isLoggedIn;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function checkLogin() {
|
|
|
|
const { data } = await api.get('/admin/checklogin');
|
|
|
|
return data.isLoggedIn;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchStats() {
|
|
|
|
const { data } = await api.get('/admin/stats');
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-14 20:18:43 -08:00
|
|
|
async function fetchUsers(params: Partial<PageQuery>) {
|
|
|
|
const { data } = await api.get('/admin/users', { params });
|
2018-10-30 09:35:47 -07:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-01-16 21:01:29 -08:00
|
|
|
async function fetchUserDetail(id: number) {
|
|
|
|
const { data } = await api.get(`/admin/users/${id}`);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-14 20:18:43 -08:00
|
|
|
async function editUser(id: number, args: Partial<User>) {
|
|
|
|
const { data } = await api.put(`/admin/users/${id}`, args);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-04 13:18:50 -08:00
|
|
|
async function deleteUser(id: number) {
|
2018-10-30 09:35:47 -07:00
|
|
|
const { data } = await api.delete('/admin/users/' + id);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-06 10:38:07 -08:00
|
|
|
async function fetchArbiters(search: string) {
|
|
|
|
const { data } = await api.get(`/admin/arbiters`, { params: { search } });
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function setArbiter(proposalId: number, userId: number) {
|
|
|
|
const { data } = await api.put(`/admin/arbiters`, { proposalId, userId });
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-05 12:34:19 -08:00
|
|
|
async function fetchProposals(params: Partial<PageQuery>) {
|
2019-02-06 09:47:47 -08:00
|
|
|
const { data } = await api.get('/admin/proposals', { params });
|
2019-01-09 10:23:08 -08:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchProposalDetail(id: number) {
|
|
|
|
const { data } = await api.get(`/admin/proposals/${id}`);
|
2018-10-30 09:35:47 -07:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-01-29 15:50:27 -08:00
|
|
|
async function updateProposal(p: Partial<Proposal>) {
|
|
|
|
const { data } = await api.put('/admin/proposals/' + p.proposalId, p);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2018-11-09 10:48:55 -08:00
|
|
|
async function deleteProposal(id: number) {
|
2018-10-30 09:35:47 -07:00
|
|
|
const { data } = await api.delete('/admin/proposals/' + id);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-01-09 10:23:08 -08:00
|
|
|
async function approveProposal(id: number, isApprove: boolean, rejectReason?: string) {
|
|
|
|
const { data } = await api.put(`/admin/proposals/${id}/approve`, {
|
|
|
|
isApprove,
|
|
|
|
rejectReason,
|
|
|
|
});
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-17 11:15:40 -08:00
|
|
|
async function fetchComments(params: Partial<PageQuery>) {
|
|
|
|
const { data } = await api.get('/admin/comments', { params });
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-02-13 08:54:46 -08:00
|
|
|
async function markMilestonePaid(proposalId: number, milestoneId: number, txId: string) {
|
|
|
|
const { data } = await api.put(
|
|
|
|
`/admin/proposals/${proposalId}/milestone/${milestoneId}/paid`,
|
|
|
|
{ txId },
|
|
|
|
);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-01-09 11:08:25 -08:00
|
|
|
async function getEmailExample(type: string) {
|
|
|
|
const { data } = await api.get(`/admin/email/example/${type}`);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-01-30 09:59:15 -08:00
|
|
|
async function getRFPs() {
|
|
|
|
const { data } = await api.get(`/admin/rfps`);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createRFP(args: RFPArgs) {
|
|
|
|
const { data } = await api.post('/admin/rfps', args);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function editRFP(id: number, args: RFPArgs) {
|
|
|
|
const { data } = await api.put(`/admin/rfps/${id}`, args);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function deleteRFP(id: number) {
|
|
|
|
await api.delete(`/admin/rfps/${id}`);
|
|
|
|
}
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
async function getContributions(params: PageQuery) {
|
|
|
|
const { data } = await api.get('/admin/contributions', { params });
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getContribution(id: number) {
|
|
|
|
const { data } = await api.get(`/admin/contributions/${id}`);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createContribution(args: ContributionArgs) {
|
|
|
|
const { data } = await api.post('/admin/contributions', args);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function editContribution(id: number, args: ContributionArgs) {
|
|
|
|
const { data } = await api.put(`/admin/contributions/${id}`, args);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2018-10-30 09:35:47 -07:00
|
|
|
// STORE
|
|
|
|
const app = store({
|
2019-02-06 09:47:47 -08:00
|
|
|
/*** DATA ***/
|
2019-02-07 09:51:16 -08:00
|
|
|
|
2018-10-30 09:35:47 -07:00
|
|
|
hasCheckedLogin: false,
|
|
|
|
isLoggedIn: false,
|
|
|
|
loginError: '',
|
|
|
|
generalError: [] as string[],
|
2019-01-09 10:23:08 -08:00
|
|
|
statsFetched: false,
|
|
|
|
statsFetching: false,
|
2018-10-30 09:35:47 -07:00
|
|
|
stats: {
|
2019-01-09 10:23:08 -08:00
|
|
|
userCount: 0,
|
|
|
|
proposalCount: 0,
|
|
|
|
proposalPendingCount: 0,
|
2019-02-05 12:45:26 -08:00
|
|
|
proposalNoArbiterCount: 0,
|
2019-02-13 08:54:46 -08:00
|
|
|
proposalMilestonePayoutsCount: 0,
|
2018-10-30 09:35:47 -07:00
|
|
|
},
|
2019-01-16 21:01:29 -08:00
|
|
|
|
2019-02-14 20:18:43 -08:00
|
|
|
users: {
|
|
|
|
page: createDefaultPageData<User>('EMAIL:DESC'),
|
|
|
|
},
|
|
|
|
userSaving: false,
|
|
|
|
userSaved: false,
|
|
|
|
|
2019-01-16 21:01:29 -08:00
|
|
|
userDetailFetching: false,
|
|
|
|
userDetail: null as null | User,
|
2019-02-04 13:18:50 -08:00
|
|
|
userDeleting: false,
|
|
|
|
userDeleted: false,
|
2019-01-16 21:01:29 -08:00
|
|
|
|
2019-02-15 19:35:25 -08:00
|
|
|
arbiterSaving: false,
|
|
|
|
arbiterSaved: false,
|
|
|
|
|
2019-02-06 10:38:07 -08:00
|
|
|
arbitersSearch: {
|
|
|
|
search: '',
|
|
|
|
results: [] as User[],
|
|
|
|
fetching: false,
|
|
|
|
error: null as string | null,
|
|
|
|
},
|
|
|
|
|
2019-02-05 12:34:19 -08:00
|
|
|
proposals: {
|
2019-02-06 09:47:47 -08:00
|
|
|
page: createDefaultPageData<Proposal>('CREATED:DESC'),
|
2019-02-05 12:34:19 -08:00
|
|
|
},
|
|
|
|
|
2019-01-09 10:23:08 -08:00
|
|
|
proposalDetail: null as null | Proposal,
|
2019-02-06 09:47:47 -08:00
|
|
|
proposalDetailFetching: false,
|
2019-01-09 10:23:08 -08:00
|
|
|
proposalDetailApproving: false,
|
2019-02-13 08:54:46 -08:00
|
|
|
proposalDetailMarkingMilestonePaid: false,
|
2019-01-16 21:01:29 -08:00
|
|
|
|
2019-02-17 11:15:40 -08:00
|
|
|
comments: {
|
|
|
|
page: createDefaultPageData<Comment>('CREATED:DESC'),
|
|
|
|
},
|
|
|
|
|
2019-01-30 09:59:15 -08:00
|
|
|
rfps: [] as RFP[],
|
|
|
|
rfpsFetching: false,
|
|
|
|
rfpsFetched: false,
|
|
|
|
rfpSaving: false,
|
|
|
|
rfpSaved: false,
|
|
|
|
rfpDeleting: false,
|
|
|
|
rfpDeleted: false,
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
contributions: {
|
|
|
|
page: createDefaultPageData<Contribution>('CREATED:DESC'),
|
|
|
|
},
|
|
|
|
|
|
|
|
contributionDetail: null as null | Contribution,
|
|
|
|
contributionDetailFetching: false,
|
|
|
|
contributionSaving: false,
|
|
|
|
contributionSaved: false,
|
|
|
|
|
2019-01-09 11:08:25 -08:00
|
|
|
emailExamples: {} as { [type: string]: EmailExample },
|
2018-10-30 09:35:47 -07:00
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
/*** ACTIONS ***/
|
|
|
|
|
2018-10-30 09:35:47 -07:00
|
|
|
removeGeneralError(i: number) {
|
|
|
|
app.generalError.splice(i, 1);
|
|
|
|
},
|
|
|
|
|
2019-01-09 10:23:08 -08:00
|
|
|
updateProposalInStore(p: Proposal) {
|
2019-02-05 12:34:19 -08:00
|
|
|
const index = app.proposals.page.items.findIndex(x => x.proposalId === p.proposalId);
|
2019-01-09 10:23:08 -08:00
|
|
|
if (index > -1) {
|
2019-02-05 12:34:19 -08:00
|
|
|
app.proposals.page.items[index] = p;
|
2019-01-09 10:23:08 -08:00
|
|
|
}
|
|
|
|
if (app.proposalDetail && app.proposalDetail.proposalId === p.proposalId) {
|
|
|
|
app.proposalDetail = p;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-02-06 10:38:07 -08:00
|
|
|
updateUserInStore(u: User) {
|
2019-02-14 20:18:43 -08:00
|
|
|
const index = app.users.page.items.findIndex(x => x.userid === u.userid);
|
2019-02-06 10:38:07 -08:00
|
|
|
if (index > -1) {
|
2019-02-14 20:18:43 -08:00
|
|
|
app.users.page.items[index] = u;
|
2019-02-06 10:38:07 -08:00
|
|
|
}
|
|
|
|
if (app.userDetail && app.userDetail.userid === u.userid) {
|
2019-02-14 20:18:43 -08:00
|
|
|
app.userDetail = {
|
|
|
|
...app.userDetail,
|
|
|
|
...u,
|
|
|
|
};
|
2019-02-06 10:38:07 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// Auth
|
|
|
|
|
2018-10-30 09:35:47 -07:00
|
|
|
async checkLogin() {
|
|
|
|
app.isLoggedIn = await checkLogin();
|
|
|
|
app.hasCheckedLogin = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
async login(username: string, password: string) {
|
|
|
|
try {
|
|
|
|
app.isLoggedIn = await login(username, password);
|
|
|
|
} catch (e) {
|
|
|
|
app.loginError = e.response.data.message;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async logout() {
|
|
|
|
try {
|
|
|
|
app.isLoggedIn = await logout();
|
|
|
|
} catch (e) {
|
|
|
|
app.generalError.push(e.toString());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async fetchStats() {
|
2019-01-09 10:23:08 -08:00
|
|
|
app.statsFetching = true;
|
2018-10-30 09:35:47 -07:00
|
|
|
try {
|
|
|
|
app.stats = await fetchStats();
|
2019-01-09 10:23:08 -08:00
|
|
|
app.statsFetched = true;
|
2018-10-30 09:35:47 -07:00
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
2019-01-09 10:23:08 -08:00
|
|
|
app.statsFetching = false;
|
2018-10-30 09:35:47 -07:00
|
|
|
},
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// Users
|
|
|
|
|
2018-10-30 09:35:47 -07:00
|
|
|
async fetchUsers() {
|
2019-02-17 12:21:53 -08:00
|
|
|
return await pageFetch(app.users, fetchUsers);
|
2019-02-14 20:18:43 -08:00
|
|
|
},
|
|
|
|
|
2019-02-17 12:21:53 -08:00
|
|
|
setUserPageQuery(params: Partial<PageQuery>) {
|
|
|
|
setPageParams(app.users, params);
|
2019-02-14 20:18:43 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
resetUserPageQuery() {
|
2019-02-17 12:21:53 -08:00
|
|
|
resetPageParams(app.users);
|
2019-01-16 21:01:29 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
async fetchUserDetail(id: number) {
|
|
|
|
app.userDetailFetching = true;
|
|
|
|
try {
|
|
|
|
app.userDetail = await fetchUserDetail(id);
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.userDetailFetching = false;
|
2018-10-30 09:35:47 -07:00
|
|
|
},
|
|
|
|
|
2019-02-14 20:18:43 -08:00
|
|
|
async editUser(id: number, args: Partial<User>) {
|
|
|
|
app.userSaving = true;
|
|
|
|
app.userSaved = false;
|
|
|
|
try {
|
|
|
|
const user = await editUser(id, args);
|
|
|
|
app.updateUserInStore(user);
|
|
|
|
app.userSaved = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.userSaving = false;
|
|
|
|
},
|
|
|
|
|
2019-02-04 13:18:50 -08:00
|
|
|
async deleteUser(id: number) {
|
|
|
|
app.userDeleting = false;
|
|
|
|
app.userDeleted = false;
|
2018-10-30 09:35:47 -07:00
|
|
|
try {
|
|
|
|
await deleteUser(id);
|
2019-02-14 20:18:43 -08:00
|
|
|
app.users.page.items = app.users.page.items.filter(u => u.userid !== id);
|
2019-02-04 13:18:50 -08:00
|
|
|
app.userDeleted = true;
|
|
|
|
app.userDetail = null;
|
2018-10-30 09:35:47 -07:00
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
2019-02-04 13:18:50 -08:00
|
|
|
app.userDeleting = false;
|
2018-10-30 09:35:47 -07:00
|
|
|
},
|
|
|
|
|
2019-02-07 07:57:56 -08:00
|
|
|
// Arbiters
|
|
|
|
|
2019-02-06 10:38:07 -08:00
|
|
|
async searchArbiters(search: string) {
|
|
|
|
app.arbitersSearch = {
|
|
|
|
...app.arbitersSearch,
|
|
|
|
search,
|
|
|
|
fetching: true,
|
|
|
|
};
|
|
|
|
try {
|
|
|
|
const data = await fetchArbiters(search);
|
|
|
|
app.arbitersSearch = {
|
|
|
|
...app.arbitersSearch,
|
|
|
|
...data,
|
|
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.arbitersSearch.fetching = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
async searchArbitersClear() {
|
|
|
|
app.arbitersSearch = {
|
|
|
|
search: '',
|
|
|
|
results: [] as User[],
|
|
|
|
fetching: false,
|
|
|
|
error: null,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
async setArbiter(proposalId: number, userId: number) {
|
2019-02-15 19:35:25 -08:00
|
|
|
app.arbiterSaving = true;
|
|
|
|
app.arbiterSaved = false;
|
|
|
|
try {
|
|
|
|
const { proposal, user } = await setArbiter(proposalId, userId);
|
|
|
|
this.updateProposalInStore(proposal);
|
|
|
|
this.updateUserInStore(user);
|
|
|
|
app.arbiterSaved = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.arbiterSaving = false;
|
2019-02-06 10:38:07 -08:00
|
|
|
},
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// Proposals
|
|
|
|
|
2019-02-05 12:34:19 -08:00
|
|
|
async fetchProposals() {
|
2019-02-17 12:21:53 -08:00
|
|
|
return await pageFetch(app.proposals, fetchProposals);
|
2019-02-05 12:34:19 -08:00
|
|
|
},
|
|
|
|
|
2019-02-17 12:21:53 -08:00
|
|
|
setProposalPageQuery(params: Partial<PageQuery>) {
|
|
|
|
setPageParams(app.proposals, params);
|
2019-02-05 12:34:19 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
resetProposalPageQuery() {
|
2019-02-17 12:21:53 -08:00
|
|
|
resetPageParams(app.proposals);
|
2019-01-09 10:23:08 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
async fetchProposalDetail(id: number) {
|
|
|
|
app.proposalDetailFetching = true;
|
|
|
|
try {
|
|
|
|
app.proposalDetail = await fetchProposalDetail(id);
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.proposalDetailFetching = false;
|
2018-10-30 09:35:47 -07:00
|
|
|
},
|
|
|
|
|
2019-01-29 15:50:27 -08:00
|
|
|
async updateProposalDetail(updates: Partial<Proposal>) {
|
|
|
|
if (!app.proposalDetail) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const res = await updateProposal({
|
|
|
|
...updates,
|
|
|
|
proposalId: app.proposalDetail.proposalId,
|
|
|
|
});
|
|
|
|
app.updateProposalInStore(res);
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-11-09 10:48:55 -08:00
|
|
|
async deleteProposal(id: number) {
|
2018-10-30 09:35:47 -07:00
|
|
|
try {
|
|
|
|
await deleteProposal(id);
|
2019-02-05 12:34:19 -08:00
|
|
|
app.proposals.page.items = app.proposals.page.items.filter(
|
|
|
|
p => p.proposalId === id,
|
|
|
|
);
|
2018-10-30 09:35:47 -07:00
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
},
|
2019-01-09 10:23:08 -08:00
|
|
|
|
|
|
|
async approveProposal(isApprove: boolean, rejectReason?: string) {
|
|
|
|
if (!app.proposalDetail) {
|
2019-01-29 15:50:27 -08:00
|
|
|
const m = 'store.approveProposal(): Expected proposalDetail to be populated!';
|
|
|
|
app.generalError.push(m);
|
|
|
|
console.error(m);
|
2019-01-09 10:23:08 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
app.proposalDetailApproving = true;
|
|
|
|
try {
|
|
|
|
const { proposalId } = app.proposalDetail;
|
|
|
|
const res = await approveProposal(proposalId, isApprove, rejectReason);
|
|
|
|
app.updateProposalInStore(res);
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.proposalDetailApproving = false;
|
|
|
|
},
|
2019-01-09 13:57:15 -08:00
|
|
|
|
2019-02-13 08:54:46 -08:00
|
|
|
async markMilestonePaid(proposalId: number, milestoneId: number, txId: string) {
|
|
|
|
app.proposalDetailMarkingMilestonePaid = true;
|
|
|
|
try {
|
|
|
|
const res = await markMilestonePaid(proposalId, milestoneId, txId);
|
|
|
|
app.updateProposalInStore(res);
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.proposalDetailMarkingMilestonePaid = false;
|
|
|
|
},
|
|
|
|
|
2019-02-17 11:15:40 -08:00
|
|
|
// Comments
|
|
|
|
|
2019-02-17 12:21:53 -08:00
|
|
|
async fetchComments() {
|
|
|
|
return await pageFetch(app.comments, fetchComments);
|
|
|
|
},
|
|
|
|
|
|
|
|
setCommentPageParams(params: Partial<PageQuery>) {
|
|
|
|
setPageParams(app.comments, params);
|
|
|
|
},
|
|
|
|
|
|
|
|
resetCommentPageParams() {
|
|
|
|
resetPageParams(app.comments);
|
|
|
|
},
|
2019-02-17 11:15:40 -08:00
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// Email
|
|
|
|
|
2019-01-09 11:08:25 -08:00
|
|
|
async getEmailExample(type: string) {
|
|
|
|
try {
|
|
|
|
const example = await getEmailExample(type);
|
|
|
|
app.emailExamples = {
|
|
|
|
...app.emailExamples,
|
|
|
|
[type]: example,
|
|
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
},
|
2019-01-30 09:59:15 -08:00
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// RFPs
|
|
|
|
|
2019-01-30 09:59:15 -08:00
|
|
|
async fetchRFPs() {
|
|
|
|
app.rfpsFetching = true;
|
|
|
|
try {
|
|
|
|
app.rfps = await getRFPs();
|
|
|
|
app.rfpsFetched = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.rfpsFetching = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
async createRFP(args: RFPArgs) {
|
|
|
|
app.rfpSaving = true;
|
|
|
|
try {
|
|
|
|
const data = await createRFP(args);
|
|
|
|
app.rfps = [data, ...app.rfps];
|
|
|
|
app.rfpSaved = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.rfpSaving = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
async editRFP(id: number, args: RFPArgs) {
|
|
|
|
app.rfpSaving = true;
|
|
|
|
app.rfpSaved = false;
|
|
|
|
try {
|
|
|
|
await editRFP(id, args);
|
|
|
|
app.rfpSaved = true;
|
|
|
|
await app.fetchRFPs();
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.rfpSaving = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
async deleteRFP(id: number) {
|
|
|
|
app.rfpDeleting = true;
|
|
|
|
app.rfpDeleted = false;
|
|
|
|
try {
|
|
|
|
await deleteRFP(id);
|
|
|
|
app.rfps = app.rfps.filter(rfp => rfp.id !== id);
|
|
|
|
app.rfpDeleted = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.rfpDeleting = false;
|
|
|
|
},
|
2019-02-06 09:47:47 -08:00
|
|
|
|
|
|
|
// Contributions
|
|
|
|
|
|
|
|
async fetchContributions() {
|
2019-02-17 12:21:53 -08:00
|
|
|
return await pageFetch(app.contributions, getContributions);
|
2019-02-06 09:47:47 -08:00
|
|
|
},
|
|
|
|
|
2019-02-17 12:21:53 -08:00
|
|
|
setContributionPageQuery(params: Partial<PageQuery>) {
|
|
|
|
setPageParams(app.contributions, params);
|
2019-02-06 09:47:47 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
resetContributionPageQuery() {
|
2019-02-17 12:21:53 -08:00
|
|
|
resetPageParams(app.contributions);
|
2019-02-06 09:47:47 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
async fetchContributionDetail(id: number) {
|
|
|
|
app.contributionDetailFetching = true;
|
|
|
|
try {
|
|
|
|
app.contributionDetail = await getContribution(id);
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.contributionDetailFetching = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
async editContribution(id: number, args: ContributionArgs) {
|
|
|
|
app.contributionSaving = true;
|
|
|
|
app.contributionSaved = false;
|
|
|
|
try {
|
|
|
|
await editContribution(id, args);
|
|
|
|
app.contributionSaved = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.contributionSaving = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
async createContribution(args: ContributionArgs) {
|
|
|
|
app.contributionSaving = true;
|
|
|
|
app.contributionSaved = false;
|
|
|
|
try {
|
|
|
|
await createContribution(args);
|
|
|
|
app.contributionSaved = true;
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
app.contributionSaving = false;
|
2019-02-07 07:57:56 -08:00
|
|
|
},
|
2018-10-30 09:35:47 -07:00
|
|
|
});
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// Utils
|
2018-10-30 09:35:47 -07:00
|
|
|
function handleApiError(e: AxiosError) {
|
|
|
|
if (e.response && e.response.data!.message) {
|
|
|
|
app.generalError.push(e.response!.data.message);
|
|
|
|
} else if (e.response && e.response.data!.data!) {
|
|
|
|
app.generalError.push(e.response!.data.data);
|
|
|
|
} else {
|
|
|
|
app.generalError.push(e.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
function createDefaultPageData<T>(sort: string): PageData<T> {
|
|
|
|
return {
|
|
|
|
sort,
|
|
|
|
page: 1,
|
|
|
|
search: '',
|
|
|
|
filters: [] as string[],
|
|
|
|
pageSize: 0,
|
|
|
|
total: 0,
|
|
|
|
items: [] as T[],
|
|
|
|
fetching: false,
|
|
|
|
fetched: false,
|
2019-02-07 07:57:56 -08:00
|
|
|
};
|
2019-02-06 09:47:47 -08:00
|
|
|
}
|
|
|
|
|
2019-02-17 11:15:40 -08:00
|
|
|
type FNFetchPage = (params: PageQuery) => Promise<any>;
|
2019-02-17 12:21:53 -08:00
|
|
|
interface PageParent<T> {
|
|
|
|
page: PageData<T>;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function pageFetch<T>(ref: PageParent<T>, fetch: FNFetchPage) {
|
|
|
|
ref.page.fetching = true;
|
|
|
|
try {
|
|
|
|
const params = getPageParams(ref.page);
|
|
|
|
const newPage = await fetch(params);
|
|
|
|
ref.page = {
|
|
|
|
...ref.page,
|
|
|
|
...newPage,
|
|
|
|
fetched: true,
|
|
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
handleApiError(e);
|
|
|
|
}
|
|
|
|
ref.page.fetching = false;
|
2019-02-17 11:15:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
function getPageParams<T>(page: PageData<T>) {
|
|
|
|
return pick(page, ['page', 'search', 'filters', 'sort']) as PageQuery;
|
|
|
|
}
|
|
|
|
|
2019-02-17 12:21:53 -08:00
|
|
|
function setPageParams<T>(ref: PageParent<T>, query: Partial<PageQuery>) {
|
|
|
|
// sometimes we need to reset page to 1
|
|
|
|
if (query.filters || query.search) {
|
|
|
|
query.page = 1;
|
|
|
|
}
|
|
|
|
ref.page = {
|
|
|
|
...ref.page,
|
|
|
|
...query,
|
2019-02-17 11:15:40 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-02-17 12:21:53 -08:00
|
|
|
function resetPageParams<T>(ref: PageParent<T>) {
|
|
|
|
ref.page.page = 1;
|
|
|
|
ref.page.search = '';
|
|
|
|
ref.page.sort = 'CREATED:DESC';
|
|
|
|
ref.page.filters = [];
|
2019-02-17 11:15:40 -08:00
|
|
|
}
|
|
|
|
|
2019-02-06 09:47:47 -08:00
|
|
|
// Attach to window for inspection
|
2018-10-30 09:35:47 -07:00
|
|
|
(window as any).appStore = app;
|
|
|
|
|
|
|
|
// check login status periodically
|
|
|
|
app.checkLogin();
|
|
|
|
window.setInterval(app.checkLogin, 10000);
|
|
|
|
|
|
|
|
export type TApp = typeof app;
|
|
|
|
export default app;
|