diff --git a/src/Categorize.jsx b/src/Categorize.jsx
new file mode 100644
index 0000000..3f6da66
--- /dev/null
+++ b/src/Categorize.jsx
@@ -0,0 +1,228 @@
+import React from 'react';
+import {useState, useEffect} from "react";
+import {useParams, useNavigate} from 'react-router-dom';
+import {
+ categorize
+} from "./apiService";
+
+const Categorize = (props) => {
+ const [error, setError] = useState(null);
+ const [isLoaded, setIsLoaded] = useState(false);
+ const [report, setReport] = useState({});
+ const navigate = useNavigate();
+ const params = useParams();
+
+ console.log("Categorize: calling useEffect");
+ useEffect(() => {
+ console.log("Categorize: useEffect");
+ console.log("params", params);
+ let redo = params.redo == 'true';
+
+ categorize(redo).then(
+ data => {
+ console.log("Categorize.useEffect categorize data", data);
+ if(data.status === 401) {
+ navigate('/login', {replace: true});
+ return;
+ }
+ if(data.status !== 200) {
+ setIsLoaded(true);
+ setError(data);
+ return;
+ }
+ setIsLoaded(true);
+ setReport(data.result);
+ },
+ error => {
+ if(error.status === 401) {
+ navigate('/login', {replace: true});
+ return;
+ }
+ console.log('error object', error);
+ setIsLoaded(true);
+ setError(error);
+ }
+ );
+ }, [navigate, params]);
+
+ if(error) {
+ return
Error: {error.message}
;
+ }
+
+ if(!isLoaded) {
+ return Loading...
;
+ }
+
+ const getFlagNames = function(flags) {
+ let names = [];
+ if(flags & 0x02) {
+ names.push("Case Insensitive");
+ }
+ if ((flags & 0x08) !== 0) {
+ names.push("Multiline");
+ }
+ if ((flags & 0x20) !== 0) {
+ names.push("Dotall");
+ }
+ if ((flags & 0x40) !== 0) {
+ names.push("Unicode Case");
+ }
+ if ((flags & 0x80) !== 0) {
+ names.push("Canon EQ");
+ }
+ if ((flags & 0x01) !== 0) {
+ names.push("Unix Linex");
+ }
+ if ((flags & 0x10) !== 0) {
+ names.push("Literal");
+ }
+ if ((flags & 0x100) !== 0) {
+ names.push("Unicode Character Class");
+ }
+ if ((flags & 0x04) !== 0) {
+ names.push("Comments");
+ }
+ return names.join(", ");
+ }
+
+ const numberFormat = new Intl.NumberFormat("en-US", { style: 'currency', currency: 'USD' });
+
+ console.log("report render", report);
+ return (
+ <>
+ Categorize Result
+ {
+ Object.values(report).map(yearReport => {
+ return (
+ <>
+ {yearReport.year}
+ Multiply Assigned Transactions
+
+
+
+ | ID |
+ source |
+ description |
+ date |
+ amount |
+ regexes |
+
+
+
+ {
+ yearReport.multiplyAssignedTransactions.map(mat => {
+ return (
+
+ |
+ {mat.transaction.transactionId}
+ |
+
+ {mat.transaction.source}
+ |
+
+ {mat.transaction.description}
+ |
+
+ {mat.transaction.date}
+ |
+
+ {numberFormat.format(mat.transaction.amount)}
+ |
+
+
+
+
+ | Category |
+ Source |
+ Regular Expression |
+ Flags |
+ Priority |
+ Extra Description |
+ Year |
+
+
+
+ {
+ mat.regexes.map(regex => {
+ return (
+
+ |
+ {regex.fqCategoryName}
+ |
+
+ {regex.source}
+ |
+
+ {regex.regex}
+ |
+
+ {getFlagNames(regex.flags)}
+ |
+
+ {regex.priority}
+ |
+
+ {regex.description}
+ |
+
+ {regex.year}
+ |
+
+ )
+ })
+ }
+
+
+ |
+
+ )
+ })
+ }
+
+
+ Unassigned Transactions
+
+
+
+ | Year |
+ Source |
+ Description |
+ Date |
+ Amount |
+
+
+
+ {
+ yearReport.unassignedTransactions.map(transaction => {
+ return (
+
+ |
+ {transaction.year}
+ |
+
+ {transaction.source}
+ |
+
+ {transaction.description}
+ |
+
+ {transaction.date}
+ |
+
+ {numberFormat.format(transaction.amount)}
+ |
+
+ )
+ })
+ }
+
+
+ >
+ );
+ })
+ }
+ >
+ );
+}
+
+export default Categorize;
\ No newline at end of file
diff --git a/src/EditRegex.jsx b/src/EditRegex.jsx
index d85ae2c..128f102 100644
--- a/src/EditRegex.jsx
+++ b/src/EditRegex.jsx
@@ -29,8 +29,8 @@ const EditRegex = (props) => {
const [addingCategory, setAddingCategory] = useState(null);
const [addCategoryOkButtonDisabled, setAddCategoryOkButtonDisabled] = useState(true);
const [dirty, setDirty] = useState(false);
- const [attachedTransactions, setAttachedTransactions] = useState(null);
- const [allTransactions, setAllTransactions] = useState(null);
+ const [attachedTransactions, setAttachedTransactions] = useState([]);
+ const [allTransactions, setAllTransactions] = useState([]);
const navigate = useNavigate();
useEffect(() => {
@@ -115,18 +115,18 @@ const EditRegex = (props) => {
useEffect(() => {
console.log('start useEffect 2', regexObj);
let ignore = false;
-
- if(regexObj && regexObj.categoryId) {
- getCategoryAncestry(regexObj.categoryId).then(
- data => {
- // console.log('useEffect 2 getCategoryAncestry data', data);
- if(!ignore) {
- setCategoryAncestry(data);
- setOriginalCategoryAncestry(data);
- setCategoryAncestryIsLoaded(true);
- }
+ let categoryId = regexObj != null ? regexObj.categoryId : null;
+ getCategoryAncestry(categoryId).then(
+ data => {
+ // console.log('useEffect 2 getCategoryAncestry data', data);
+ if(!ignore) {
+ setCategoryAncestry(data);
+ setOriginalCategoryAncestry(data);
+ setCategoryAncestryIsLoaded(true);
}
- );
+ }
+ );
+ if(regexObj && regexObj.id) {
getTransactionsByRegexId("2025", regexObj.id).then(
data => {
if(!ignore) {
@@ -134,14 +134,14 @@ const EditRegex = (props) => {
}
}
);
- getAllTransactions("2025").then(
- data => {
- if(!ignore) {
- setAllTransactions(data);
- }
- }
- )
}
+ getAllTransactions("2025").then(
+ data => {
+ if(!ignore) {
+ setAllTransactions(data);
+ }
+ }
+ )
return () => {
ignore = true;
};
@@ -327,7 +327,7 @@ const EditRegex = (props) => {
}
const handleCategoryChange = e => {
- // console.log('EditRegex.handleCategoryChange', e);
+ console.log('EditRegex.handleCategoryChange', e);
const name = e.target.name;
const value = e.target.value;
let categoryLevel = +name;
@@ -341,6 +341,10 @@ const EditRegex = (props) => {
alert(error.message);
console.error('error object', error);
});
+ let newRegexObj = getCopyOfRegexObj();
+ newRegexObj.categoryId = categoryId;
+ setRegexObj(newRegexObj);
+ setDirty(newRegexObj.categoryId !== originalRegexObj.categoryId);
}
const categoryAncestryWasChanged = (categoryAncestry) => {
@@ -507,7 +511,7 @@ const EditRegex = (props) => {
const categorySelects = [];
// console.log("render categoryAncestry", categoryAncestry);
- if(categoryAncestry) {
+ if(categoryAncestry.length > 0) {
for(let category of categoryAncestry) {
// console.log("category", category);
let key = categorySelects.length;
@@ -550,6 +554,20 @@ const EditRegex = (props) => {
categorySelects.push(select);
}
}
+ else {
+ let key = categorySelects.length;
+ let options = [];
+ options.push()
+ options.push();
+ // console.log("options", options);
+ let select = ;
+ // console.log("select", select);
+ categorySelects.push(select);
+ }
let attachedTransactionRows = [];
let i = 0;
@@ -570,26 +588,34 @@ const EditRegex = (props) => {
if(regexObj.flags & 2) {
flagsStr += "i";
}
- let re = new RegExp(regexObj.regex, flagsStr);
+ let re = null;
+ try {
+ re = new RegExp(regexObj.regex, flagsStr);
+ }
+ catch(e) {
+ re = null;
+ }
let matchedTransactionRows = [];
- i = 0;
- for(let transaction of allTransactions) {
- let regexMatches = re.test(transaction.description);
- let sourceMatches = regexObj.source == null || regexObj.source === "" ||
- transaction.source === regexObj.source;
- // console.log(regexMatches, sourceMatches, transaction.description, transaction.source);
- if(regexMatches && sourceMatches) {
- matchedTransactionRows.push(
- | {transaction.id} |
- {transaction.source} |
- {transaction.type} |
- {transaction.description} |
- {transaction.extraDescription} |
- {transaction.date} |
- {transaction.amount} |
- {transaction.optional} |
- {transaction.regexId} |
-
);
+ if(re) {
+ i = 0;
+ for(let transaction of allTransactions) {
+ let regexMatches = re.test(transaction.description);
+ let sourceMatches = regexObj.source == null || regexObj.source === "" ||
+ transaction.source === regexObj.source;
+ // console.log(regexMatches, sourceMatches, transaction.description, transaction.source);
+ if(regexMatches && sourceMatches) {
+ matchedTransactionRows.push(
+ | {transaction.id} |
+ {transaction.source} |
+ {transaction.type} |
+ {transaction.description} |
+ {transaction.extraDescription} |
+ {transaction.date} |
+ {transaction.amount} |
+ {transaction.optional} |
+ {transaction.regexId} |
+
);
+ }
}
}
diff --git a/src/Regexes.jsx b/src/Regexes.jsx
index bfac7fb..e8304f3 100644
--- a/src/Regexes.jsx
+++ b/src/Regexes.jsx
@@ -186,6 +186,12 @@ const Regexes = (props) => {
deleteRegex(regex.id).then(
data => {
console.log('deleteRegex data', data);
+ var index = regexes.indexOf(regex);
+ if(index > -1) {
+ var newRegexes = regexes.slice();
+ newRegexes.splice(index, 1);
+ setRegexes(newRegexes);
+ }
},
error => {
console.error('error object', error);
diff --git a/src/Report.jsx b/src/Report.jsx
new file mode 100644
index 0000000..3b7cb19
--- /dev/null
+++ b/src/Report.jsx
@@ -0,0 +1,354 @@
+import React from 'react';
+import {useState, useEffect} from "react";
+import {useSearchParams, useNavigate, useLocation} from 'react-router-dom';
+import {report} from "./apiService";
+
+const Report = (props) => {
+ const [error, setError] = useState(null);
+ const [isLoaded, setIsLoaded] = useState(false);
+ const [reportMap, setReportMap] = useState({});
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [searchParams, setSearchParams] = useSearchParams();
+ const [years, setYears] = useState(null);
+ const [startDate, setStartDate] = useState(null);
+ const [endDate, setEndDate] = useState(null);
+ const [includes, setIncludes] = useState(null);
+
+ console.log("Report: calling useEffect");
+ useEffect(() => {
+ console.log("Report: useEffect");
+ console.log("searchParams", searchParams);
+ console.log('location', location);
+ let tmpYears = searchParams.get("years");
+ if(tmpYears == null && location.state != null) {
+ tmpYears = location.state.years;
+ }
+ setYears(tmpYears);
+ let tmpStartDate = searchParams.get("startDate");
+ if(tmpStartDate == null && location.state != null) {
+ tmpStartDate = location.state.startDate;
+ }
+ setStartDate(tmpStartDate);
+ let tmpEndDate = searchParams.get("endDate");
+ if(tmpEndDate == null && location.state != null) {
+ tmpEndDate = location.state.endDate;
+ }
+ setEndDate(tmpEndDate);
+ let tmpIncludes = searchParams.get("includes");
+ if(tmpIncludes == null && location.state != null) {
+ tmpIncludes = location.state.includes;
+ }
+ setIncludes(tmpIncludes);
+ window.localStorage.setItem('report-years', tmpYears);
+ window.localStorage.setItem('report-startDate', tmpStartDate);
+ window.localStorage.setItem('report-endDate', tmpEndDate);
+ window.localStorage.setItem('report-includes', tmpIncludes);
+ console.log(tmpYears, tmpStartDate, tmpEndDate, tmpIncludes);
+ report(tmpYears, tmpStartDate, tmpEndDate, tmpIncludes).then(
+ data => {
+ console.log("Report.useEffect report() data", data);
+ if(data.status === 401) {
+ navigate('/login', {replace: true});
+ return;
+ }
+ if(data.status !== 200) {
+ setIsLoaded(true);
+ setError(data);
+ return;
+ }
+ setIsLoaded(true);
+ setReportMap(data.result);
+ },
+ error => {
+ if(error.status === 401) {
+ navigate('/login', {replace: true});
+ return;
+ }
+ console.log('error object', error);
+ setIsLoaded(true);
+ setError(error);
+ }
+ );
+ }, [navigate, location, searchParams]);
+
+ if(error) {
+ return Error: {error.message}
;
+ }
+
+ if(!isLoaded) {
+ return Loading...
;
+ }
+
+ const getFlagNames = function(flags) {
+ let names = [];
+ if(flags & 0x02) {
+ names.push("Case Insensitive");
+ }
+ if ((flags & 0x08) !== 0) {
+ names.push("Multiline");
+ }
+ if ((flags & 0x20) !== 0) {
+ names.push("Dotall");
+ }
+ if ((flags & 0x40) !== 0) {
+ names.push("Unicode Case");
+ }
+ if ((flags & 0x80) !== 0) {
+ names.push("Canon EQ");
+ }
+ if ((flags & 0x01) !== 0) {
+ names.push("Unix Linex");
+ }
+ if ((flags & 0x10) !== 0) {
+ names.push("Literal");
+ }
+ if ((flags & 0x100) !== 0) {
+ names.push("Unicode Character Class");
+ }
+ if ((flags & 0x04) !== 0) {
+ names.push("Comments");
+ }
+ return names.join(", ");
+ }
+
+ let renderCategoryName = (category, indent) => {
+ if(category.details.length > 0) {
+ return (
+
+ { category.name }
+ |
+ )
+ }
+ return (
+
+ { category.name }
+ |
+ )
+ };
+
+ const numberFormat = new Intl.NumberFormat("en-US", { style: 'currency', currency: 'USD' });
+
+ let renderMonths = (category) => {
+ let cols = [];
+ for(let monthNum = 0; monthNum < category.monthCount; monthNum++) {
+ let amount = category.monthGrandTotals[monthNum];
+ if(amount == null) {
+ amount = 0;
+ }
+ let style = {textAlign: "right", display: "none"};
+ if(category.largestMonth === monthNum) {
+ style["color"] = "RED";
+ }
+ cols.push({numberFormat.format(amount)} | );
+ }
+ return cols;
+ };
+
+ let renderDetail = (key, category) => {
+ let rows = [];
+ for(let detail of category.details) {
+ rows.push(
+ | {detail.transactionId} |
+ {detail.source} |
+ {detail.description} |
+ {detail.date} |
+ {numberFormat.format(detail.amount)} |
+ {detail.extraDescription} |
+ {detail.regex} |
+ {getFlagNames(detail.flags)} |
+ {detail.requiredSource} |
+
)
+ }
+ return rows;
+ };
+
+ let handleCategoryNameClick = (e) => {
+ // alert("clicked " + e.target);
+ let row = e.target.parentElement.nextSibling;
+ let d = row.style.display;
+ row.style.display = d === "none" ? "" : "none";
+ };
+
+ let renderCategory = (level, year, category) => {
+ let indent = level * 10;
+ let result = [];
+ let key = "key-" + year + "-" + category.id;
+ result.push(
+ <>
+
+ { renderCategoryName(category, indent) }
+ |
+ { numberFormat.format(category.grandAverage) }
+ |
+
+ { numberFormat.format(category.grandTotal) }
+ |
+ { renderMonths(category) }
+
+
+
+
+
+
+ | ID |
+ Source |
+ Description |
+ Date |
+ Amount |
+ Extra Description |
+ Pattern |
+ Flags |
+ Required Source |
+
+
+
+ { renderDetail(key, category) }
+
+
+ |
+
+ >
+ );
+ for(let childCategory of category.childCategories) {
+ result.push(renderCategory(level + 1, year, childCategory));
+ }
+ return result;
+ };
+
+ const toReport = (years, startDate, endDate, includes) => {
+ console.log("toReport", years, startDate, endDate, includes);
+ navigate('/report-params', {replace: true, state: {
+ years: years,
+ startDate: startDate,
+ endDate: endDate,
+ includes: includes
+ }});
+ };
+
+ const showColumn = (e, name, doNotCheckAll) => {
+ let cols = document.body.querySelectorAll("table.report td." + name + ",th." + name);
+ for(let col of cols) {
+ col.style.display = e.target.checked ? "" : "none";
+ }
+ if(!doNotCheckAll) {
+ let inputs = document.body.querySelectorAll("table.parameters label input:not(.all)");
+ var allChecked = true;
+ for(let input of inputs) {
+ if(!input.checked) {
+ allChecked = false;
+ break;
+ }
+ }
+ let input = document.body.querySelector("table.parameters label input.all");
+ input.checked = allChecked;
+ }
+ else {
+ let input = document.body.querySelector("table.parameters label input." + name);
+ input.checked = e.target.checked;
+ }
+ };
+
+ const showAllColumns = (e) => {
+ showColumn(e, "average", true);
+ showColumn(e, "total", true);
+ showColumn(e, "month0", true);
+ showColumn(e, "month1", true);
+ showColumn(e, "month2", true);
+ showColumn(e, "month3", true);
+ showColumn(e, "month4", true);
+ showColumn(e, "month5", true);
+ showColumn(e, "month6", true);
+ showColumn(e, "month7", true);
+ showColumn(e, "month8", true);
+ showColumn(e, "month9", true);
+ showColumn(e, "month10", true);
+ showColumn(e, "month11", true);
+ };
+
+ console.log("report render", report);
+ return (
+ <>
+ Report
+
+
+
+
+
+
+ | Years |
+ {years} |
+ |
+ |
+ |
+ |
+ |
+
+
+ | Start date |
+ {startDate} |
+ |
+ |
+ |
+ |
+
+
+ | End date |
+ {endDate} |
+ |
+ |
+ |
+ |
+
+
+ | Includes |
+ {includes} |
+ |
+ |
+ |
+ |
+
+
+
+
+ {
+ Object.values(reportMap).map(yearReport => {
+ let style = {textAlign: "right", display: "none"};
+ return (
+ <>
+ {yearReport.year}
+
+
+
+ | Category |
+ Average |
+ Total |
+ Jan |
+ Feb |
+ Mar |
+ Apr |
+ May |
+ Jun |
+ Jul |
+ Aug |
+ Sep |
+ Oct |
+ Nov |
+ Dec |
+
+
+
+ { renderCategory(0, yearReport.year, yearReport.rootCategory) }
+
+
+ >
+ );
+ })
+ }
+ >
+ );
+}
+
+export default Report;
\ No newline at end of file
diff --git a/src/ReportParams.jsx b/src/ReportParams.jsx
new file mode 100644
index 0000000..9ced202
--- /dev/null
+++ b/src/ReportParams.jsx
@@ -0,0 +1,98 @@
+import React from 'react';
+import {useState, useEffect} from "react";
+import {useSearchParams, useNavigate} from 'react-router-dom';
+
+const ReportParamsForm = (props) => {
+ const navigate = useNavigate();
+ const [inputs, setInputs] = useState({});
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ console.log("ReportParamsForm: calling useEffect");
+ useEffect(() => {
+ console.log("ReportParamsForm: useEffect");
+ console.log("searchParams", searchParams);
+ let years = searchParams.get("years") || window.localStorage.getItem('report-years');
+ let startDate = searchParams.get("startDate") || window.localStorage.getItem('report-startDate');
+ let endDate = searchParams.get("endDate") || window.localStorage.getItem('report-endDate');
+ let includes = searchParams.get("includes") || window.localStorage.getItem('report-includes');
+ let tmpInputs = {};
+ if(years) {
+ tmpInputs.years = years == "null" ? null : years;
+ }
+ if(startDate) {
+ tmpInputs.startDate = startDate == 'null' ? null : startDate;
+ }
+ if(endDate) {
+ tmpInputs.endDate = endDate == 'null' ? null : endDate;
+ }
+ if(includes) {
+ tmpInputs.includes = includes == 'null' ? null : includes;
+ }
+ setInputs(tmpInputs);
+ }, [navigate, searchParams]);
+
+ const handleChange = event => {
+ const name = event.target.name;
+ const value = event.target.value;
+ setInputs(values => ({...values, [name]: value}));
+ console.log("handleChange", inputs);
+ }
+
+ const handleSubmit = event => {
+ event.preventDefault();
+ console.log("handleSubmit", inputs);
+ window.localStorage.setItem('report-years', inputs.years);
+ window.localStorage.setItem('report-startDate', inputs.startDate);
+ window.localStorage.setItem('report-endDate', inputs.endDate);
+ window.localStorage.setItem('report-includes', inputs.includes);
+ navigate('/report',{state:inputs});
+ }
+
+ console.log("ReportParamsForm: render, inputs", inputs);
+ return (
+ <>
+
+ >
+ )
+}
+
+export default ReportParamsForm;
\ No newline at end of file
diff --git a/src/apiService.jsx b/src/apiService.jsx
index 73e82fd..b42208e 100644
--- a/src/apiService.jsx
+++ b/src/apiService.jsx
@@ -1,4 +1,4 @@
-const baseUrl = 'http://localhost:9090';
+const baseUrl = 'http://kirk:9090';
const fetchOptions = (method, data) => {
let token = window.localStorage.getItem('token');
@@ -76,6 +76,52 @@ export const getRegexesBySource = source => {
});
};
+export const categorize = (redo) => {
+ let url = redo ? `${baseUrl}/categorize` : `${baseUrl}/problems`;
+ return fetch(url, fetchOptions('GET')).then(response => {
+ // console.log(url, "response", response);
+ if(response.status !== 200) {
+ return new Promise((resolve, reject) => {
+ reject({status: response.status});
+ });
+ }
+ return response.json(); // promise
+ });
+}
+
+export const report = (years, startDate, endDate, includes) => {
+ let url = `${baseUrl}/report`;
+ let params = {};
+ if(years) {
+ params.years = years;
+ }
+ if(startDate) {
+ params.startDate = startDate;
+ }
+ if(endDate) {
+ params.endDate = endDate;
+ }
+ if(includes) {
+ params.includes = includes;
+ }
+ var sep = "?";
+ for(let key in params) {
+ let value = params[key];
+ url += sep;
+ sep = "&";
+ url += key + "=" + value;
+ }
+ return fetch(url, fetchOptions('GET')).then(response => {
+ // console.log(url, "response", response);
+ if(response.status !== 200) {
+ return new Promise((resolve, reject) => {
+ reject({status: response.status});
+ });
+ }
+ return response.json(); // promise
+ });
+}
+
export const getRegexes = () => {
let url = `${baseUrl}/regexes`;
return fetch(url, fetchOptions('GET')).then(response => {
@@ -222,52 +268,79 @@ export const createCategory = (dataResult, parentCategoryId) => ({
name: dataResult.name
});
+const alphabetize = (siblings) => {
+ siblings.sort(function(c1, c2) {
+ if(c1.name < c2.name) {
+ return -1;
+ }
+ if(c1.name > c2.name) {
+ return 1;
+ }
+ return 0;
+ });
+ return siblings;
+};
+
export const getCategoryAncestry = categoryId => {
- let url = `${baseUrl}/categories/ancestry/${categoryId}`;
- return fetch(url, fetchOptions('GET')).then(response => {
- // console.log(url, "response", response);
- if(response.status !== 200) {
+ if(categoryId) {
+ let url = `${baseUrl}/categories/ancestry/${categoryId}`;
+ return fetch(url, fetchOptions('GET')).then(response => {
+ // console.log(url, "response", response);
+ if(response.status !== 200) {
+ return new Promise((resolve, reject) => {
+ reject({status: response.status});
+ });
+ }
+ return response.json(); // promise
+ }).then(data => {
+ // console.log('getCategoryAncestry data', categoryId, data);
+ if(data.status !== 200) {
+ return new Promise((resolve, reject) => {
+ reject(data);
+ });
+ }
return new Promise((resolve, reject) => {
- reject({status: response.status});
- });
- }
- return response.json(); // promise
- }).then(data => {
- // console.log('getCategoryAncestry data', categoryId, data);
- if(data.status !== 200) {
- return new Promise((resolve, reject) => {
- reject(data);
- });
- }
- return new Promise((resolve, reject) => {
- let ancestry = data.result;
- let getSelectionList = index => {
- if(index >= ancestry.length) {
- let lastCategoryId = ancestry.length > 0 ? ancestry[ancestry.length - 1].id : null;
- let category = {parentCategoryId: lastCategoryId};
+ let ancestry = data.result;
+ let getSelectionList = index => {
+ if(index >= ancestry.length) {
+ let lastCategoryId = ancestry.length > 0 ? ancestry[ancestry.length - 1].id : null;
+ let category = {parentCategoryId: lastCategoryId};
+ getCategories(category.parentCategoryId).then(siblings => {
+ // console.log("siblings", siblings);
+ category.siblings = alphabetize(siblings);
+ ancestry.push(category);
+ resolve(ancestry);
+ });
+ return;
+ }
+ let category = ancestry[index];
+ category.siblings = [];
getCategories(category.parentCategoryId).then(siblings => {
// console.log("siblings", siblings);
- category.siblings = siblings;
- ancestry.push(category);
- resolve(ancestry);
+ category.siblings = alphabetize(siblings);
+ getSelectionList(index + 1);
});
- return;
- }
- let category = ancestry[index];
- category.siblings = [];
- getCategories(category.parentCategoryId).then(siblings => {
- // console.log("siblings", siblings);
- category.siblings = siblings;
- getSelectionList(index + 1);
- });
- };
- getSelectionList(0);
+ };
+ getSelectionList(0);
+ });
+ }, error => {
+ return new Promise((resolve, reject) => {
+ reject(error);
+ });
});
- }, error => {
+ }
+ else {
return new Promise((resolve, reject) => {
- reject(error);
+ let category = {};
+ getCategories(category.parentCategoryId).then(siblings => {
+ // console.log("siblings", siblings);
+ category.siblings = alphabetize(siblings);
+ let ancestry = [category];
+ resolve(ancestry);
+ });
+ return;
});
- });
+ }
};
export const saveCategory = category => {
diff --git a/src/index.jsx b/src/index.jsx
index da6ca5a..6aab26a 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -4,15 +4,20 @@ import LoginForm from './LoginForm';
import {BrowserRouter, Routes, Route, Link} from "react-router-dom";
import Regexes from "./Regexes";
import EditRegex from "./EditRegex";
+import Categorize from "./Categorize";
+import Report from "./Report";
+import ReportParamsForm from "./ReportParams";
import './main.css';
export default function App() {
let token = window.localStorage.getItem('token');
- console.log('token', token);
+ console.log('App: token', token);
return (
Regexes
- Week
+ Categorize
+ Problems
+ Report
Log out
@@ -20,6 +25,9 @@ export default function App() {
}/>
}/>
+ }/>
+ }/>
+ }/>
}/>
}/>
}/>
diff --git a/src/main.css b/src/main.css
index eeaa89c..5cc79bc 100644
--- a/src/main.css
+++ b/src/main.css
@@ -2,6 +2,18 @@ body {
padding: 40px;
font-family: sans-serif;
}
+table.mat tr td {
+ border: 1px solid #CCC;
+}
+table.ut tr td {
+ border: 1px solid #CCC;
+}
+table.mat tr td.amount {
+ text-align: right;
+}
+table.ut tr td.amount {
+ text-align: right;
+}
table.week tr td.duration {
text-align: right;
}
@@ -24,6 +36,9 @@ div.header-date, div.header-links {
div.header-date {
margin-right: 5px;
}
+table.detail tr th,td {
+ padding-right: 20px;
+}
@media only screen and (max-width: 640px) {
body {
padding: 0px;
diff --git a/start b/start
index 39cfe6b..284b5cb 100755
--- a/start
+++ b/start
@@ -1,4 +1,4 @@
#!/bin/sh
npm run build > build.log 2> build.err.log
-serve -s --no-port-switching -l 3000 build > start.log 2> start.err.log &
+serve -s --no-port-switching -l 3001 build > start.log 2> start.err.log &
echo "$!" > pid
\ No newline at end of file