@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.
Files changed (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +15 -0
  4. package/dist/index.css +2183 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.LICENSE.txt +68 -0
  7. package/dist/index.js.map +1 -0
  8. package/lib/components/Alert.d.ts +7 -0
  9. package/lib/components/App.d.ts +2 -0
  10. package/lib/components/EditLink.d.ts +2 -0
  11. package/lib/components/EnvironmentDot.d.ts +8 -0
  12. package/lib/components/ExpandConditions.d.ts +6 -0
  13. package/lib/components/ExpandRuleSegments.d.ts +6 -0
  14. package/lib/components/Footer.d.ts +2 -0
  15. package/lib/components/Header.d.ts +2 -0
  16. package/lib/components/HistoryTimeline.d.ts +10 -0
  17. package/lib/components/LastModified.d.ts +2 -0
  18. package/lib/components/ListAttributes.d.ts +2 -0
  19. package/lib/components/ListFeatures.d.ts +2 -0
  20. package/lib/components/ListHistory.d.ts +2 -0
  21. package/lib/components/ListSegments.d.ts +2 -0
  22. package/lib/components/Markdown.d.ts +2 -0
  23. package/lib/components/PageContent.d.ts +4 -0
  24. package/lib/components/PageTitle.d.ts +5 -0
  25. package/lib/components/PrettyDate.d.ts +7 -0
  26. package/lib/components/SearchInput.d.ts +7 -0
  27. package/lib/components/ShowAttribute.d.ts +5 -0
  28. package/lib/components/ShowFeature.d.ts +10 -0
  29. package/lib/components/ShowSegment.d.ts +5 -0
  30. package/lib/components/Tabs.d.ts +10 -0
  31. package/lib/components/Tag.d.ts +6 -0
  32. package/lib/contexts/SearchIndexContext.d.ts +7 -0
  33. package/lib/hooks/searchIndexHook.d.ts +1 -0
  34. package/lib/index.d.ts +1 -0
  35. package/lib/utils/index.d.ts +23 -0
  36. package/package.json +62 -0
  37. package/public/favicon-128.png +0 -0
  38. package/public/index.html +15 -0
  39. package/src/components/Alert.tsx +22 -0
  40. package/src/components/App.tsx +129 -0
  41. package/src/components/EditLink.tsx +18 -0
  42. package/src/components/EnvironmentDot.tsx +48 -0
  43. package/src/components/ExpandConditions.tsx +84 -0
  44. package/src/components/ExpandRuleSegments.tsx +59 -0
  45. package/src/components/Footer.tsx +14 -0
  46. package/src/components/Header.tsx +62 -0
  47. package/src/components/HistoryTimeline.tsx +179 -0
  48. package/src/components/LastModified.tsx +29 -0
  49. package/src/components/ListAttributes.tsx +87 -0
  50. package/src/components/ListFeatures.tsx +95 -0
  51. package/src/components/ListHistory.tsx +17 -0
  52. package/src/components/ListSegments.tsx +81 -0
  53. package/src/components/Markdown.tsx +11 -0
  54. package/src/components/PageContent.tsx +9 -0
  55. package/src/components/PageTitle.tsx +23 -0
  56. package/src/components/PrettyDate.tsx +56 -0
  57. package/src/components/SearchInput.tsx +34 -0
  58. package/src/components/ShowAttribute.tsx +145 -0
  59. package/src/components/ShowFeature.tsx +490 -0
  60. package/src/components/ShowSegment.tsx +125 -0
  61. package/src/components/Tabs.tsx +46 -0
  62. package/src/components/Tag.tsx +13 -0
  63. package/src/contexts/SearchIndexContext.tsx +13 -0
  64. package/src/hooks/searchIndexHook.ts +9 -0
  65. package/src/index.css +8 -0
  66. package/src/index.tsx +12 -0
  67. package/src/utils/index.ts +203 -0
  68. package/tailwind.config.js +8 -0
  69. package/tsconfig.cjs.json +7 -0
  70. package/webpack.config.js +14 -0
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ interface AlertProps {
3
+ type: "success" | "warning";
4
+ children: React.ReactNode;
5
+ }
6
+ export declare function Alert(props: AlertProps): JSX.Element;
7
+ export {};
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function App(): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function EditLink(props: any): JSX.Element;
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ interface EnvironmentDotProps {
3
+ feature: any;
4
+ className?: string;
5
+ animate?: boolean;
6
+ }
7
+ export declare function EnvironmentDot(props: EnvironmentDotProps): JSX.Element;
8
+ export {};
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ interface ExpandConditionsProps {
3
+ conditions: any;
4
+ }
5
+ export declare function ExpandConditions(props: ExpandConditionsProps): JSX.Element;
6
+ export {};
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ interface ExpandRuleSegmentsProps {
3
+ segments: any;
4
+ }
5
+ export declare function ExpandRuleSegments(props: ExpandRuleSegmentsProps): JSX.Element;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function Footer(): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function Header(): JSX.Element;
@@ -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,2 @@
1
+ /// <reference types="react" />
2
+ export declare function LastModified(props: any): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function ListAttributes(): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function ListFeatures(): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function ListHistory(): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function ListSegments(): JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function Markdown(props: any): JSX.Element;
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ export declare function PageContent(props: {
3
+ children: React.ReactNode;
4
+ }): JSX.Element;
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ export declare function PageTitle(props: {
3
+ children: React.ReactNode;
4
+ className?: string;
5
+ }): JSX.Element;
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ interface PrettyDateProps {
3
+ date: string;
4
+ showTime?: boolean;
5
+ }
6
+ export declare function PrettyDate(props: PrettyDateProps): JSX.Element;
7
+ export {};
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ interface SearchInputProps {
3
+ value: string;
4
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
5
+ }
6
+ export declare function SearchInput(props: SearchInputProps): JSX.Element;
7
+ 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,10 @@
1
+ /// <reference types="react" />
2
+ interface Tab {
3
+ title: string;
4
+ to: string;
5
+ }
6
+ interface TabsProps {
7
+ tabs: Tab[];
8
+ }
9
+ export declare function Tabs(props: TabsProps): JSX.Element;
10
+ export {};
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ interface TagProps {
3
+ tag: string;
4
+ }
5
+ export declare function Tag(props: TagProps): JSX.Element;
6
+ export {};
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ import { SearchIndex } from "@featurevisor/types";
3
+ export interface SearchIndexProps {
4
+ isLoaded: boolean;
5
+ data?: SearchIndex;
6
+ }
7
+ export declare const SearchIndexContext: React.Context<SearchIndexProps>;
@@ -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
+ }