Skip to content

Commit

Permalink
Merge pull request #48 from bcgov/feature/tests-in-rules
Browse files Browse the repository at this point in the history
Feature - Scenario Upload and Version Management Updates
  • Loading branch information
brysonjbest authored Nov 27, 2024
2 parents a79cafc + 7c91240 commit 8b69800
Show file tree
Hide file tree
Showing 34 changed files with 1,353 additions and 373 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ This app is a tool for authoring and testing/simulating rules for SDPR's Busines

This project current depends on the API provided by the [brm-backend](https://github.com/bcgov/brm-backend) project. You'll have to set an environment variable of `NEXT_PUBLIC_SERVER_URL` pointing to the URL of that when it is up and running (like `http://localhost:3000`).

This project also depends on the API provided by the [klamm](https://github.com/bcgov/klamm) project. You'll have to set an environment variable of `NEXT_PUBLIC_KLAMM_URL` pointing to the url and endpoint for the business rules set of information (like `http://localhost/bre`).

## Getting it running

Install dependencies:
Expand Down
17 changes: 15 additions & 2 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ export default function Admin() {

const renderInputField = (fieldName: keyof RuleInfoBasic) => {
const Component = (value: string, _: RuleInfo, index: number) => (
<Input value={value} onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateRule(e, index, fieldName)} />
<Input
value={value}
aria-label={`Enter ${fieldName}`}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateRule(e, index, fieldName)}
/>
);
Component.displayName = "InputField";
return Component;
Expand All @@ -156,6 +160,7 @@ export default function Admin() {
render: renderInputField("filepath"),
},
{
title: "Manage",
dataIndex: "delete",
width: "60px",
render: (value: string, _: RuleInfo, index: number) => {
Expand All @@ -179,6 +184,7 @@ export default function Admin() {
},
},
{
title: "View",
dataIndex: "view",
width: "60px",
render: (_: string, { _id, isPublished }: RuleInfo) => {
Expand Down Expand Up @@ -221,7 +227,14 @@ export default function Admin() {
</Button>
)}
</Flex>
<Input.Search placeholder="Search rules..." onSearch={handleSearch} style={{ marginBottom: 16 }} allowClear />
<Input.Search
placeholder="Search rules..."
onSearch={handleSearch}
style={{ marginBottom: 16 }}
allowClear
aria-label="Search rules"
role="searchbox"
/>

{isLoading ? (
<p>Loading...</p>
Expand Down
4 changes: 3 additions & 1 deletion app/components/InputOutputTable/InputOutputTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ export default function InputOutputTable({
.map(([field, value], index) => {
const propertyRule = propertyRuleMap?.find((item) => item.field === field);
return {
field: FieldStyler(propertyRule?.name || field, propertyRule?.description),
field: FieldStyler(
propertyRule?.name ? propertyRule : { name: field, description: propertyRule?.description }
),
value: convertAndStyleValue(value, field, editable),
key: index,
};
Expand Down
43 changes: 38 additions & 5 deletions app/components/InputStyler/subcomponents/FieldStyler.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
import { Tooltip, Popover } from "antd";
import { InfoCircleOutlined } from "@ant-design/icons";
import { InfoCircleOutlined, ArrowRightOutlined } from "@ant-design/icons";
import React from "react";

interface FieldProps {
name: string;
description?: string;
field?: string;
}

export default function FieldStyler({ name, description = "", field }: FieldProps) {
const klammLink = `${process.env.NEXT_PUBLIC_KLAMM_URL}/fields/${field}`;
const formattedDescription = description
? description.split("\n").map((text, index) => (
<p key={index} style={{ margin: "0" }}>
{text}
</p>
))
: null;

const descriptionLink = (
<>
{formattedDescription}{" "}
<a href={klammLink} rel="noopener noreferrer" target="_blank">
KLAMM <ArrowRightOutlined />
</a>
</>
);

const finalDescription = process.env.NEXT_PUBLIC_KLAMM_URL && field ? descriptionLink : formattedDescription;

export default function FieldStyler(fieldName: string, description?: string) {
const popOverInformation = (
<Popover placement="top" content={description} title={fieldName} trigger={"click"}>
<Popover
placement="top"
content={finalDescription}
title={name}
trigger="click"
overlayStyle={{ maxWidth: "300px", wordWrap: "break-word" }}
>
<span>
{" "}
<InfoCircleOutlined />
Expand All @@ -12,8 +45,8 @@ export default function FieldStyler(fieldName: string, description?: string) {
);
const helpDialog = "View Description";
return (
<label>
{fieldName}
<label htmlFor={field ? field : name}>
{name}
{description && (
<Tooltip title={helpDialog} placement="top">
{popOverInformation}
Expand Down
33 changes: 24 additions & 9 deletions app/components/RuleHeader/RuleHeader.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,33 @@
position: relative;
margin-bottom: 8px;
border-radius: 12px 12px 0 0;
background: white;
}

.homeWrapper {
padding: 8px 8px 0 8px;
}

.headerWrapper {
background: linear-gradient(0deg, #FFFFFF, #FFFFFFe4);
padding: 16px 8px 8px 8px;
padding: 8px;
}

.homeButton {
color: #666;
border: 1px solid;
border-radius: 10px;
color: #666 !important;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: white;
border: 1px solid;
border-radius: 10px;
font-size: 16px;
}

.titleHeader {
margin: 0;
word-wrap: break-word;
max-width: calc(100vw - 32px);
max-width: 100%;
}

.titleInput {
Expand All @@ -30,10 +38,14 @@
}

.titleFilePath {
margin: 4px 0;
margin: 0;
color: #aaa;
}

.rightContent {
min-width: 200px;
}

.editButton {
background: none;
border: none;
Expand All @@ -43,8 +55,6 @@
color: #4b4b4b;
}



@media (max-width: 767px) {
.headerWrapper {
flex-direction: column;
Expand All @@ -55,6 +65,11 @@
align-items: flex-start !important;
gap: 8px !important;
}
.rightContent {
flex-direction: column;
align-items: flex-start;
min-width: unset;
}
.titleHeader {
font-size: 24px;
}
Expand Down
93 changes: 21 additions & 72 deletions app/components/RuleHeader/RuleHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
"use client";
import { useState, useRef, useEffect } from "react";
import { usePathname } from "next/navigation";
import { Button, Flex, Tag } from "antd";
import {
HomeOutlined,
EyeOutlined,
EditOutlined,
CheckOutlined,
CheckCircleOutlined,
CheckCircleFilled,
} from "@ant-design/icons";
import { Flex, Tooltip, Button } from "antd";
import { HomeOutlined, CheckOutlined, ExportOutlined } from "@ant-design/icons";
import { RuleInfo } from "@/app/types/ruleInfo";
import { RULE_VERSION } from "@/app/constants/ruleVersion";
import { updateRuleData } from "@/app/utils/api";
import styles from "./RuleHeader.module.css";
import Link from "next/link";

export default function RuleHeader({
ruleInfo,
version = RULE_VERSION.inProduction,
}: {
ruleInfo: RuleInfo;
version?: string;
}) {
const pathname = usePathname();

export default function RuleHeader({ ruleInfo }: { ruleInfo: RuleInfo }) {
const [savedTitle, setSavedTitle] = useState("");
const [isEditingTitle, setIsEditingTitle] = useState(false);
const [currTitle, setCurrTitle] = useState<string>();
Expand Down Expand Up @@ -62,34 +46,17 @@ export default function RuleHeader({
}
};

const switchVersion = (versionToSwitchTo: RULE_VERSION) => {
// Use window.locaiton.href instead of router.push so that we can detect page changes for "unsaved changes" popup
window.location.href = `${pathname}?version=${versionToSwitchTo}&_=${new Date().getTime()}`;
};

const formatVersionText = (text: string) => {
const words = text.split(/(?=[A-Z])/);
return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
};

let versionColor = "green";
if (version === RULE_VERSION.draft) {
versionColor = "red";
} else if (version === RULE_VERSION.inReview) {
versionColor = "orange";
} else if (version === RULE_VERSION.inDev) {
versionColor = "purple";
}

if (currTitle === undefined) return null;

return (
<div className={styles.headerContainer} style={{ background: versionColor }}>
<Flex justify="space-between" className={styles.headerWrapper}>
<Flex gap="middle" align="center" flex={isEditingTitle ? "1" : "none"} className={styles.headerContent}>
<a href="/" className={styles.homeButton}>
<HomeOutlined />
</a>
<div className={styles.headerContainer}>
<div className={styles.homeWrapper}>
<Button type="link" href="/" className={styles.homeButton} icon={<HomeOutlined />}>
Home
</Button>
</div>
<Flex justify="space-between" align="center" className={styles.headerWrapper}>
<Flex gap="middle" align="center" flex={1} className={styles.headerContent}>
<Flex flex={1} vertical>
<h1
onClick={startEditingTitle}
Expand All @@ -109,42 +76,24 @@ export default function RuleHeader({
currTitle
)}
</h1>
<p className={styles.titleFilePath}>{ruleInfo.filepath}</p>
</Flex>
{isEditingTitle && (
<button className={styles.editButton} onClick={isEditingTitle ? doneEditingTitle : startEditingTitle}>
<CheckOutlined />
</button>
)}
<Tag color={versionColor}>{formatVersionText(version)}</Tag>
</Flex>
<Flex gap="small" align="end">
{version !== RULE_VERSION.draft && (
<Button onClick={() => switchVersion(RULE_VERSION.draft)} icon={<EditOutlined />} type="dashed">
Draft
</Button>
)}
{ruleInfo.reviewBranch && version !== RULE_VERSION.inReview && (
<Button onClick={() => switchVersion(RULE_VERSION.inReview)} icon={<EyeOutlined />} type="dashed">
In Review
</Button>
)}
{version !== RULE_VERSION.inDev && ruleInfo.isPublished && (
<Button onClick={() => switchVersion(RULE_VERSION.inDev)} icon={<CheckCircleOutlined />} type="dashed">
In Dev
</Button>
)}
{version !== RULE_VERSION.inProduction &&
ruleInfo.isPublished &&
process.env.NEXT_PUBLIC_IN_PRODUCTION === "true" && (
<Button
onClick={() => switchVersion(RULE_VERSION.inProduction)}
icon={<CheckCircleFilled />}
type="dashed"
>
In Production
</Button>
<Flex gap="small" align="end" vertical className={styles.rightContent}>
<p className={styles.titleFilePath}>{ruleInfo.filepath}</p>
{ruleInfo.name && process.env.NEXT_PUBLIC_KLAMM_URL && ruleInfo.isPublished && (
<Tooltip title="View rule details in KLAMM">
<Link href={`${process.env.NEXT_PUBLIC_KLAMM_URL}/rules/${ruleInfo.name}`} passHref target="_blank">
View In KLAMM <ExportOutlined />
</Link>
</Tooltip>
)}
</Flex>
</Flex>
</Flex>
</div>
Expand Down
27 changes: 27 additions & 0 deletions app/components/RuleManager/RuleManager.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
.rulesWrapper {
position: relative;
min-height: 500px;
z-index: 1;
}

.rulesWrapper::before {
content: "";
position: absolute;
left: 50%;
transform: translateX(-50%);
width: calc(100vw - 15px);
height: 103%;
background: transparent;
background-color: var(--version-color);
z-index: -1;
opacity: 0.1;
}

.rulesGraph {
border: 1px solid #d9d9d9;
}

.actionBar {
padding: 15px;
}

.rootLayout {
padding: 0;
}

Loading

0 comments on commit 8b69800

Please sign in to comment.