Skip to content

Commit

Permalink
Merge pull request #14 from bcgov/dev
Browse files Browse the repository at this point in the history
Production Release
  • Loading branch information
timwekkenbc authored Jul 10, 2024
2 parents 26bf0b3 + 6eef5a3 commit 732edcd
Show file tree
Hide file tree
Showing 33 changed files with 1,734 additions and 153 deletions.
5 changes: 1 addition & 4 deletions .github/workflows/eslint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ jobs:
continue-on-error: true

- name: Upload ESLint report
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: eslint-report
path: eslint-report.html

- name: Display ESLint report link
run: echo "::set-output name=eslint-report::${{ steps.upload.outputs.artifact_path }}"
77 changes: 16 additions & 61 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,14 @@ export default function Admin() {
// Get rules that are already defined in the DB
const existingRules = await getAllRuleData();
setInitialRules(existingRules);
// Get rules that exist in the rules repository, but aren't yet defined in the DB
const existingRuleDocuments = await getAllRuleDocuments();
const undefinedRules = existingRuleDocuments
.filter((ruleJSON: string) => {
return !existingRules.find((rule: RuleInfo) => rule.goRulesJSONFilename === ruleJSON);
})
.map((ruleJSON: string) => ({ goRulesJSONFilename: ruleJSON }));
const ruleData = [...existingRules, ...undefinedRules];
setRules(JSON.parse(JSON.stringify(ruleData))); // JSON.parse(JSON.stringify(data)) is a hacky way to deep copy the data - needed for comparison later
setRules(JSON.parse(JSON.stringify([...existingRules]))); // JSON.parse(JSON.stringify(data)) is a hacky way to deep copy the data - needed for comparison later
setIsLoading(false);
};

useEffect(() => {
getOrRefreshRuleList();
}, []);

const addNewRule = async () => {
const newRules = [...rules];
newRules.push({
_id: "",
title: "",
goRulesJSONFilename: "",
chefsFormId: "",
chefsFormAPIKey: "",
});
setRules(newRules);
};

