Skip to content
Snippets Groups Projects
Verified Commit fb3ff1c8 authored by Karel van Klink's avatar Karel van Klink :smiley_cat:
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
This folder contains a manifest file that holds the metainfo about the available modules and components in the custom
package.
An example that shows some extra pages and subscription detail plugins that are included from the custom folder:
```JSON
{
"name": "SURF",
"customPages": [
{
"name": "prefixes",
"path": "pages",
"file": "Prefixes",
"component": "Prefixes",
"showInMenu": true
},
{
"name": "tickets",
"path": "pages",
"file": "ServiceTickets",
"component": "ServiceTickets",
"showInMenu": true
},
{
"name": "tickets/create",
"path": "pages",
"file": "CreateServiceTicket",
"component": "CreateServiceTicket",
"showInMenu": false
}
],
"disabledRoutes": ["/YOUR_DISABLED_ROUTE_HERE"],
"disabledMenuItems": [],
"plugins": {
"subscriptionDetailPlugins": ["RenderDiagram", "RenderExternalLinks", "RenderDienstafname"]
}
}
```
import { BaseApiClient } from "api";
import { Organization, ServicePortFilterItem, ServicePortSubscription } from "utils/types";
abstract class CustomApiClientInterface extends BaseApiClient {
abstract portSubscriptions: (
tagList?: string[],
statusList?: string[],
productList?: string[]
) => Promise<ServicePortSubscription[]>;
abstract organisations: () => Promise<Organization[] | undefined>;
abstract locationCodes: () => Promise<string[] | undefined>;
abstract getPortSubscriptionsForNode: (id: string) => Promise<ServicePortFilterItem[]>;
}
export class CustomApiClient extends CustomApiClientInterface {
portSubscriptions = (...[,,]): Promise<ServicePortSubscription[]> => {
return this.fetchJson("non-existent-url");
};
organisations = async (): Promise<Organization[] | undefined> => {
return undefined;
};
locationCodes = async (): Promise<string[] | undefined> => {
return undefined;
};
getPortSubscriptionsForNode = (_: string): Promise<ServicePortFilterItem[]> => {
return this.fetchJson("non-existent-url");
};
}
/*
* Copyright 2019-2023 SURF.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { EuiButtonIcon, EuiCopy, EuiFlexGroup, EuiFlexItem } from "@elastic/eui";
import React, { useContext, useState } from "react";
import { FormattedMessage } from "react-intl";
import { useQuery } from "react-query";
import ApplicationContext from "utils/ApplicationContext";
function SubscriptionInstanceValueRow({
label,
value,
isSubscriptionValue,
isDeleted,
isExternalLinkValue,
toggleCollapsed,
type,
children,
}: React.PropsWithChildren<{
label: string;
value: string;
isSubscriptionValue: boolean;
isDeleted: boolean;
isExternalLinkValue: boolean;
toggleCollapsed: () => void;
type: string;
}>) {
const icon = children ? "minus" : "plus";
const { apiClient, theme } = useContext(ApplicationContext);
const { isLoading: subscriptionIsLoading, error: subscriptionError, data: subscriptionData } = useQuery(
["subscription", { id: isSubscriptionValue ? value : "disabled" }],
() => apiClient.subscriptionsDetailWithModel(value),
{
enabled: isSubscriptionValue,
}
);
const subscriptionLink =
isSubscriptionValue && !subscriptionIsLoading && !subscriptionError
? `${subscriptionData?.description} (${value})`
: value;
return (
<tbody className={theme}>
<tr>
<td>{label.toUpperCase()}</td>
<td colSpan={isDeleted ? 1 : 2}>
<div className="resource-type">
{isExternalLinkValue && !isDeleted && (
<i className={`fa fa-${icon}-circle`} onClick={toggleCollapsed} />
)}
{isSubscriptionValue && (
<EuiFlexGroup alignItems={"center"}>
<EuiFlexItem grow={false}>
<a target="_blank" rel="noopener noreferrer" href={`/subscriptions/${value}`}>
{subscriptionLink}
</a>
</EuiFlexItem>
<EuiCopy textToCopy={value}>
{(copy) => <EuiButtonIcon iconType={"copyClipboard"} onClick={copy} />}
</EuiCopy>
</EuiFlexGroup>
)}
{!isSubscriptionValue && <span>{value.toString()}</span>}
</div>
</td>
{isDeleted && (
<td>
<em className="error">
<FormattedMessage id={`subscription.${type}.removed`} />
</em>
</td>
)}
</tr>
{children && isExternalLinkValue && !isDeleted && (
<tr className="related-subscription">
<td className="whitespace" />
<td className="related-subscription-values" colSpan={2}>
{children}
</td>
</tr>
)}
</tbody>
);
}
interface IProps {
label: string;
value: string;
}
export default function SubscriptionInstanceValue({ label, value }: IProps) {
const [collapsed, setCollapsed] = useState(true);
const [data] = useState<any | null | undefined>(undefined);
const isSubscriptionValue = label.endsWith("subscription_id");
const isExternalLinkValue = false;
const isDeleted = isExternalLinkValue && data === null;
return (
<SubscriptionInstanceValueRow
label={label}
value={value}
isSubscriptionValue={isSubscriptionValue}
isDeleted={isDeleted}
isExternalLinkValue={isExternalLinkValue}
toggleCollapsed={() => setCollapsed(!collapsed)}
type={label}
>
{!!data && !collapsed && data}
</SubscriptionInstanceValueRow>
);
}
{
"name": "standalone",
"customPages": [],
"disabledRoutes": ["non-existent-url"],
"disabledMenuItems": [],
"subscriptionDetailItems": [],
"plugins": {}
}
plugin folder
import {
AcceptField,
BoolField,
DateField,
DividerField,
LabelField,
ListField,
LocationCodeField,
LongTextField,
NestField,
NumField,
OptGroupField,
OrganisationField,
ProductField,
RadioField,
SelectField,
SubscriptionField,
SubscriptionSummaryField,
SummaryField,
TextField,
} from "lib/uniforms-surfnet/src";
import { Context, GuaranteedProps } from "uniforms";
import { AutoField } from "uniforms-unstyled";
export function autoFieldFunction(props: GuaranteedProps<unknown> & Record<string, any>, uniforms: Context<unknown>) {
const { allowedValues, checkboxes, fieldType, field } = props;
const { format } = field;
switch (fieldType) {
case Number:
// If you need custom field for numeric resource types add a switch here
break;
case Object:
switch (format) {
case "optGroup":
return OptGroupField;
}
break;
case String:
switch (format) {
case "subscriptionId":
return SubscriptionField;
case "productId":
return ProductField;
case "locationCode":
return LocationCodeField;
case "organisationId":
return OrganisationField;
case "long":
return LongTextField;
case "label":
return LabelField;
case "divider":
return DividerField;
case "summary":
return SummaryField;
case "subscription":
return SubscriptionSummaryField;
case "accept":
return AcceptField;
}
break;
}
if (allowedValues && format !== "accept") {
if (checkboxes && fieldType !== Array) {
return RadioField;
} else {
return SelectField;
}
} else {
switch (fieldType) {
case Array:
return ListField;
case Boolean:
return BoolField;
case Date:
return DateField;
case Number:
return NumField;
case Object:
return NestField;
case String:
return TextField;
}
}
// Todo React upgrade: fix uniform types
// @ts-ignore
return AutoField.defaultComponentDetector(props, uniforms);
}
logo.svg 0 → 100644
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 635.59 276.02">
<defs>
<style>
.cls-1 {
fill: #003f5f;
}
.cls-2 {
fill: #ed1556;
}
</style>
</defs>
<g>
<path class="cls-1" d="m132.19,143.2c4.55-3.78,8.66-5.61,12.16-5.45,7.54.32,10.21,5.61,10.55,8.79-1.71.6-35.66,12.01-37.1,12.49-.52-.54-1.11-1.18-1.85-1.97,1.8-1.51,16.24-13.86,16.24-13.86Z"/>
<path class="cls-1" d="m7.32,215.37c0,38.18,16.81,57.56,49.96,57.56,21.88,0,35.17-9.67,35.71-10.09l1.06-.78v-52.49h-45.73v14.66h27.11v29.73c-2.71,1.58-9.43,4.33-19.17,4.33-19.6,0-29.14-14.04-29.14-42.91,0-16.53,4.71-36.2,27.09-36.2,14.9,0,20.2,8.65,20.2,16.79v2.59h20.32v-2.59c0-18.82-16.35-31.46-40.7-31.46-29.69,0-46.71,18.56-46.71,50.87Z"/>
<path class="cls-1" d="m166.28,166.08h-59.94v105.24h64.09v-14.63h-45.47v-33.04h41.88v-14.63h-41.88v-28.29h43.94v-14.64h-2.62Z"/>
<path class="cls-1" d="m436.43,166.08h-91.15v77.96c-7.45-12.93-44.97-77.96-44.97-77.96h-21.27v105.24h17.91v-77.66c7.46,12.88,44.97,77.66,44.97,77.66h21.28s0-85.64,0-90.59h28.91v90.59h18.61v-90.59h28.32v-14.64h-2.62Z"/>
<path class="cls-1" d="m234.9,166.08h-19.92l-39.72,105.24h18.97s10.37-27.83,11.51-30.84h38.6c1.15,2.99,11.85,30.84,11.85,30.84h18.97l-40.27-105.24Zm-23.67,59.72c1.79-5.13,10.63-30.33,13.8-39.4,3.17,9.06,12.02,34.27,13.82,39.4h-27.62Z"/>
</g>
<path class="cls-1" d="m613.53,65.92c-69.24-85.72-349.66,35.38-425.07,67.26-5.44,2.28-12.38,1.78-16.32-6.04,2.97,7.54,9.26,10.63,16.96,7.54,100.26-40.06,341.96-126.93,402.44-47.6,27.37,35.87,19.7,80.18-10.25,142.23-1.43,2.69-2.35,4.44-2.56,4.79-.09.18-.2.31-.29.47-.1.16-.2.35-.29.49-2.3,3.74-5.23,5.79-8.18,6.62,3.46-.18,7.14-1.87,9.96-6.26.73-1.14,1.69-2.72,2.84-4.65v-.02c44.36-73.83,59.65-129.06,30.76-164.83Z"/>
<path class="cls-2" d="m560.84,238.73c-1.01-.84-13.42-12.02-25.87-23.74C469.13,152.97,270.69-38.96,183.94,11.44c-24.45,14.21-28.66,55.54-13.51,110.71.04.11.07.2.1.29v.02c.2.73.41,1.48.63,2.19,1.64,5.74,5.82,9.37,10.82,9.7-3.85-.99-6.95-3.84-8.57-8.24-.25-.59-.46-1.23-.64-1.83-.26-.96-.5-1.94-.78-2.96h-.02c-.15-.64-.29-1.27-.4-1.91-8.07-46.76,1.62-77.53,20.54-89.95,69.51-45.65,236.71,98.28,320.47,170.75,19.19,16.61,40.78,35.07,47.58,40.18,9.63,7.27,17.43-1.07,19.41-4.45-2.98,4.43-11.24,9.01-18.74,2.8Z"/>
</svg>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment