@featurevisor/site 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.css +2183 -0
- package/dist/index.js +3 -0
- package/dist/index.js.LICENSE.txt +68 -0
- package/dist/index.js.map +1 -0
- package/lib/components/Alert.d.ts +7 -0
- package/lib/components/App.d.ts +2 -0
- package/lib/components/EditLink.d.ts +2 -0
- package/lib/components/EnvironmentDot.d.ts +8 -0
- package/lib/components/ExpandConditions.d.ts +6 -0
- package/lib/components/ExpandRuleSegments.d.ts +6 -0
- package/lib/components/Footer.d.ts +2 -0
- package/lib/components/Header.d.ts +2 -0
- package/lib/components/HistoryTimeline.d.ts +10 -0
- package/lib/components/LastModified.d.ts +2 -0
- package/lib/components/ListAttributes.d.ts +2 -0
- package/lib/components/ListFeatures.d.ts +2 -0
- package/lib/components/ListHistory.d.ts +2 -0
- package/lib/components/ListSegments.d.ts +2 -0
- package/lib/components/Markdown.d.ts +2 -0
- package/lib/components/PageContent.d.ts +4 -0
- package/lib/components/PageTitle.d.ts +5 -0
- package/lib/components/PrettyDate.d.ts +7 -0
- package/lib/components/SearchInput.d.ts +7 -0
- package/lib/components/ShowAttribute.d.ts +5 -0
- package/lib/components/ShowFeature.d.ts +10 -0
- package/lib/components/ShowSegment.d.ts +5 -0
- package/lib/components/Tabs.d.ts +10 -0
- package/lib/components/Tag.d.ts +6 -0
- package/lib/contexts/SearchIndexContext.d.ts +7 -0
- package/lib/hooks/searchIndexHook.d.ts +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/utils/index.d.ts +23 -0
- package/package.json +62 -0
- package/public/favicon-128.png +0 -0
- package/public/index.html +15 -0
- package/src/components/Alert.tsx +22 -0
- package/src/components/App.tsx +129 -0
- package/src/components/EditLink.tsx +18 -0
- package/src/components/EnvironmentDot.tsx +48 -0
- package/src/components/ExpandConditions.tsx +84 -0
- package/src/components/ExpandRuleSegments.tsx +59 -0
- package/src/components/Footer.tsx +14 -0
- package/src/components/Header.tsx +62 -0
- package/src/components/HistoryTimeline.tsx +179 -0
- package/src/components/LastModified.tsx +29 -0
- package/src/components/ListAttributes.tsx +87 -0
- package/src/components/ListFeatures.tsx +95 -0
- package/src/components/ListHistory.tsx +17 -0
- package/src/components/ListSegments.tsx +81 -0
- package/src/components/Markdown.tsx +11 -0
- package/src/components/PageContent.tsx +9 -0
- package/src/components/PageTitle.tsx +23 -0
- package/src/components/PrettyDate.tsx +56 -0
- package/src/components/SearchInput.tsx +34 -0
- package/src/components/ShowAttribute.tsx +145 -0
- package/src/components/ShowFeature.tsx +490 -0
- package/src/components/ShowSegment.tsx +125 -0
- package/src/components/Tabs.tsx +46 -0
- package/src/components/Tag.tsx +13 -0
- package/src/contexts/SearchIndexContext.tsx +13 -0
- package/src/hooks/searchIndexHook.ts +9 -0
- package/src/index.css +8 -0
- package/src/index.tsx +12 -0
- package/src/utils/index.ts +203 -0
- package/tailwind.config.js +8 -0
- package/tsconfig.cjs.json +7 -0
- package/webpack.config.js +14 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface HistoryTimelineProps {
|
|
3
|
+
className?: string;
|
|
4
|
+
filter?: (entry: any) => boolean;
|
|
5
|
+
entityType?: string;
|
|
6
|
+
entityKey?: string;
|
|
7
|
+
showTime?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function HistoryTimeline(props: HistoryTimelineProps): JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare function DisplayAttributeOverview(): JSX.Element;
|
|
3
|
+
export declare function DisplayAttributeUsage(): JSX.Element;
|
|
4
|
+
export declare function DisplayAttributeHistory(): JSX.Element;
|
|
5
|
+
export declare function ShowAttribute(props: any): JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare function DisplayFeatureOverview(): JSX.Element;
|
|
3
|
+
export declare function DisplayFeatureForceTable(): JSX.Element;
|
|
4
|
+
export declare function DisplayFeatureForce(): JSX.Element;
|
|
5
|
+
export declare function DisplayFeatureRulesTable(): JSX.Element;
|
|
6
|
+
export declare function DisplayFeatureRules(): JSX.Element;
|
|
7
|
+
export declare function DisplayFeatureVariations(): JSX.Element;
|
|
8
|
+
export declare function DisplayFeatureVariablesSchema(): JSX.Element;
|
|
9
|
+
export declare function DisplayFeatureHistory(): JSX.Element;
|
|
10
|
+
export declare function ShowFeature(props: any): JSX.Element;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare function DisplaySegmentOverview(): JSX.Element;
|
|
3
|
+
export declare function DisplaySegmentUsage(): JSX.Element;
|
|
4
|
+
export declare function DisplaySegmentHistory(): JSX.Element;
|
|
5
|
+
export declare function ShowSegment(props: any): JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useSearchIndex(): import("../contexts/SearchIndexContext").SearchIndexProps;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SearchIndex } from "@featurevisor/types";
|
|
2
|
+
export interface Query {
|
|
3
|
+
keyword: string;
|
|
4
|
+
tags: string[];
|
|
5
|
+
environments: string[];
|
|
6
|
+
archived?: boolean;
|
|
7
|
+
capture?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function getQueryFromString(q: string): Query;
|
|
10
|
+
export declare function isEnabledInEnvironment(feature: any, environment: string): boolean;
|
|
11
|
+
export declare function isEnabledInAnyEnvironment(feature: any): boolean;
|
|
12
|
+
export declare function getFeaturesByQuery(query: Query, data: SearchIndex): (import("@featurevisor/types").ParsedFeature & {
|
|
13
|
+
lastModified?: import("@featurevisor/types").LastModified;
|
|
14
|
+
})[];
|
|
15
|
+
export declare function getAttributesByQuery(query: Query, data: SearchIndex): (import("@featurevisor/types").Attribute & {
|
|
16
|
+
lastModified?: import("@featurevisor/types").LastModified;
|
|
17
|
+
usedInSegments: string[];
|
|
18
|
+
usedInFeatures: string[];
|
|
19
|
+
})[];
|
|
20
|
+
export declare function getSegmentsByQuery(query: Query, data: SearchIndex): (import("@featurevisor/types").Segment & {
|
|
21
|
+
lastModified?: import("@featurevisor/types").LastModified;
|
|
22
|
+
usedInFeatures: string[];
|
|
23
|
+
})[];
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@featurevisor/site",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Static site for Featurevisor",
|
|
5
|
+
"main": "dist",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"lint": "echo 'not linting in this package yet'",
|
|
8
|
+
"transpile": "echo 'Nothing to transpile in this package'",
|
|
9
|
+
"dist:js": "webpack --config ./webpack.config.js",
|
|
10
|
+
"dist:css": "tailwindcss -i ./src/index.css -o ./dist/index.css",
|
|
11
|
+
"dist": "npm run dist:js && npm run dist:css",
|
|
12
|
+
"build": "npm run dist",
|
|
13
|
+
"test": "echo 'Nothing to test in types package'"
|
|
14
|
+
},
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Fahad Heylaal",
|
|
17
|
+
"url": "https://fahad19.com"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://featurevisor.com",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"featurevisor",
|
|
22
|
+
"feature",
|
|
23
|
+
"features",
|
|
24
|
+
"flags",
|
|
25
|
+
"feature flags",
|
|
26
|
+
"feature toggles",
|
|
27
|
+
"feature management",
|
|
28
|
+
"experimentation",
|
|
29
|
+
"experiment",
|
|
30
|
+
"experiments"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/fahad19/featurevisor.git"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public",
|
|
38
|
+
"registry": "https://registry.npmjs.org/"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/fahad19/featurevisor/issues"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@heroicons/react": "^2.0.17",
|
|
46
|
+
"@tailwindcss/forms": "^0.5.3",
|
|
47
|
+
"@tailwindcss/typography": "^0.5.9",
|
|
48
|
+
"@types/react": "^18.0.29",
|
|
49
|
+
"@types/react-dom": "^18.0.11",
|
|
50
|
+
"css-loader": "^6.7.3",
|
|
51
|
+
"react": "^18.2.0",
|
|
52
|
+
"react-dom": "^18.2.0",
|
|
53
|
+
"react-markdown": "^8.0.6",
|
|
54
|
+
"react-router-dom": "^6.10.0",
|
|
55
|
+
"style-loader": "^3.3.2",
|
|
56
|
+
"tailwindcss": "^3.3.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@featurevisor/types": "^0.8.0"
|
|
60
|
+
},
|
|
61
|
+
"gitHead": "fe803f91b34ee7aa98a384769ba1fc181fb9c64e"
|
|
62
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Featurevisor Status</title>
|
|
6
|
+
<link rel="stylesheet" href="./index.css" />
|
|
7
|
+
<link rel="icon" type="image/png" href="./favicon-128.png" />
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
|
|
13
|
+
<script src="./index.js"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
interface AlertProps {
|
|
4
|
+
type: "success" | "warning";
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function Alert(props: AlertProps) {
|
|
9
|
+
if (props.type === "success") {
|
|
10
|
+
return (
|
|
11
|
+
<p className="mx-6 block rounded border-2 border-green-300 bg-green-200 p-3 text-sm text-gray-600">
|
|
12
|
+
{props.children}
|
|
13
|
+
</p>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<p className="mx-6 block rounded border-2 border-orange-300 bg-orange-200 p-3 text-sm text-gray-600">
|
|
19
|
+
{props.children}
|
|
20
|
+
</p>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { Routes, Route, redirect } from "react-router-dom";
|
|
4
|
+
|
|
5
|
+
import { Header } from "./Header";
|
|
6
|
+
import { Footer } from "./Footer";
|
|
7
|
+
import { Alert } from "./Alert";
|
|
8
|
+
|
|
9
|
+
import { ListFeatures } from "./ListFeatures";
|
|
10
|
+
import { ListSegments } from "./ListSegments";
|
|
11
|
+
import { ListAttributes } from "./ListAttributes";
|
|
12
|
+
import { ListHistory } from "./ListHistory";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
DisplayAttributeOverview,
|
|
16
|
+
DisplayAttributeUsage,
|
|
17
|
+
DisplayAttributeHistory,
|
|
18
|
+
ShowAttribute,
|
|
19
|
+
} from "./ShowAttribute";
|
|
20
|
+
import {
|
|
21
|
+
DisplaySegmentOverview,
|
|
22
|
+
DisplaySegmentUsage,
|
|
23
|
+
DisplaySegmentHistory,
|
|
24
|
+
ShowSegment,
|
|
25
|
+
} from "./ShowSegment";
|
|
26
|
+
import {
|
|
27
|
+
DisplayFeatureOverview,
|
|
28
|
+
DisplayFeatureVariations,
|
|
29
|
+
DisplayFeatureVariablesSchema,
|
|
30
|
+
DisplayFeatureRules,
|
|
31
|
+
DisplayFeatureRulesTable,
|
|
32
|
+
DisplayFeatureForce,
|
|
33
|
+
DisplayFeatureForceTable,
|
|
34
|
+
DisplayFeatureHistory,
|
|
35
|
+
ShowFeature,
|
|
36
|
+
} from "./ShowFeature";
|
|
37
|
+
|
|
38
|
+
import { SearchIndexContext } from "../contexts/SearchIndexContext";
|
|
39
|
+
import { SearchIndex } from "@featurevisor/types";
|
|
40
|
+
|
|
41
|
+
export function App() {
|
|
42
|
+
const [fetchedSearchIndex, setSearchIndex] = React.useState(undefined);
|
|
43
|
+
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
fetch("/search-index.json")
|
|
46
|
+
.then((response) => response.json())
|
|
47
|
+
.then((data) => {
|
|
48
|
+
setSearchIndex(data);
|
|
49
|
+
});
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
const environmentKeys = fetchedSearchIndex
|
|
53
|
+
? Object.keys((fetchedSearchIndex as SearchIndex).entities.features[0].environments).sort()
|
|
54
|
+
: [];
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div>
|
|
58
|
+
<Header />
|
|
59
|
+
|
|
60
|
+
<main>
|
|
61
|
+
{!fetchedSearchIndex && <Alert type="warning">Loading...</Alert>}
|
|
62
|
+
|
|
63
|
+
{fetchedSearchIndex && (
|
|
64
|
+
<SearchIndexContext.Provider value={{ isLoaded: true, data: fetchedSearchIndex }}>
|
|
65
|
+
<Routes>
|
|
66
|
+
{/* @TODO: try redirecting to /features */}
|
|
67
|
+
<Route path="/" element={<ListFeatures />} />
|
|
68
|
+
|
|
69
|
+
<Route path="features">
|
|
70
|
+
<Route index element={<ListFeatures />} />
|
|
71
|
+
|
|
72
|
+
<Route path=":featureKey" element={<ShowFeature />}>
|
|
73
|
+
<Route index element={<DisplayFeatureOverview />} />
|
|
74
|
+
<Route path="variations" element={<DisplayFeatureVariations />} />
|
|
75
|
+
<Route path="variables" element={<DisplayFeatureVariablesSchema />} />
|
|
76
|
+
<Route path="rules" element={<DisplayFeatureRules />}>
|
|
77
|
+
<Route path=":environmentKey" element={<DisplayFeatureRulesTable />} />
|
|
78
|
+
<Route
|
|
79
|
+
path="*"
|
|
80
|
+
loader={({ params }) =>
|
|
81
|
+
/* @TODO: fix redirection */
|
|
82
|
+
redirect(`/features/${params.featureKey}/rules/${environmentKeys[0]}`)
|
|
83
|
+
}
|
|
84
|
+
/>
|
|
85
|
+
</Route>
|
|
86
|
+
<Route path="force" element={<DisplayFeatureForce />}>
|
|
87
|
+
<Route path=":environmentKey" element={<DisplayFeatureForceTable />} />
|
|
88
|
+
<Route
|
|
89
|
+
path="*"
|
|
90
|
+
loader={({ params }) =>
|
|
91
|
+
/* @TODO: fix redirection */
|
|
92
|
+
redirect(`/features/${params.featureKey}/force/${environmentKeys[0]}`)
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
95
|
+
</Route>
|
|
96
|
+
<Route path="history" element={<DisplayFeatureHistory />} />
|
|
97
|
+
</Route>
|
|
98
|
+
</Route>
|
|
99
|
+
|
|
100
|
+
<Route path="segments">
|
|
101
|
+
<Route index element={<ListSegments />} />
|
|
102
|
+
|
|
103
|
+
<Route path=":segmentKey" element={<ShowSegment />}>
|
|
104
|
+
<Route index element={<DisplaySegmentOverview />} />
|
|
105
|
+
<Route path="usage" element={<DisplaySegmentUsage />} />
|
|
106
|
+
<Route path="history" element={<DisplaySegmentHistory />} />
|
|
107
|
+
</Route>
|
|
108
|
+
</Route>
|
|
109
|
+
|
|
110
|
+
<Route path="attributes">
|
|
111
|
+
<Route index element={<ListAttributes />} />
|
|
112
|
+
|
|
113
|
+
<Route path=":attributeKey" element={<ShowAttribute />}>
|
|
114
|
+
<Route index element={<DisplayAttributeOverview />} />
|
|
115
|
+
<Route path="usage" element={<DisplayAttributeUsage />} />
|
|
116
|
+
<Route path="history" element={<DisplayAttributeHistory />} />
|
|
117
|
+
</Route>
|
|
118
|
+
</Route>
|
|
119
|
+
|
|
120
|
+
<Route path="history" element={<ListHistory />} />
|
|
121
|
+
</Routes>
|
|
122
|
+
</SearchIndexContext.Provider>
|
|
123
|
+
)}
|
|
124
|
+
</main>
|
|
125
|
+
|
|
126
|
+
<Footer />
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/20/solid";
|
|
4
|
+
|
|
5
|
+
export function EditLink(props: any) {
|
|
6
|
+
const { path } = props;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<a
|
|
10
|
+
href={props.url}
|
|
11
|
+
target="_blank"
|
|
12
|
+
className="w-18 h-18 relative top-0.5 float-right inline-block rounded-md bg-slate-100 px-2 py-1 text-lg text-gray-500 hover:bg-slate-200"
|
|
13
|
+
>
|
|
14
|
+
<span className="pr-1 text-sm font-normal">Edit</span>{" "}
|
|
15
|
+
<ArrowTopRightOnSquareIcon className="inline-block h-5 w-5 text-sm text-gray-400" />
|
|
16
|
+
</a>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { isEnabledInAnyEnvironment, isEnabledInEnvironment } from "../utils";
|
|
4
|
+
|
|
5
|
+
interface EnvironmentDotProps {
|
|
6
|
+
feature: any;
|
|
7
|
+
className?: string;
|
|
8
|
+
animate?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function EnvironmentDot(props: EnvironmentDotProps) {
|
|
12
|
+
const enabledInProduction = isEnabledInEnvironment(
|
|
13
|
+
props.feature,
|
|
14
|
+
"production"
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (enabledInProduction) {
|
|
18
|
+
return (
|
|
19
|
+
<div className={props.className || ""}>
|
|
20
|
+
<span className="relative flex h-3 w-3">
|
|
21
|
+
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
|
|
22
|
+
|
|
23
|
+
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
|
|
24
|
+
</span>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const enabledInOtherEnvironments = isEnabledInAnyEnvironment(props.feature);
|
|
30
|
+
|
|
31
|
+
if (enabledInOtherEnvironments) {
|
|
32
|
+
return (
|
|
33
|
+
<div className={props.className || ""}>
|
|
34
|
+
<span className="relative flex h-3 w-3">
|
|
35
|
+
<span className="relative inline-flex h-3 w-3 rounded-full bg-amber-500"></span>
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={props.className || ""}>
|
|
43
|
+
<span className="relative flex h-3 w-3">
|
|
44
|
+
<span className="relative inline-flex h-3 w-3 rounded-full bg-slate-300"></span>
|
|
45
|
+
</span>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
interface ConditionValueProps {
|
|
4
|
+
value: any;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function ConditionValue(props: ConditionValueProps) {
|
|
8
|
+
const { value } = props;
|
|
9
|
+
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
return <span className="text-sm">{value}</span>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
15
|
+
return (
|
|
16
|
+
<code className="rounded bg-gray-100 px-2 py-1 text-sm text-red-400">
|
|
17
|
+
{JSON.stringify(value)}
|
|
18
|
+
</code>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<pre className="rounded bg-gray-100 px-2 py-1 text-sm text-red-400">
|
|
24
|
+
<code>{JSON.stringify(value, null, 2)}</code>
|
|
25
|
+
</pre>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ExpandConditionsProps {
|
|
30
|
+
conditions: any;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ExpandConditions(props: ExpandConditionsProps) {
|
|
34
|
+
const { conditions } = props;
|
|
35
|
+
|
|
36
|
+
if (Array.isArray(conditions)) {
|
|
37
|
+
return (
|
|
38
|
+
<ul className="relative list-inside list-disc pl-5">
|
|
39
|
+
{conditions.map((condition: any, index: number) => {
|
|
40
|
+
return (
|
|
41
|
+
<li key={index} className="py-1">
|
|
42
|
+
{typeof condition.attribute !== "undefined" ? (
|
|
43
|
+
<>
|
|
44
|
+
<a href="#" className="text-sm">
|
|
45
|
+
{condition.attribute}
|
|
46
|
+
</a>{" "}
|
|
47
|
+
<span className="text-sm font-semibold">
|
|
48
|
+
{condition.operator}
|
|
49
|
+
</span>{" "}
|
|
50
|
+
<ConditionValue value={condition.value} />
|
|
51
|
+
</>
|
|
52
|
+
) : (
|
|
53
|
+
<ExpandConditions conditions={condition} />
|
|
54
|
+
)}
|
|
55
|
+
</li>
|
|
56
|
+
);
|
|
57
|
+
})}
|
|
58
|
+
</ul>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof conditions === "object") {
|
|
63
|
+
const type = Object.keys(conditions)[0];
|
|
64
|
+
|
|
65
|
+
let classes = "bg-green-300 text-green-700";
|
|
66
|
+
|
|
67
|
+
if (type === "or") {
|
|
68
|
+
classes = "bg-yellow-300 text-yellow-700";
|
|
69
|
+
} else if (type === "not") {
|
|
70
|
+
classes = "bg-red-300 text-red-700";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<span className={`rounded-full px-2 py-1 text-sm font-bold ${classes}`}>
|
|
76
|
+
{type}:
|
|
77
|
+
</span>
|
|
78
|
+
<ExpandConditions conditions={conditions[type]} />
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return <span>n/a</span>; // should never happen
|
|
84
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
interface ExpandRuleSegmentsProps {
|
|
4
|
+
segments: any;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function ExpandRuleSegments(props: ExpandRuleSegmentsProps) {
|
|
8
|
+
const { segments } = props;
|
|
9
|
+
|
|
10
|
+
if (segments === "*") {
|
|
11
|
+
return (
|
|
12
|
+
<pre>
|
|
13
|
+
<code className="rounded bg-gray-100 px-2 py-1 text-red-400">*</code>{" "}
|
|
14
|
+
(everyone)
|
|
15
|
+
</pre>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (Array.isArray(segments)) {
|
|
20
|
+
return (
|
|
21
|
+
<ul className="relative list-inside list-disc pl-5">
|
|
22
|
+
{segments.map((segment: any, index: number) => {
|
|
23
|
+
return (
|
|
24
|
+
<li key={index} className="py-1">
|
|
25
|
+
{typeof segment === "string" ? (
|
|
26
|
+
segment
|
|
27
|
+
) : (
|
|
28
|
+
<ExpandRuleSegments segments={segment} />
|
|
29
|
+
)}
|
|
30
|
+
</li>
|
|
31
|
+
);
|
|
32
|
+
})}
|
|
33
|
+
</ul>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof segments === "object") {
|
|
38
|
+
const type = Object.keys(segments)[0];
|
|
39
|
+
|
|
40
|
+
let classes = "bg-green-300 text-green-700";
|
|
41
|
+
|
|
42
|
+
if (type === "or") {
|
|
43
|
+
classes = "bg-yellow-300 text-yellow-700";
|
|
44
|
+
} else if (type === "not") {
|
|
45
|
+
classes = "bg-red-300 text-red-700";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<span className={`rounded-full px-2 py-1 text-sm font-bold ${classes}`}>
|
|
51
|
+
{type}:
|
|
52
|
+
</span>
|
|
53
|
+
<ExpandRuleSegments segments={segments[type]} />
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return <span>n/a</span>; // should never happen
|
|
59
|
+
}
|