const updateRule = (e: React.ChangeEvent<HTMLInputElement>, index: number, property: keyof RuleInfo) => {
const newRules = [...rules];
newRules[index][property] = e.target.value;
Expand All @@ -72,9 +52,7 @@ export default function Admin() {
} else if (
initialRule._id !== rule._id ||
initialRule.title !== rule.title ||
initialRule.goRulesJSONFilename !== rule.goRulesJSONFilename ||
initialRule.chefsFormId !== rule.chefsFormId ||
initialRule.chefsFormAPIKey !== rule.chefsFormAPIKey
initialRule.goRulesJSONFilename !== rule.goRulesJSONFilename
) {
return { rule, action: ACTION_STATUS.UPDATE };
}
Expand All @@ -92,19 +70,19 @@ export default function Admin() {
const entriesToUpdate = getRulesToUpdate();
await Promise.all(
entriesToUpdate.map(async ({ rule, action }) => {
try {
if (action === ACTION_STATUS.NEW) {
await postRuleData(rule);
} else if (rule?._id) {
if (action === ACTION_STATUS.UPDATE) {
await updateRuleData(rule._id, rule);
} else if (action === ACTION_STATUS.DELETE) {
await deleteRuleData(rule._id);
}
try {
if (action === ACTION_STATUS.NEW) {
await postRuleData(rule);
} else if (rule?._id) {
if (action === ACTION_STATUS.UPDATE) {
await updateRuleData(rule._id, rule);
} else if (action === ACTION_STATUS.DELETE) {
await deleteRuleData(rule._id);
}
} catch (error) {
console.error(`Error performing action ${action} on rule ${rule._id}: ${error}`);
}
} catch (error) {
console.error(`Error performing action ${action} on rule ${rule._id}: ${error}`);
}
})
);
getOrRefreshRuleList();
Expand All @@ -123,28 +101,13 @@ export default function Admin() {
title: "Title",
dataIndex: "title",
render: renderInputField("title"),
width: "220px"
},
{
title: "GoRules Id",
dataIndex: "_id",
render: renderInputField("_id"),
width: "220px",
},
{
title: "GoRules JSON Filename",
dataIndex: "goRulesJSONFilename",
render: renderInputField("goRulesJSONFilename"),
width: "260px"
},
{
title: "CHEFS Form Id",
dataIndex: "chefsFormId",
render: renderInputField("chefsFormId"),
},
{
title: "CHEFS Form API Key",
dataIndex: "chefsFormAPIKey",
render: renderInputField("chefsFormAPIKey"),
width: "260px",
},
{
dataIndex: "delete",
Expand Down Expand Up @@ -180,15 +143,7 @@ export default function Admin() {
{isLoading ? (
<p>Loading...</p>
) : (
<Table
columns={columns}
dataSource={rules.map((rule, key) => ({ key, ...rule }))}
footer={() => (
<Button type="primary" onClick={addNewRule}>
Add New Rule +
</Button>
)}
/>
<Table columns={columns} dataSource={rules.map((rule, key) => ({ key, ...rule }))} />
)}
</>
);
Expand Down
4 changes: 4 additions & 0 deletions app/components/InputOutputTable/InputOutputTable.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
.tableTitle {
display: flex;
gap: 20px;
justify-content: space-between;
align-items: center;
margin: 0;
background: #f9f9f9;
padding: 16px;
Expand Down
129 changes: 102 additions & 27 deletions app/components/InputOutputTable/InputOutputTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect } from "react";
import { Table, Tag } from "antd";
import { useState, useEffect, FocusEvent } from "react";
import { Table, Tag, Input, Button } from "antd";
import { RuleMap } from "@/app/types/rulemap";
import styles from "./InputOutputTable.module.css";

const COLUMNS = [
Expand All @@ -15,54 +16,128 @@ const COLUMNS = [
},
];

const PROPERTIES_TO_IGNORE = ["submit", "lateEntry"];
const PROPERTIES_TO_IGNORE = ["submit", "lateEntry", "rulemap"];

interface rawDataProps {
[key: string]: any;
}

interface InputOutputTableProps {
title: string;
rawData: object;
rawData: rawDataProps | null | undefined;
setRawData?: (data: rawDataProps) => void;
submitButtonRef?: React.RefObject<HTMLButtonElement>;
editable?: boolean;
rulemap?: RuleMap;
}

export default function InputOutputTable({ title, rawData }: InputOutputTableProps) {
export default function InputOutputTable({
title,
rawData,
setRawData,
submitButtonRef,
editable = false,
rulemap,
}: InputOutputTableProps) {
const [dataSource, setDataSource] = useState<object[]>([]);
const [columns, setColumns] = useState(COLUMNS);
const [showTable, setShowTable] = useState(true);

const toggleTableVisibility = () => {
setShowTable(!showTable);
};

const convertAndStyleValue = (value: any, property: string, editable: boolean) => {
if (editable) {
return (
<label className="labelsmall">
<Input
defaultValue={value}
onBlur={(e) => handleValueChange(e, property)}
onKeyDown={(e) => handleKeyDown(e)}
/>
<span className="label-text">{property}</span>
</label>
);
}

const convertAndStyleValue = (value: any, property: string) => {
// Handle booleans
if (typeof value === "boolean") {
return value ? <Tag color="green">TRUE</Tag> : <Tag color="red">FALSE</Tag>;
}
// Handle money amounts

if (typeof value === "number" && property.toLowerCase().includes("amount")) {
value = `$${value}`;
return `$${value}`;
}

return <b>{value}</b>;
};

const handleValueChange = (e: FocusEvent<HTMLInputElement, Element>, property: string) => {
if (!e.target) return;
const newValue = (e.target as HTMLInputElement).value;
let queryValue: any = newValue;

if (newValue.toLowerCase() === "true") {
queryValue = true;
} else if (newValue.toLowerCase() === "false") {
queryValue = false;
} else if (!isNaN(Number(newValue))) {
queryValue = Number(newValue);
}

const updatedData = { ...rawData, [property]: queryValue } || {};

if (typeof setRawData === "function") {
setRawData(updatedData);
} else {
console.error("setRawData is not a function or is undefined");
}
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && submitButtonRef) {
if (submitButtonRef.current) {
submitButtonRef.current.click();
}
}
};

useEffect(() => {
if (rawData) {
const newData: object[] = [];
Object.entries(rawData).forEach(([property, value], index) => {
if (!PROPERTIES_TO_IGNORE.includes(property)) {
newData.push({
property,
value: convertAndStyleValue(value, property),
key: index,
});
}
});
const propertyRuleMap = Object.values(rulemap || {}).flat();
const newData = Object.entries(rawData)
.filter(([property]) => !PROPERTIES_TO_IGNORE.includes(property))
.sort(([propertyA], [propertyB]) => propertyA.localeCompare(propertyB))
.map(([property, value], index) => ({
property: propertyRuleMap?.find((item) => item.property === property)?.name || property,
value: convertAndStyleValue(value, property, editable),
key: index,
}));
setDataSource(newData);
const newColumns = COLUMNS.filter((column) => showColumn(newData, column.dataIndex));
setColumns(newColumns);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rawData]);

const showColumn = (data: any[], columnKey: string) => {
return data.some((item) => item[columnKey] !== null && item[columnKey] !== undefined);
};

return (
<div>
<h4 className={styles.tableTitle}>{title}</h4>
<Table
columns={COLUMNS}
showHeader={false}
dataSource={dataSource}
bordered
pagination={{ hideOnSinglePage: true }}
/>
<h4 className={styles.tableTitle}>
{title} {title === "Outputs" && <Button onClick={toggleTableVisibility}>{showTable ? "Hide" : "Show"}</Button>}
</h4>
{showTable && (
<Table
columns={columns}
showHeader={false}
dataSource={dataSource}
bordered
pagination={{ hideOnSinglePage: true }}
/>
)}
</div>
);
}
Loading

0 comments on commit 732edcd

Please sign in to comment.