@material-ui/core#AccordionDetails TypeScript Examples
The following examples show how to use
@material-ui/core#AccordionDetails.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: CollapsibleAlert.tsx From abacus with GNU General Public License v2.0 | 6 votes |
export default function CollapsibleAlert({
id,
severity,
summary,
className,
children,
}: {
id: string
severity: Color
summary?: React.ReactNode
className?: string
children?: React.ReactNode
}): JSX.Element {
const classes = useStyles()
const severityClassMap = new Map([
['success', 'MuiAlert-standardSuccess'],
['info', 'MuiAlert-standardInfo'],
['warning', 'MuiAlert-standardWarning'],
['error', 'MuiAlert-standardError'],
])
return (
<Alert severity={severity} className={className}>
<Accordion className={clsx(severityClassMap.get(severity), classes.accordionRoot)}>
<AccordionSummary
classes={{ root: classes.accordionSummary }}
expandIcon={<ExpandMoreIcon />}
aria-controls={`${id}-content`}
id={id}
>
{summary}
</AccordionSummary>
<AccordionDetails>
<div>{children}</div>
</AccordionDetails>
</Accordion>
</Alert>
)
}
Example #2
Source File: ErrorMessage.tsx From TidGi-Desktop with Mozilla Public License 2.0 | 6 votes |
export function WikiErrorMessages(props: IWikiErrorMessagesProps): JSX.Element {
const { t } = useTranslation();
const wikiLogs = usePromiseValue(async () => await window.service.wiki.getWikiLogs(props.activeWorkspace.wikiFolderLocation));
if (wikiLogs !== undefined) {
return (
<WikiErrorMessagesContainer>
<Accordion>
<AccordionSummary>
<Typography align="left" variant="h5">
{t('Error.WikiRuntimeError')} {t('ClickForDetails')}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography align="left" variant="body2">
{t('Error.WikiRuntimeErrorDescription')}
</Typography>
<Button variant="outlined" onClick={async () => await window.service.native.open(wikiLogs.filePath, true)}>
{t('Preference.OpenLogFolder')}
</Button>
<div>
<pre>
<code>{wikiLogs.content}</code>
</pre>
</div>
</AccordionDetails>
</Accordion>
</WikiErrorMessagesContainer>
);
}
return <div />;
}
Example #3
Source File: ServerListItemGroup.tsx From shadowsocks-electron with GNU General Public License v3.0 | 6 votes |
StyledAccordionDetails = withStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
flexDirection: 'column',
paddingLeft: 0,
paddingRight: 0,
margin: 'auto',
backgroundColor: theme.palette.type === "dark" ? '#383838' : '#fdfdfd',
}
}),
)(AccordionDetails)
Example #4
Source File: MyAccordion.tsx From clearflask with Apache License 2.0 | 6 votes |
export default function MyAccordion(props: {
name?: React.ReactNode;
} & React.ComponentProps<typeof Accordion>) {
const classes = useStyles();
const { children, name, ...AccordionProps } = props;
return (
<Accordion
TransitionProps={{
appear: true,
}}
classes={{
root: classes.accordion,
}}
elevation={0}
{...AccordionProps}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>
{name}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography className={classes.children}>
{children}
</Typography>
</AccordionDetails>
</Accordion>
);
}
Example #5
Source File: with-subheader.tsx From react-component-library with BSD 3-Clause "New" or "Revised" License | 6 votes |
accordion = ( <Accordion> <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1a-content" id="panel1a-header"> <Typography>Expansion Panel 1</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. </Typography> </AccordionDetails> </Accordion> )
Example #6
Source File: Accordion.tsx From backstage with Apache License 2.0 | 6 votes |
Accordion = (props: PropsWithChildren<AccordionProps>) => {
const classes = useStyles();
return (
<MuiAccordion
disabled={props.disabled}
TransitionProps={{ unmountOnExit: props.unmountOnExit ?? false }}
>
<MuiAccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`${props.id}-content`}
id={`${props.id}-header`}
>
<Typography className={classes.heading}>{props.heading}</Typography>
<Typography className={classes.secondaryHeading}>
{props.secondaryHeading}
</Typography>
</MuiAccordionSummary>
<AccordionDetails>{props.children}</AccordionDetails>
</MuiAccordion>
);
}
Example #7
Source File: StatefulSetsAccordions.tsx From backstage with Apache License 2.0 | 6 votes |
StatefulSetAccordion = ({
statefulset,
ownedPods,
matchingHpa,
}: StatefulSetAccordionProps) => {
const podNamesWithErrors = useContext(PodNamesWithErrorsContext);
const podsWithErrors = ownedPods.filter(p =>
podNamesWithErrors.has(p.metadata?.name ?? ''),
);
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<StatefulSetSummary
statefulset={statefulset}
numberOfCurrentPods={ownedPods.length}
numberOfPodsWithErrors={podsWithErrors.length}
hpa={matchingHpa}
/>
</AccordionSummary>
<AccordionDetails>
<PodsTable
pods={ownedPods}
extraColumns={[READY_COLUMNS, RESOURCE_COLUMNS]}
/>
</AccordionDetails>
</Accordion>
);
}
Example #8
Source File: ServicesAccordions.tsx From backstage with Apache License 2.0 | 6 votes |
ServiceAccordion = ({ service }: ServiceAccordionProps) => {
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<ServiceSummary service={service} />
</AccordionSummary>
<AccordionDetails>
<ServiceCard service={service} />
</AccordionDetails>
</Accordion>
);
}
Example #9
Source File: JobsAccordions.tsx From backstage with Apache License 2.0 | 6 votes |
JobAccordion = ({ job, ownedPods }: JobAccordionProps) => {
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<JobSummary job={job} />
</AccordionSummary>
<AccordionDetails>
<PodsTable pods={ownedPods} />
</AccordionDetails>
</Accordion>
);
}
Example #10
Source File: IngressesAccordions.tsx From backstage with Apache License 2.0 | 6 votes |
IngressAccordion = ({ ingress }: IngressAccordionProps) => {
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<IngressSummary ingress={ingress} />
</AccordionSummary>
<AccordionDetails>
<IngressCard ingress={ingress} />
</AccordionDetails>
</Accordion>
);
}
Example #11
Source File: DeploymentsAccordions.tsx From backstage with Apache License 2.0 | 6 votes |
DeploymentAccordion = ({
deployment,
ownedPods,
matchingHpa,
}: DeploymentAccordionProps) => {
const podNamesWithErrors = useContext(PodNamesWithErrorsContext);
const podsWithErrors = ownedPods.filter(p =>
podNamesWithErrors.has(p.metadata?.name ?? ''),
);
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<DeploymentSummary
deployment={deployment}
numberOfCurrentPods={ownedPods.length}
numberOfPodsWithErrors={podsWithErrors.length}
hpa={matchingHpa}
/>
</AccordionSummary>
<AccordionDetails>
<PodsTable
pods={ownedPods}
extraColumns={[READY_COLUMNS, RESOURCE_COLUMNS]}
/>
</AccordionDetails>
</Accordion>
);
}
Example #12
Source File: DefaultCustomResource.tsx From backstage with Apache License 2.0 | 6 votes |
DefaultCustomResourceAccordion = ({
customResource,
customResourceName,
defaultExpanded,
}: DefaultCustomResourceAccordionProps) => {
return (
<Accordion
defaultExpanded={defaultExpanded}
TransitionProps={{ unmountOnExit: true }}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<DefaultCustomResourceSummary
customResource={customResource}
customResourceName={customResourceName}
/>
</AccordionSummary>
<AccordionDetails>
{customResource.hasOwnProperty('status') && (
<StructuredMetadataTable metadata={customResource.status} />
)}
</AccordionDetails>
</Accordion>
);
}
Example #13
Source File: CronJobsAccordions.tsx From backstage with Apache License 2.0 | 6 votes |
CronJobAccordion = ({ cronJob, ownedJobs }: CronJobAccordionProps) => {
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<CronJobSummary cronJob={cronJob} />
</AccordionSummary>
<AccordionDetails>
<JobsAccordions jobs={ownedJobs.reverse()} />
</AccordionDetails>
</Accordion>
);
}
Example #14
Source File: ActionOutput.tsx From backstage with Apache License 2.0 | 6 votes |
ActionOutput = ({ url, name, className }: ActionOutputProps) => {
const classes = useStyles();
useEffect(() => {}, [url]);
return (
<Accordion TransitionProps={{ unmountOnExit: true }} className={className}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`panel-${name}-content`}
id={`panel-${name}-header`}
IconButtonProps={{
className: classes.button,
}}
>
<Typography variant="button">{name}</Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
Nothing here...
</AccordionDetails>
</Accordion>
);
}
Example #15
Source File: InfoPanel.tsx From backstage-plugin-opsgenie with MIT License | 6 votes |
InfoPanel = (props: Props) => {
const classes = useStyles(props);
const { title, message, children } = props;
return (
<Accordion className={classes.panel}>
<AccordionSummary
expandIcon={<ExpandMoreIconStyled />}
className={classes.summary}
>
<ErrorOutlineStyled />
<Typography className={classes.summaryText} variant="subtitle1">
{title}
</Typography>
</AccordionSummary>
{(message || children) && (
<AccordionDetails>
<Grid container>
{message && (
<Grid item xs={12}>
<Typography className={classes.message} variant="body1">
{message}
</Typography>
</Grid>
)}
{children && (
<Grid item xs={12} className={classes.details}>
{children}
</Grid>
)}
</Grid>
</AccordionDetails>
)}
</Accordion>
);
}
Example #16
Source File: Cluster.tsx From backstage with Apache License 2.0 | 5 votes |
Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => {
const groupedResponses = groupResponses(clusterObjects.resources);
const podNameToMetrics = clusterObjects.podMetrics
.flat()
.reduce((accum, next) => {
const name = next.pod.metadata?.name;
if (name !== undefined) {
accum.set(name, next);
}
return accum;
}, new Map<string, ClientPodStatus>());
return (
<ClusterContext.Provider value={clusterObjects.cluster}>
<GroupedResponsesContext.Provider value={groupedResponses}>
<PodNamesWithMetricsContext.Provider value={podNameToMetrics}>
<PodNamesWithErrorsContext.Provider value={podsWithErrors}>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<ClusterSummary
clusterName={clusterObjects.cluster.name}
totalNumberOfPods={groupedResponses.pods.length}
numberOfPodsWithErrors={podsWithErrors.size}
/>
</AccordionSummary>
<AccordionDetails>
<Grid container direction="column">
<Grid item>
<CustomResources />
</Grid>
<Grid item>
<DeploymentsAccordions />
</Grid>
<Grid item>
<StatefulSetsAccordions />
</Grid>
<Grid item>
<IngressesAccordions />
</Grid>
<Grid item>
<ServicesAccordions />
</Grid>
<Grid item>
<CronJobsAccordions />
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</PodNamesWithErrorsContext.Provider>
</PodNamesWithMetricsContext.Provider>
</GroupedResponsesContext.Provider>
</ClusterContext.Provider>
);
}
Example #17
Source File: ComponentAccordion.tsx From backstage with Apache License 2.0 | 5 votes |
ComponentAccordion = (props: {
title: string;
expanded?: boolean;
Content: () => JSX.Element;
Actions?: () => JSX.Element;
Settings?: () => JSX.Element;
ContextProvider?: (props: any) => JSX.Element;
}) => {
const {
title,
expanded = false,
Content,
Actions,
Settings,
ContextProvider,
...childProps
} = props;
const classes = useStyles();
const [settingsIsExpanded, setSettingsIsExpanded] = React.useState(false);
const [isExpanded, setIsExpanded] = React.useState(expanded);
const handleOpenSettings = (e: any) => {
e.stopPropagation();
setSettingsIsExpanded(prevState => !prevState);
};
const innerContent = (
<>
{Settings && (
<SettingsModal
open={settingsIsExpanded}
close={() => setSettingsIsExpanded(false)}
componentName={title}
>
<Settings />
</SettingsModal>
)}
<Accordion
expanded={isExpanded}
onChange={(_e: any, expandedValue: boolean) =>
setIsExpanded(expandedValue)
}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
{Settings && (
<IconButton
onClick={handleOpenSettings}
className={classes.settingsIconButton}
>
<SettingsIcon />
</IconButton>
)}
<Typography>{title}</Typography>
</AccordionSummary>
<AccordionDetails>
<div className={classes.contentContainer}>
<Content />
{Actions && <Actions />}
</div>
</AccordionDetails>
</Accordion>
</>
);
return ContextProvider ? (
<ContextProvider {...childProps}>{innerContent}</ContextProvider>
) : (
innerContent
);
}
Example #18
Source File: WorkflowRunDetails.tsx From backstage with Apache License 2.0 | 5 votes |
JobListItem = ({
job,
className,
entity,
}: {
job: Job;
className: string;
entity: Entity;
}) => {
const classes = useStyles();
return (
<Accordion TransitionProps={{ unmountOnExit: true }} className={className}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`panel-${name}-content`}
id={`panel-${name}-header`}
IconButtonProps={{
className: classes.button,
}}
>
<Typography variant="button">
{job.name} ({getElapsedTime(job.started_at, job.completed_at)})
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
<TableContainer>
<Table>
{job.steps.map(step => (
<StepView key={step.number} step={step} />
))}
</Table>
</TableContainer>
</AccordionDetails>
{job.status === 'queued' || job.status === 'in_progress' ? (
<WorkflowRunLogs runId={job.id} inProgress entity={entity} />
) : (
<WorkflowRunLogs runId={job.id} inProgress={false} entity={entity} />
)}
</Accordion>
);
}
Example #19
Source File: ActionOutput.tsx From backstage with Apache License 2.0 | 5 votes |
ActionOutput = ({
url,
name,
className,
action,
}: {
url: string;
name: string;
className?: string;
action: BuildStepAction;
}) => {
const classes = useStyles();
const [messages, setMessages] = useState([]);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(actionOutput => {
if (typeof actionOutput !== 'undefined') {
setMessages(
actionOutput.map(({ message }: { message: string }) => message),
);
}
});
}, [url]);
const timeElapsed = durationHumanized(action.start_time, action.end_time);
return (
<Accordion TransitionProps={{ unmountOnExit: true }} className={className}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`panel-${name}-content`}
id={`panel-${name}-header`}
IconButtonProps={{
className: classes.button,
}}
>
<Typography variant="button">
{name} ({timeElapsed})
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
{messages.length === 0 ? (
'Nothing here...'
) : (
<div style={{ height: '20vh', width: '100%' }}>
<LogViewer text={messages.join('\n')} />
</div>
)}
</AccordionDetails>
</Accordion>
);
}
Example #20
Source File: Rollout.tsx From backstage with Apache License 2.0 | 5 votes |
RolloutAccordion = ({
rollout,
ownedPods,
matchingHpa,
defaultExpanded,
}: RolloutAccordionProps) => {
const podNamesWithErrors = useContext(PodNamesWithErrorsContext);
const podsWithErrors = ownedPods.filter(p =>
podNamesWithErrors.has(p.metadata?.name ?? ''),
);
const currentStepIndex = rollout.status?.currentStepIndex ?? 0;
const abortedMessage = findAbortedMessage(rollout);
return (
<Accordion
defaultExpanded={defaultExpanded}
TransitionProps={{ unmountOnExit: true }}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<RolloutSummary
rollout={rollout}
numberOfCurrentPods={ownedPods.length}
numberOfPodsWithErrors={podsWithErrors.length}
hpa={matchingHpa}
/>
</AccordionSummary>
<AccordionDetails>
<div style={{ width: '100%' }}>
<div>
<Typography variant="h6">Rollout status</Typography>
</div>
<div style={{ margin: '1rem' }}>
{abortedMessage && (
<>
{AbortedTitle}
<Typography variant="subtitle2">{abortedMessage}</Typography>
</>
)}
<StepsProgress
aborted={abortedMessage !== undefined}
steps={rollout.spec?.strategy?.canary?.steps ?? []}
currentStepIndex={currentStepIndex}
/>
</div>
<div>
<PodsTable
pods={ownedPods}
extraColumns={[READY_COLUMNS, RESOURCE_COLUMNS]}
/>
</div>
</div>
</AccordionDetails>
</Accordion>
);
}
Example #21
Source File: TransactionContainer.tsx From parity-bridges-ui with GNU General Public License v3.0 | 5 votes |
TransactionContainer = ({ transaction, expanded, selected = false }: Props) => {
const classes = useStyles();
const {
payloadHex,
transactionDisplayPayload,
sourceChain,
targetChain,
sourceAccount,
companionAccount,
senderName,
transferAmount,
type,
status,
steps,
receiverAddress
} = transaction;
const [accordionExpanded, setAccordionExpanded] = useState(expanded);
const onChange = useCallback(() => setAccordionExpanded(!accordionExpanded), [accordionExpanded]);
useEffect(() => {
if (status === TransactionStatusEnum.COMPLETED) {
setAccordionExpanded(false);
}
}, [status]);
return (
<Accordion
expanded={accordionExpanded}
onChange={onChange}
className={cx(classes.accordion, selected ? classes.selectedBorder : '')}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<div className={classes.header}>
<TransactionHeader
type={type}
status={status}
sourceChain={sourceChain}
targetChain={targetChain}
transferAmount={transferAmount}
/>
</div>
</AccordionSummary>
<AccordionDetails>
<Box component="div" width="100%">
<TransactionAccounts
senderName={senderName}
sourceAccount={sourceAccount ? sourceAccount : undefined}
senderCompanionAccount={companionAccount ? companionAccount : undefined}
receiverAddress={receiverAddress ? receiverAddress : undefined}
type={type}
/>
<TransactionSwitchTab payloadHex={payloadHex} transactionDisplayPayload={transactionDisplayPayload}>
<TransactionReceipt steps={steps} />
</TransactionSwitchTab>
</Box>
</AccordionDetails>
</Accordion>
);
}
Example #22
Source File: index.tsx From prism-frontend with MIT License | 5 votes |
function MenuItemMobile({
expanded,
selectAccordion,
classes,
title,
icon,
layersCategories,
}: MenuItemMobileProps) {
const { t } = useSafeTranslation();
const handleChange = (panel: string) => (
event: React.ChangeEvent<{}>,
newExpanded: boolean,
) => {
selectAccordion(newExpanded ? panel : '');
};
return (
<Accordion
key={title}
square
elevation={0}
expanded={expanded === title}
onChange={handleChange(title)}
>
<AccordionSummary
expandIcon={<FontAwesomeIcon icon={faCaretDown} />}
IconButtonProps={{ color: 'inherit', size: 'small' }}
aria-controls={title}
id={title}
>
<img className={classes.icon} src={`/images/${icon}`} alt={title} />
<Typography variant="body2">{t(title)}</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container direction="column">
{layersCategories.map(({ title: categoryTitle, layers, tables }) => (
<MenuSwitch
key={categoryTitle}
title={categoryTitle}
layers={layers}
tables={tables}
/>
))}
</Grid>
</AccordionDetails>
</Accordion>
);
}
Example #23
Source File: FAQItem.tsx From homebase-app with MIT License | 5 votes |
AccordionContent = styled(AccordionDetails)({
flexDirection: "column",
padding: "35px 40px",
background: "rgb(47, 52, 56)",
})
Example #24
Source File: Summary.tsx From UsTaxes with GNU Affero General Public License v3.0 | 4 votes |
F1040Summary = ({ summary }: F1040SummaryProps): ReactElement => (
<>
{(() => {
if (summary.amountOwed !== undefined && summary.amountOwed > 0) {
return (
<div>
<Typography variant="body2" color="textSecondary">
Amount Owed: <Currency value={-summary.amountOwed} />
</Typography>
</div>
)
}
if (summary.refundAmount !== undefined && summary.refundAmount > 0) {
return (
<div>
<Typography variant="body2" color="textSecondary">
Refund Amount: <Currency value={summary.refundAmount} />
</Typography>
</div>
)
}
})()}
<h3>Credits</h3>
<Grid container>
<Grid item zeroMinWidth>
<List>
{summary.credits.map((credit) => (
<BinaryStateListItem key={credit.name} active={credit.allowed}>
<Typography variant="body2" color="textPrimary">
{credit.name}
</Typography>
{(() => {
if (credit.value !== undefined) {
return (
<Typography variant="body2" color="textSecondary">
Credit: <Currency value={credit.value} />
</Typography>
)
}
return <></>
})()}
</BinaryStateListItem>
))}
</List>
</Grid>
</Grid>
{(() => {
if (summary.worksheets.length > 0) {
return (
<Grid container>
<Grid item zeroMinWidth>
<h3>Worksheets</h3>
<List>
{summary.worksheets.map((worksheet, idx) => (
<Accordion key={idx}>
<AccordionSummary expandIcon={<ExpandMore />}>
{worksheet.name}
</AccordionSummary>
<AccordionDetails>
<TableContainer>
<Table size="small">
<TableBody>
{worksheet.lines.map((line) => (
<TableRow key={line.line}>
<TableCell component="th">
Line {line.line}
</TableCell>
<TableCell>
{typeof line.value === 'number' ? (
<Currency
value={displayRound(line.value) ?? 0}
/>
) : (
line.value
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
))}
</List>
</Grid>
</Grid>
)
}
return <></>
})()}
</>
)
Example #25
Source File: ExperimentResults.tsx From abacus with GNU General Public License v2.0 | 4 votes |
/**
* Render the latest analyses for the experiment for each metric assignment as a single condensed table, using only
* the experiment's default analysis strategy.
*/
export default function ExperimentResults({
analyses,
experiment,
metrics,
}: {
analyses: Analysis[]
experiment: ExperimentFull
metrics: Metric[]
debugMode?: boolean
}): JSX.Element {
const classes = useStyles()
const theme = useTheme()
const availableAnalysisStrategies = [
AnalysisStrategy.IttPure,
AnalysisStrategy.MittNoCrossovers,
AnalysisStrategy.MittNoSpammers,
AnalysisStrategy.MittNoSpammersNoCrossovers,
]
if (experiment.exposureEvents) {
availableAnalysisStrategies.push(AnalysisStrategy.PpNaive)
}
const [strategy, setStrategy] = useState<AnalysisStrategy>(() => Experiments.getDefaultAnalysisStrategy(experiment))
const onStrategyChange = (event: React.ChangeEvent<{ value: unknown }>) => {
setStrategy(event.target.value as AnalysisStrategy)
}
const baseVariationId = experiment.variations.find((v) => v.isDefault)?.variationId
const changeVariationId = experiment.variations.find((v) => !v.isDefault)?.variationId
// istanbul ignore next; Shouldn't occur.
if (!baseVariationId || !changeVariationId) {
throw new Error('Missing base or change variations.')
}
const variationDiffKey = `${changeVariationId}_${baseVariationId}`
const indexedMetrics = indexMetrics(metrics)
const analysesByMetricAssignmentId = _.groupBy(analyses, 'metricAssignmentId')
const allMetricAssignmentAnalysesData: MetricAssignmentAnalysesData[] = MetricAssignments.sort(
experiment.metricAssignments,
).map((metricAssignment) => {
const metricAssignmentAnalyses = analysesByMetricAssignmentId[metricAssignment.metricAssignmentId] || []
return {
metricAssignment,
metric: indexedMetrics[metricAssignment.metricId],
analysesByStrategyDateAsc: _.groupBy(
_.orderBy(metricAssignmentAnalyses, ['analysisDatetime'], ['asc']),
'analysisStrategy',
) as Record<AnalysisStrategy, Analysis[]>,
}
})
const metricAssignmentSummaryData = allMetricAssignmentAnalysesData.map(
({ metricAssignment, metric, analysesByStrategyDateAsc }) => ({
experiment,
strategy,
metricAssignment,
metric,
analysesByStrategyDateAsc,
recommendation: Recommendations.getAggregateMetricAssignmentRecommendation(
Object.values(analysesByStrategyDateAsc)
.map(_.last.bind(null))
.filter((x) => x)
.map((analysis) =>
Recommendations.getMetricAssignmentRecommendation(
experiment,
metric,
analysis as Analysis,
variationDiffKey,
),
),
strategy,
),
}),
)
// ### Result Summary Visualizations
const primaryMetricAssignmentAnalysesData = allMetricAssignmentAnalysesData.find(
({ metricAssignment: { isPrimary } }) => isPrimary,
) as MetricAssignmentAnalysesData
const primaryAnalyses = primaryMetricAssignmentAnalysesData.analysesByStrategyDateAsc[strategy] || []
const dates = primaryAnalyses.map(({ analysisDatetime }) => analysisDatetime.toISOString())
const plotlyDataParticipantGraph: Array<Partial<PlotData>> = [
..._.flatMap(experiment.variations, (variation, index) => {
const variationKey = `variation_${variation.variationId}`
return [
{
name: `${variation.name}`,
x: dates,
y: primaryAnalyses.map(({ participantStats: { [variationKey]: variationCount } }) => variationCount),
line: {
color: Visualizations.variantColors[index],
},
mode: 'lines+markers' as const,
type: 'scatter' as const,
},
]
}),
]
// ### Top Level Stats
const primaryMetricLatestAnalysesByStrategy = _.mapValues(
primaryMetricAssignmentAnalysesData.analysesByStrategyDateAsc,
_.last.bind(null),
)
const latestPrimaryMetricAnalysis = primaryMetricLatestAnalysesByStrategy[strategy]
// istanbul ignore next; trivial
const totalParticipants = latestPrimaryMetricAnalysis?.participantStats['total'] ?? 0
const primaryMetricRecommendation = Recommendations.getAggregateMetricAssignmentRecommendation(
Object.values(primaryMetricLatestAnalysesByStrategy)
.filter((x) => x)
.map((analysis) =>
Recommendations.getMetricAssignmentRecommendation(
experiment,
primaryMetricAssignmentAnalysesData.metric,
analysis as Analysis,
variationDiffKey,
),
),
strategy,
)
// We check if there are any analyses at all to show as we want to show what we can to the Experimenter:
const hasAnalyses = allMetricAssignmentAnalysesData.some(
(x) => Object.values(x.analysesByStrategyDateAsc).filter((y) => y).length > 0,
)
const experimentParticipantStats = Analyses.getExperimentParticipantStats(
experiment,
primaryMetricLatestAnalysesByStrategy,
)
const experimentHealthIndicators = [
...Analyses.getExperimentParticipantHealthIndicators(experimentParticipantStats),
...Analyses.getExperimentHealthIndicators(experiment),
]
const maxIndicationSeverity = experimentHealthIndicators
.map(({ indication: { severity } }) => severity)
.sort(
(severityA, severityB) =>
Analyses.healthIndicationSeverityOrder.indexOf(severityB) -
Analyses.healthIndicationSeverityOrder.indexOf(severityA),
)[0]
const maxIndicationSeverityMessage = {
[Analyses.HealthIndicationSeverity.Ok]: 'No issues detected',
[Analyses.HealthIndicationSeverity.Warning]: 'Potential issues',
[Analyses.HealthIndicationSeverity.Error]: 'Serious issues',
}
// ### Metric Assignments Table
const tableColumns = [
{
title: 'Metric (attribution window)',
render: ({ metric, metricAssignment }: { metric: Metric; metricAssignment: MetricAssignment }) => (
<>
<span className={classes.metricAssignmentNameLine}>
<Tooltip title={metric.description}>
<span>{metric.name}</span>
</Tooltip>
({AttributionWindowSecondsToHuman[metricAssignment.attributionWindowSeconds]})
</span>
{metricAssignment.isPrimary && (
<>
<br />
<Attribute name='primary' />
</>
)}
</>
),
cellStyle: {
fontFamily: theme.custom.fonts.monospace,
fontWeight: 600,
minWidth: 450,
},
},
{
title: 'Absolute change',
render: ({
metric,
strategy,
analysesByStrategyDateAsc,
recommendation,
}: {
metric: Metric
strategy: AnalysisStrategy
analysesByStrategyDateAsc: Record<AnalysisStrategy, Analysis[]>
recommendation: Recommendations.Recommendation
}) => {
const latestEstimates = _.last(analysesByStrategyDateAsc[strategy])?.metricEstimates
if (
!latestEstimates ||
recommendation.decision === Recommendations.Decision.ManualAnalysisRequired ||
recommendation.decision === Recommendations.Decision.MissingAnalysis
) {
return null
}
return (
<MetricValueInterval
intervalName={'the absolute change between variations'}
isDifference={true}
metricParameterType={metric.parameterType}
bottomValue={latestEstimates.diffs[variationDiffKey].bottom_95}
topValue={latestEstimates.diffs[variationDiffKey].top_95}
displayTooltipHint={false}
/>
)
},
cellStyle: {
fontFamily: theme.custom.fonts.monospace,
},
},
{
title: 'Relative change (lift)',
render: ({
strategy,
analysesByStrategyDateAsc,
recommendation,
}: {
metric: Metric
strategy: AnalysisStrategy
analysesByStrategyDateAsc: Record<AnalysisStrategy, Analysis[]>
recommendation: Recommendations.Recommendation
}) => {
const latestEstimates = _.last(analysesByStrategyDateAsc[strategy])?.metricEstimates
if (
!latestEstimates?.ratios[variationDiffKey]?.top_95 ||
recommendation.decision === Recommendations.Decision.ManualAnalysisRequired ||
recommendation.decision === Recommendations.Decision.MissingAnalysis
) {
return null
}
return (
<MetricValueInterval
intervalName={'the relative change between variations'}
metricParameterType={MetricParameterType.Conversion}
bottomValue={Analyses.ratioToDifferenceRatio(latestEstimates.ratios[variationDiffKey].bottom_95)}
topValue={Analyses.ratioToDifferenceRatio(latestEstimates.ratios[variationDiffKey].top_95)}
displayTooltipHint={false}
/>
)
},
cellStyle: {
fontFamily: theme.custom.fonts.monospace,
},
},
{
title: 'Analysis',
render: ({
experiment,
recommendation,
}: {
experiment: ExperimentFull
recommendation: Recommendations.Recommendation
}) => {
return <AnalysisDisplay {...{ experiment, analysis: recommendation }} />
},
cellStyle: {
fontFamily: theme.custom.fonts.monospace,
},
},
]
const DetailPanel = [
({
strategy,
analysesByStrategyDateAsc,
metricAssignment,
metric,
recommendation,
}: {
strategy: AnalysisStrategy
analysesByStrategyDateAsc: Record<AnalysisStrategy, Analysis[]>
metricAssignment: MetricAssignment
metric: Metric
recommendation: Recommendations.Recommendation
}) => {
let disabled = recommendation.decision === Recommendations.Decision.ManualAnalysisRequired
// istanbul ignore next; debug only
disabled = disabled && !isDebugMode()
return {
render: () => (
<MetricAssignmentResults
{...{
strategy,
analysesByStrategyDateAsc,
metricAssignment,
metric,
experiment,
recommendation,
variationDiffKey,
}}
/>
),
disabled,
}
},
]
return (
<div className='analysis-latest-results'>
<div className={classes.root}>
{hasAnalyses ? (
<>
{experiment.variations.length > 2 && (
<>
<Alert severity='error'>
<strong>A/B/n analysis is an ALPHA quality feature.</strong>
<br />
<br />
<strong>What's not working:</strong>
<ul>
<li> Metric Assignment Table (Absolute/Relative Change, Analysis.) </li>
<li> Summary text. </li>
<li> Recommendations. </li>
<li> Difference charts. </li>
<li> The health report. </li>
</ul>
<strong>What's working:</strong>
<ul>
<li> Participation stats and charts. </li>
<li> Analysis tables (when you open a metric assignment.)</li>
<li> Variation value charts. </li>
<li> Observed data. </li>
</ul>
</Alert>
<br />
</>
)}
<div className={classes.summary}>
<Paper className={classes.participantsPlotPaper}>
<Typography variant='h3' gutterBottom>
Participants by Variation
</Typography>
<Plot
layout={{
...Visualizations.plotlyLayoutDefault,
margin: {
l: theme.spacing(4),
r: theme.spacing(2),
t: 0,
b: theme.spacing(6),
},
}}
data={plotlyDataParticipantGraph}
className={classes.participantsPlot}
/>
</Paper>
<div className={classes.summaryColumn}>
<Paper className={classes.summaryStatsPaper}>
{latestPrimaryMetricAnalysis && (
<>
<div className={classes.summaryStatsPart}>
<Typography variant='h3' className={classes.summaryStatsStat} color='primary'>
{totalParticipants.toLocaleString('en', { useGrouping: true })}
</Typography>
<Typography variant='subtitle1'>
<strong>analyzed participants</strong> as at{' '}
{formatIsoDate(latestPrimaryMetricAnalysis.analysisDatetime)}
</Typography>
</div>
<div className={classes.summaryStatsPart}>
<Typography variant='h3' className={classes.summaryStatsStat} color='primary'>
<DeploymentRecommendation {...{ experiment, analysis: primaryMetricRecommendation }} />
</Typography>
<Typography variant='subtitle1'>
<strong>primary metric</strong> recommendation
</Typography>
</div>
</>
)}
</Paper>
<Paper
className={clsx(
classes.summaryHealthPaper,
classes[indicationSeverityClassSymbol(maxIndicationSeverity)],
)}
component='a'
// @ts-ignore: Component extensions aren't appearing in types.
href='#health-report'
>
<div className={classes.summaryStats}>
<Typography variant='h3' className={clsx(classes.summaryStatsStat)} color='primary'>
{maxIndicationSeverityMessage[maxIndicationSeverity]}
</Typography>
<Typography variant='subtitle1'>
see <strong>health report</strong>
</Typography>
</div>
</Paper>
</div>
</div>
<Typography variant='h3' className={classes.tableTitle}>
Metric Assignment Results
</Typography>
<MaterialTable
columns={tableColumns}
data={metricAssignmentSummaryData}
options={createStaticTableOptions(metricAssignmentSummaryData.length)}
onRowClick={(_event, rowData, togglePanel) => {
const { recommendation } = rowData as {
recommendation: Recommendations.Recommendation
}
let disabled = recommendation.decision === Recommendations.Decision.ManualAnalysisRequired
// istanbul ignore next; debug only
disabled = disabled && !isDebugMode()
// istanbul ignore else; trivial
if (togglePanel && !disabled) {
togglePanel()
}
}}
detailPanel={DetailPanel}
/>
<Typography variant='h3' className={classes.tableTitle}>
Health Report
</Typography>
<Paper id='health-report'>
<HealthIndicatorTable indicators={experimentHealthIndicators} />
</Paper>
</>
) : (
<Paper className={classes.noAnalysesPaper}>
<Typography variant='h3' gutterBottom>
{' '}
No Results{' '}
</Typography>
<Typography variant='body1'>No results are available at the moment, this can be due to:</Typography>
<ul>
<Typography component='li'>
<strong> An experiment being new. </strong> ExPlat can take 24-48 hours for results to process and
become available. Updates are usually released at 06:00 UTC daily.
</Typography>
<Typography component='li'>
<strong> No assignments occuring. </strong> Check the "Early Monitoring" section below to
ensure that assignments are occuring.
</Typography>
</ul>
</Paper>
)}
<div className={classes.accordions}>
{hasAnalyses && (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant='h5'>Advanced - Choose an Analysis Strategy</Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
<Typography variant='body1'>
Choosing a different analysis strategy is useful for checking the effect of different modelling
decisions on the results:
</Typography>
<ul>
<Typography variant='body1' component='li'>
<strong>All participants:</strong> All the participants are analysed based on their initial
variation assignment. Pure intention-to-treat.
</Typography>
<Typography variant='body1' component='li'>
<strong>Without crossovers:</strong> Same as all participants, but excluding participants that were
assigned to multiple experiment variations before or on the analysis date (aka crossovers). Modified
intention-to-treat.
</Typography>
<Typography variant='body1' component='li'>
<strong>Without spammers:</strong> Same as all participants, but excluding participants that were
flagged as spammers on the analysis date. Modified intention-to-treat.
</Typography>
<Typography variant='body1' component='li'>
<strong>Without crossovers and spammers:</strong> Same as all participants, but excluding both
spammers and crossovers. Modified intention-to-treat.
</Typography>
<Typography variant='body1' component='li'>
<strong>Exposed without crossovers and spammers:</strong> Only participants that triggered one of
the experiment's exposure events, excluding both spammers and crossovers. This analysis
strategy is only available if the experiment has exposure events, while the other four strategies
are used for every experiment. Naive per-protocol.
</Typography>
</ul>
<FormControl>
<InputLabel htmlFor='strategy-selector' id='strategy-selector-label'>
Analysis Strategy:
</InputLabel>
<Select
id='strategy-selector'
labelId='strategy-selector-label'
value={strategy}
onChange={onStrategyChange}
>
{availableAnalysisStrategies.map((strat) => (
<MenuItem key={strat} value={strat}>
{Analyses.AnalysisStrategyToHuman[strat]}
{strat === Experiments.getDefaultAnalysisStrategy(experiment) && ' (recommended)'}
</MenuItem>
))}
</Select>
<FormHelperText>Updates the page data.</FormHelperText>
</FormControl>
</AccordionDetails>
</Accordion>
)}
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant='h5'>Early Monitoring - Live Assignment Event Flow</Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
<Typography variant='body1'>
For early monitoring, you can run this query in Hue to retrieve unfiltered assignment counts from the
unprocessed tracks queue.
</Typography>
<Typography variant='body1'>
This query should only be used to monitor event flow. The best way to use it is to run it multiple times
and ensure that counts go up and are roughly distributed as expected. Counts may also go down as events
are moved to prod_events every day.
</Typography>
<pre className={classes.pre}>
<code>
{/* (Using a javasript string automatically replaces special characters with html entities.) */}
{`with tracks_counts as (
select
cast(json_extract(eventprops, '$.experiment_variation_id') as bigint) as experiment_variation_id,
count(distinct userid) as unique_users
from kafka_staging.etl_events
where
eventname = 'wpcom_experiment_variation_assigned' and
eventprops like '%"experiment_id":"${experiment.experimentId}"%'
group by cast(json_extract(eventprops, '$.experiment_variation_id') as bigint)
)
select
experiment_variations.name as variation_name,
unique_users
from tracks_counts
inner join wpcom.experiment_variations using (experiment_variation_id)`}
</code>
</pre>
</AccordionDetails>
</Accordion>
</div>
</div>
</div>
)
}
Example #26
Source File: index.tsx From TidGi-Desktop with Mozilla Public License 2.0 | 4 votes |
export function AddWorkspace(): JSX.Element {
const { t } = useTranslation();
const [currentTab, currentTabSetter] = useState<CreateWorkspaceTabs>(CreateWorkspaceTabs.CreateNewWiki);
const [isCreateSyncedWorkspace, isCreateSyncedWorkspaceSetter] = useIsCreateSyncedWorkspace();
const [isCreateMainWorkspace, isCreateMainWorkspaceSetter] = useState(true);
const form = useWikiWorkspaceForm();
const [errorInWhichComponent, errorInWhichComponentSetter] = useState<IErrorInWhichComponent>({});
const workspaceList = usePromiseValue(async () => await window.service.workspace.getWorkspacesAsList());
// update storageProviderSetter to local based on isCreateSyncedWorkspace. Other services value will be changed by TokenForm
const { storageProvider, storageProviderSetter, wikiFolderName, wikiPort } = form;
useEffect(() => {
if (!isCreateSyncedWorkspace && storageProvider !== SupportedStorageServices.local) {
storageProviderSetter(SupportedStorageServices.local);
}
}, [isCreateSyncedWorkspace, storageProvider, storageProviderSetter]);
// const [onClickLogin] = useAuth(storageService);
const formProps = {
form: form,
isCreateMainWorkspace: isCreateMainWorkspace,
errorInWhichComponent: errorInWhichComponent,
errorInWhichComponentSetter: errorInWhichComponentSetter,
};
if (workspaceList === undefined) {
return <Container>{t('Loading')}</Container>;
}
return (
<TabContext value={currentTab}>
<div id="test" data-usage="For spectron automating testing" />
<Helmet>
<title>
{t('AddWorkspace.AddWorkspace')} {wikiFolderName}
</title>
</Helmet>
<AppBar position="static">
<Paper square>
<TabList
onChange={(_event, newValue) => currentTabSetter(newValue as CreateWorkspaceTabs)}
variant="scrollable"
value={currentTab}
aria-label={t('AddWorkspace.SwitchCreateNewOrOpenExisted')}>
<Tab label={t('AddWorkspace.CreateNewWiki')} value={CreateWorkspaceTabs.CreateNewWiki} />
<Tab label={t(`AddWorkspace.CloneOnlineWiki`)} value={CreateWorkspaceTabs.CloneOnlineWiki} />
<Tab label={t('AddWorkspace.OpenLocalWiki')} value={CreateWorkspaceTabs.OpenLocalWiki} />
</TabList>
</Paper>
</AppBar>
{/* show advanced options if user have already created a workspace */}
<Accordion defaultExpanded={workspaceList.length > 0}>
<AdvancedSettingsAccordionSummary expandIcon={<ExpandMoreIcon />}>{t('AddWorkspace.Advanced')}</AdvancedSettingsAccordionSummary>
<AccordionDetails>
<SyncedWikiDescription isCreateSyncedWorkspace={isCreateSyncedWorkspace} isCreateSyncedWorkspaceSetter={isCreateSyncedWorkspaceSetter} />
<MainSubWikiDescription isCreateMainWorkspace={isCreateMainWorkspace} isCreateMainWorkspaceSetter={isCreateMainWorkspaceSetter} />
{isCreateMainWorkspace && (
<LocationPickerContainer>
<LocationPickerInput
error={errorInWhichComponent.wikiPort}
onChange={(event) => {
form.wikiPortSetter(Number(event.target.value));
}}
label={t('AddWorkspace.WikiServerPort')}
value={wikiPort}
/>
</LocationPickerContainer>
)}
</AccordionDetails>
</Accordion>
{isCreateSyncedWorkspace && (
<TokenFormContainer>
<TokenForm storageProvider={storageProvider} storageProviderSetter={storageProviderSetter} />
</TokenFormContainer>
)}
{storageProvider !== SupportedStorageServices.local && <GitRepoUrlForm error={errorInWhichComponent.gitRepoUrl} {...formProps} {...formProps.form} />}
<TabPanel value={CreateWorkspaceTabs.CreateNewWiki}>
<Container>
<NewWikiForm {...formProps} isCreateSyncedWorkspace={isCreateSyncedWorkspace} />
<NewWikiDoneButton {...formProps} isCreateSyncedWorkspace={isCreateSyncedWorkspace} />
</Container>
</TabPanel>
<TabPanel value={CreateWorkspaceTabs.CloneOnlineWiki}>
<Container>
<CloneWikiForm {...formProps} isCreateSyncedWorkspaceSetter={isCreateSyncedWorkspaceSetter} />
<CloneWikiDoneButton {...formProps} />
</Container>
</TabPanel>
<TabPanel value={CreateWorkspaceTabs.OpenLocalWiki}>
<Container>
<ExistedWikiForm {...formProps} isCreateSyncedWorkspace={isCreateSyncedWorkspace} />
<ExistedWikiDoneButton {...formProps} isCreateSyncedWorkspace={isCreateSyncedWorkspace} />
</Container>
</TabPanel>
</TabContext>
);
}
Example #27
Source File: Conversation.tsx From prompts-ai with MIT License | 4 votes |
export default function Conversation(props: Props) {
const styles = useStyles();
const dispatch = useDispatch();
const prompt = useSelector(selectPrompt);
const globalCompletionParameters = useSelector(selectCompletionParameters);
const conversation = useSelector((state: RootState) => {
const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!;
return workspace.conversations.find(c => c.id === props.id)!;
});
const hasStarted = conversation.parts.some(c => c.submitted);
useEffect(() => {
conversationBottom.current!.scrollTop = conversationBottom.current!.scrollHeight;
});
const conversationBottom = createRef<HTMLDivElement>();
return <Card className={styles.card}>
<CardContent>
<Grid container alignItems={'center'} justify={'space-between'}>
<Grid item><Typography>
{!hasStarted && (
"New Conversation"
)}
{hasStarted && (
<Box>
<Typography component={'span'}>Conversation #{props.ind}</Typography><br/>
<Typography variant={'caption'} component={'span'}>The prompt and parameters are locked.</Typography>
</Box>
)}
</Typography></Grid>
<Grid item>
{hasStarted && (
<IconButton onClick={() => {
dispatch(deleteConversation(props.id));
}}>
<Delete />
</IconButton>
)}
</Grid>
</Grid>
<Box mt={1} className={styles.conversationBox}>
<Paper className={styles.conversationBox} ref={conversationBottom}>
<Box ml={1} mt={1}>
{hasStarted && (<>
<Typography component={'span'} className={styles.promptedText}>{conversation.initialPrompt}</Typography>
{conversation.parts.map(part => (<>
{part.source === ConversationPartSource.gpt && <Typography component={'span'} className={styles.generatedText}>{part.text}</Typography>}
{part.source === ConversationPartSource.user && <Typography component={'span'} className={styles.promptedText}>{part.text}</Typography>}
</>))}
</>)}
{!hasStarted && (<>
<Typography component={'span'} className={styles.promptedText}>{prompt}</Typography>
<Typography component={'span'} className={styles.promptedText}>{conversation.restartSequence}</Typography>
</>)}
<div />
</Box>
</Paper>
</Box>
<Box mt={2} className={styles.responseInput}>
<Input conversationId={props.id} afterSend={() => {
conversationBottom.current!.scrollTop = conversationBottom.current!.scrollHeight;
}}/>
</Box>
<Box mt={1}>
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>Parameters</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<Grid container spacing={1}>
<Grid item>
<TextField
value={conversation.restartSequence.split('\n').join('\\n')}
onChange={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>) => {
dispatch(updateConversationRestartSequence({
conversationId: props.id,
restartSequence: event.currentTarget.value.split('\\n').join('\n')
}));
}}
className={styles.settingField}
label={'Before User Input'}
variant={'outlined'}
/>
</Grid>
<Grid item>
<TextField
value={conversation.startSequence.split('\n').join('\\n')}
onChange={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>) => {
dispatch(updateConversationStartSequence({
conversationId: props.id,
startSequence: event.currentTarget.value.split('\\n').join('\n')
}));
}}
className={styles.settingField}
label={'Before GPT-3 Completion'}
variant={'outlined'}
/>
</Grid>
</Grid>
<Box mt={1}>
{conversation.completionParams === undefined && (
<CompletionParameters parameters={globalCompletionParameters} />
)}
{conversation.completionParams !== undefined && (
<CompletionParameters parameters={conversation.completionParams} />
)}
</Box>
</Typography>
</AccordionDetails>
</Accordion>
</Box>
</CardContent>
<CardActions>
</CardActions>
</Card>;
}
Example #28
Source File: FilterDrawer.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
FilterDrawer: React.FC<Props> = (props) => {
const { filters, addFilter, removeFilter, facets, clearFilters } = props;
const classes = useStyles();
const filtersByColumn = useMemo(
() =>
filters.reduce(
(allFilters, nextFilter) => ({
...allFilters,
[nextFilter.field]: nextFilter.values
}),
{} as Record<string, string[]>
),
[filters]
);
const portFacet: any[] = facets['services.port']
? facets['services.port'][0].data
: [];
const fromDomainFacet: any[] = facets['fromRootDomain']
? facets['fromRootDomain'][0].data
: [];
const cveFacet: any[] = facets['vulnerabilities.cve']
? facets['vulnerabilities.cve'][0].data
: [];
const severityFacet: any[] = facets['vulnerabilities.severity']
? facets['vulnerabilities.severity'][0].data
: [];
// Always show all severities
for (const value of ['Critical', 'High', 'Medium', 'Low']) {
if (!severityFacet.find((severity) => value === severity.value))
severityFacet.push({ value, count: 0 });
}
return (
<Wrapper>
<div className={classes.header}>
<div className={classes.filter}>
<FaFilter /> <h3>Filter</h3>
</div>
{clearFilters && (
<div>
<button onClick={clearFilters}>Clear All Filters</button>
</div>
)}
</div>
<Accordion elevation={0} square>
<AccordionSummary expandIcon={<ExpandMore />}>
<div>IP(s)</div>
{filtersByColumn['ip']?.length > 0 && <FiltersApplied />}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.details }}>
<TaggedArrayInput
placeholder="IP address"
values={filtersByColumn.ip ?? []}
onAddTag={(value) => addFilter('ip', value, 'any')}
onRemoveTag={(value) => removeFilter('ip', value, 'any')}
/>
</AccordionDetails>
</Accordion>
<Accordion elevation={0} square>
<AccordionSummary expandIcon={<ExpandMore />}>
<div>Domain(s)</div>
{filtersByColumn['name']?.length > 0 && <FiltersApplied />}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.details }}>
<TaggedArrayInput
placeholder="Domain"
values={filtersByColumn.name ?? []}
onAddTag={(value) => addFilter('name', value, 'any')}
onRemoveTag={(value) => removeFilter('name', value, 'any')}
/>
</AccordionDetails>
</Accordion>
{fromDomainFacet.length > 0 && (
<Accordion elevation={0} square>
<AccordionSummary expandIcon={<ExpandMore />}>
<div>Root Domain(s)</div>
{filtersByColumn['fromRootDomain']?.length > 0 && (
<FiltersApplied />
)}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.details }}>
<FacetFilter
options={fromDomainFacet}
selected={filtersByColumn['fromRootDomain'] ?? []}
onSelect={(value) => addFilter('fromRootDomain', value, 'any')}
onDeselect={(value) =>
removeFilter('fromRootDomain', value, 'any')
}
/>
</AccordionDetails>
</Accordion>
)}
{portFacet.length > 0 && (
<Accordion elevation={0} square>
<AccordionSummary expandIcon={<ExpandMore />}>
<div>Port(s)</div>
{filtersByColumn['services.port']?.length > 0 && <FiltersApplied />}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.details }}>
<FacetFilter
options={portFacet}
selected={filtersByColumn['services.port'] ?? []}
onSelect={(value) => addFilter('services.port', value, 'any')}
onDeselect={(value) =>
removeFilter('services.port', value, 'any')
}
/>
</AccordionDetails>
</Accordion>
)}
{cveFacet.length > 0 && (
<Accordion elevation={0} square>
<AccordionSummary expandIcon={<ExpandMore />}>
<div>CVE(s)</div>
{filtersByColumn['vulnerabilities.cve']?.length > 0 && (
<FiltersApplied />
)}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.details }}>
<FacetFilter
options={cveFacet}
selected={filtersByColumn['vulnerabilities.cve'] ?? []}
onSelect={(value) =>
addFilter('vulnerabilities.cve', value, 'any')
}
onDeselect={(value) =>
removeFilter('vulnerabilities.cve', value, 'any')
}
/>
</AccordionDetails>
</Accordion>
)}
{severityFacet.length > 0 && (
<Accordion elevation={0} square>
<AccordionSummary expandIcon={<ExpandMore />}>
<div>Severity</div>
{filtersByColumn['vulnerabilities.severity']?.length > 0 && (
<FiltersApplied />
)}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.details }}>
<FacetFilter
options={severityFacet}
selected={filtersByColumn['vulnerabilities.severity'] ?? []}
onSelect={(value) =>
addFilter('vulnerabilities.severity', value, 'any')
}
onDeselect={(value) =>
removeFilter('vulnerabilities.severity', value, 'any')
}
/>
</AccordionDetails>
</Accordion>
)}
</Wrapper>
);
}
Example #29
Source File: DomainDetails.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
DomainDetails: React.FC<Props> = (props) => {
const { domainId } = props;
const { getDomain } = useDomainApi(false);
const { user } = useAuthContext();
const [domain, setDomain] = useState<Domain>();
const classes = useStyles();
const history = useHistory();
const fetchDomain = useCallback(async () => {
try {
setDomain(undefined);
const result = await getDomain(domainId);
setDomain(result);
} catch (e) {
console.error(e);
}
}, [domainId, getDomain]);
useEffect(() => {
fetchDomain();
}, [fetchDomain]);
const webInfo = useMemo(() => {
if (!domain) {
return [];
}
const categoriesToProducts: Record<string, Set<string>> = {};
for (const service of domain.services) {
for (const product of service.products) {
const version = product.version ? ` ${product.version}` : '';
const value = product.name + version;
const name =
product.tags && product.tags.length > 0 ? product.tags[0] : 'Misc';
if (!categoriesToProducts[name]) {
categoriesToProducts[name] = new Set();
}
categoriesToProducts[name].add(value);
}
}
return Object.entries(categoriesToProducts).reduce(
(acc, [name, value]) => [
...acc,
{
label: name,
value: Array.from(value).join(', ')
}
],
[] as any
);
}, [domain]);
const overviewInfo = useMemo(() => {
if (!domain) {
return [];
}
const ret = [];
if (domain.ip) {
ret.push({
label: 'IP',
value: domain.ip
});
}
ret.push({
label: 'First Seen',
value: `${differenceInCalendarDays(
Date.now(),
parseISO(domain.createdAt)
)} days ago`
});
ret.push({
label: 'Last Seen',
value: `${differenceInCalendarDays(
Date.now(),
parseISO(domain.updatedAt)
)} days ago`
});
if (domain.country) {
ret.push({
label: 'Country',
value: domain.country
});
}
if (domain.cloudHosted) {
ret.push({
label: 'Cloud Hosted',
value: 'Yes'
});
}
ret.push({
label: 'Organization',
value: domain.organization.name
});
return ret;
}, [domain]);
const [hiddenRows, setHiddenRows] = React.useState<{
[key: string]: boolean;
}>({});
const formatBytes = (bytes: number, decimals = 2): string => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
};
const generateWebpageList = (tree: any, prefix = '') => {
return (
<List
className={`${classes.listRoot}${prefix ? ' ' + classes.nested : ''}`}
>
{Object.keys(tree).map((key) => {
const isWebpage =
'url' in tree[key] && typeof tree[key]['url'] === 'string';
if (!isWebpage) {
const newPrefix = prefix + '/' + key;
return (
<>
<ListItem
button
onClick={() => {
setHiddenRows((hiddenRows: any) => {
hiddenRows[newPrefix] =
newPrefix in hiddenRows ? !hiddenRows[newPrefix] : true;
return { ...hiddenRows };
});
}}
key={newPrefix}
>
<ListItemText primary={(prefix ? '' : '/') + key + '/'} />
{hiddenRows[newPrefix] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse
in={!hiddenRows[newPrefix]}
timeout="auto"
unmountOnExit
>
{generateWebpageList(tree[key], newPrefix)}
</Collapse>
</>
);
}
const page = tree[key] as Webpage;
const parsed = new URL(page.url);
const split = parsed.pathname
.replace(/\/$/, '') // Remove trailing slash
.split('/');
return (
<ListItem
button
divider={true}
key={page.url}
onClick={() => window.open(page.url, '_blank')}
>
<ListItemText
primary={(prefix ? '' : '/') + split.pop()}
secondary={
page.status + ' • ' + formatBytes(page.responseSize ?? 0, 1)
}
></ListItemText>
</ListItem>
);
})}
</List>
);
};
if (!domain) {
return null;
}
const url =
(domain.services.find((service) => service.port === 443)
? 'https://'
: 'http://') + domain.name;
const { webpages = [] } = domain;
webpages.sort((a, b) => (a.url > b.url ? 1 : -1));
const webpageTree = generateWebpageTree(webpages);
const webpageList = generateWebpageList(webpageTree);
return (
<Paper classes={{ root: classes.root }}>
<div className={classes.title}>
<h4>
<Link to={`/inventory/domain/${domain.id}`}>{domain.name}</Link>
</h4>
<a href={url} target="_blank" rel="noopener noreferrer">
<LinkOffIcon />
</a>
</div>
<div className={classes.inner}>
{overviewInfo.length > 0 && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Overview</h4>
<DefinitionList items={overviewInfo} />
</div>
)}
{webInfo.length > 0 && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Known Products</h4>
<DefinitionList items={webInfo} />
</div>
)}
{domain.vulnerabilities.length > 0 && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Vulnerabilities</h4>
<Accordion className={classes.accordionHeaderRow} disabled>
<AccordionSummary>
<Typography className={classes.accordionHeading}>
Title
</Typography>
<Typography className={classes.vulnDescription}>
Serverity
</Typography>
<Typography className={classes.vulnDescription}>
State
</Typography>
<Typography className={classes.vulnDescription}>
Created
</Typography>
</AccordionSummary>
</Accordion>
{domain.vulnerabilities.map((vuln) => (
<Accordion
className={classes.accordion}
key={vuln.id}
onClick={(event) => {
event.stopPropagation();
history.push('/inventory/vulnerability/' + vuln.id);
}}
>
<AccordionSummary>
<Typography className={classes.accordionHeading}>
{vuln.title}
</Typography>
<Typography className={classes.vulnDescription}>
{vuln.severity}
</Typography>
<Typography className={classes.vulnDescription}>
{vuln.state}
</Typography>
<Typography className={classes.vulnDescription}>
{vuln.createdAt
? `${differenceInCalendarDays(
Date.now(),
parseISO(vuln.createdAt)
)} days ago`
: ''}
</Typography>
</AccordionSummary>
</Accordion>
))}
</div>
)}
{domain.services.length > 0 && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Ports</h4>
<Accordion className={classes.accordionHeaderRow} disabled>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography className={classes.accordionHeading}>
Port
</Typography>
<Typography className={classes.accordionHeading}>
Products
</Typography>
<Typography className={classes.lastSeen}>Last Seen</Typography>
</AccordionSummary>
</Accordion>
{domain.services.map((service) => {
const products = service.products
.map(
(product) =>
product.name +
(product.version ? ` ${product.version}` : '')
)
.join(', ');
return (
<Accordion className={classes.accordion} key={service.id}>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography className={classes.accordionHeading}>
{service.port}
</Typography>
<Typography className={classes.accordionHeading}>
{products}
</Typography>
<Typography className={classes.lastSeen}>
{service.lastSeen
? `${differenceInCalendarDays(
Date.now(),
parseISO(service.lastSeen)
)} days ago`
: ''}
</Typography>
</AccordionSummary>
{service.products.length > 0 && (
<AccordionDetails>
<DefinitionList
items={[
{
label: 'Products',
value: products
},
{
label: 'Banner',
value:
(user?.userType === 'globalView' ||
user?.userType === 'globalAdmin') &&
service.banner
? service.banner
: 'None'
}
]}
/>
</AccordionDetails>
)}
</Accordion>
);
})}
</div>
)}
{domain.webpages?.length > 0 && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Site Map</h4>
{webpageList}
</div>
)}
</div>
</Paper>
);
}