mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-20 18:55:40 +00:00
Share TrueNAS detail table rendering
This commit is contained in:
@@ -25,6 +25,7 @@ import { ResourceOperatorStateSection } from './ResourceOperatorStateSection';
|
||||
import { MaintenanceVerificationSection } from './MaintenanceVerificationSection';
|
||||
import { InlineResourceSummaryTables, ResourceSummaryCards } from './ResourceDetailSummary';
|
||||
import { ResourceInvestigationContextTables } from './ResourceInvestigationContextTables';
|
||||
import { TrueNASDetailSectionTable } from './TrueNASDetailTable';
|
||||
import {
|
||||
RESOURCE_CHANGE_KIND_ORDER,
|
||||
RESOURCE_CHANGE_SOURCE_ADAPTER_ORDER,
|
||||
@@ -42,7 +43,6 @@ import { buildPbsJobHealthEvidenceModel } from './resourceDetailDrawerServiceMod
|
||||
import { ResourceDetailDrawerSupportDisclosure as SupportDisclosure } from './ResourceDetailDrawerSupportDisclosure';
|
||||
import type { UseResourceDetailDrawerStateResult } from './useResourceDetailDrawerState';
|
||||
import type { ResourceDetailDrawerPresentation } from './resourceDetailDrawerPresentation';
|
||||
import type { ResourceDetailDrawerTrueNASSection } from './resourceDetailDrawerTrueNASModel';
|
||||
|
||||
interface ResourceDetailDrawerOverviewTabProps {
|
||||
resource: Resource;
|
||||
@@ -64,59 +64,6 @@ const vmwareRowToneClass = (tone?: 'default' | 'accent' | 'warning'): string =>
|
||||
}
|
||||
};
|
||||
|
||||
const trueNASRowToneClass = (tone?: 'default' | 'accent' | 'warning' | 'success'): string => {
|
||||
switch (tone) {
|
||||
case 'accent':
|
||||
return 'text-cyan-700 dark:text-cyan-300';
|
||||
case 'warning':
|
||||
return 'text-amber-700 dark:text-amber-300';
|
||||
case 'success':
|
||||
return 'text-emerald-700 dark:text-emerald-300';
|
||||
default:
|
||||
return 'text-base-content';
|
||||
}
|
||||
};
|
||||
|
||||
const TrueNASDetailSectionTables: Component<{
|
||||
sections: ResourceDetailDrawerTrueNASSection[];
|
||||
}> = (props) => (
|
||||
<div class="overflow-hidden rounded border border-border bg-surface">
|
||||
<table class="w-full table-fixed text-[11px]">
|
||||
<tbody class="divide-y divide-border">
|
||||
<For each={props.sections}>
|
||||
{(section) => (
|
||||
<>
|
||||
<tr class="bg-surface-alt">
|
||||
<th
|
||||
colspan="2"
|
||||
class="px-2 py-1 text-left text-[10px] font-semibold uppercase tracking-wide text-muted"
|
||||
>
|
||||
{section.label}
|
||||
</th>
|
||||
</tr>
|
||||
<For each={section.rows}>
|
||||
{(row) => (
|
||||
<tr>
|
||||
<td class="w-[38%] px-2 py-1 align-top text-muted">{row.label}</td>
|
||||
<td
|
||||
class={`px-2 py-1 text-right align-top font-medium ${trueNASRowToneClass(
|
||||
row.tone,
|
||||
)}`}
|
||||
title={row.title ?? row.value}
|
||||
>
|
||||
<span class="block truncate">{row.value}</span>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</For>
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
|
||||
const TrueNASDetailsDisclosure: Component<{
|
||||
drawer: UseResourceDetailDrawerStateResult;
|
||||
class?: string;
|
||||
@@ -133,7 +80,7 @@ const TrueNASDetailsDisclosure: Component<{
|
||||
contentClass={props.contentClass ?? 'mt-3 space-y-3'}
|
||||
dataTestId="resource-truenas-details-section"
|
||||
>
|
||||
<TrueNASDetailSectionTables sections={props.drawer.trueNASDetailSections()} />
|
||||
<TrueNASDetailSectionTable sections={props.drawer.trueNASDetailSections()} />
|
||||
</SupportDisclosure>
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { For, type Component } from 'solid-js';
|
||||
import { Table, TableBody, TableCell, TableHead, TableRow } from '@/components/shared/Table';
|
||||
import type { TrueNASDetailSection, TrueNASDetailTone } from './trueNASDetailTableModel';
|
||||
|
||||
export {
|
||||
compactTrueNASDetailRows,
|
||||
compactTrueNASDetailSections,
|
||||
makeTrueNASDetailRow,
|
||||
type TrueNASDetailRow,
|
||||
type TrueNASDetailSection,
|
||||
type TrueNASDetailTone,
|
||||
} from './trueNASDetailTableModel';
|
||||
|
||||
const detailValueToneClass = (tone: TrueNASDetailTone | undefined): string => {
|
||||
if (tone === 'accent') return 'text-cyan-700 dark:text-cyan-300';
|
||||
if (tone === 'success') return 'text-emerald-700 dark:text-emerald-300';
|
||||
if (tone === 'warning') return 'text-amber-700 dark:text-amber-300';
|
||||
if (tone === 'danger') return 'text-rose-700 dark:text-rose-300';
|
||||
if (tone === 'muted') return 'text-muted';
|
||||
return 'text-base-content';
|
||||
};
|
||||
|
||||
const truenasDetailAttribute = (
|
||||
detailKind: 'alert' | 'protection' | 'service' | undefined,
|
||||
detailFor: string,
|
||||
): Record<string, string> =>
|
||||
detailKind ? { [`data-truenas-${detailKind}-detail-for`]: detailFor } : {};
|
||||
|
||||
export const TrueNASDetailSectionTable: Component<{
|
||||
sections: TrueNASDetailSection[];
|
||||
class?: string;
|
||||
}> = (props) => (
|
||||
<div class={props.class ?? 'overflow-hidden rounded border border-border bg-surface'}>
|
||||
<Table class="w-full table-fixed text-[11px]">
|
||||
<TableBody class="divide-y divide-border">
|
||||
<For each={props.sections}>
|
||||
{(section) => (
|
||||
<>
|
||||
<TableRow class="bg-surface-alt">
|
||||
<TableHead
|
||||
colspan={2}
|
||||
class="px-2 py-1 text-left text-[10px] font-semibold uppercase tracking-wide text-muted"
|
||||
>
|
||||
{section.label}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
<For each={section.rows}>
|
||||
{(row) => (
|
||||
<TableRow>
|
||||
<TableCell class="w-[38%] px-2 py-1 align-top text-muted">
|
||||
{row.label}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
class={`px-2 py-1 text-right align-top font-medium ${detailValueToneClass(
|
||||
row.tone,
|
||||
)}`}
|
||||
title={row.title ?? row.value}
|
||||
>
|
||||
<span class="block truncate">{row.value}</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</For>
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const TrueNASInlineDetailTable: Component<{
|
||||
testId: string;
|
||||
detailFor: string;
|
||||
detailKind?: 'alert' | 'protection' | 'service';
|
||||
title: string;
|
||||
summary: string;
|
||||
sections: TrueNASDetailSection[];
|
||||
onClose: () => void;
|
||||
}> = (props) => (
|
||||
<div
|
||||
class="space-y-3"
|
||||
data-testid={props.testId}
|
||||
data-truenas-inline-detail-for={props.detailFor}
|
||||
{...truenasDetailAttribute(props.detailKind, props.detailFor)}
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-[11px] font-medium uppercase tracking-wide text-base-content">
|
||||
{props.title}
|
||||
</div>
|
||||
<div class="mt-1 text-[10px] text-muted">{props.summary}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center rounded-md border border-border bg-surface px-2.5 py-1 text-[10px] font-medium text-base-content transition-colors hover:bg-base"
|
||||
onClick={props.onClose}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<TrueNASDetailSectionTable sections={props.sections} />
|
||||
</div>
|
||||
);
|
||||
@@ -9,20 +9,20 @@ import type {
|
||||
ResourceTrueNASShareMeta,
|
||||
ResourceTrueNASVMMeta,
|
||||
} from '@/types/resource';
|
||||
import {
|
||||
compactTrueNASDetailRows,
|
||||
compactTrueNASDetailSections,
|
||||
makeTrueNASDetailRow,
|
||||
type TrueNASDetailRow,
|
||||
type TrueNASDetailSection,
|
||||
type TrueNASDetailTone,
|
||||
} from './trueNASDetailTableModel';
|
||||
|
||||
export type ResourceDetailDrawerTrueNASRowTone = 'default' | 'accent' | 'warning' | 'success';
|
||||
export type ResourceDetailDrawerTrueNASRowTone = TrueNASDetailTone;
|
||||
|
||||
export type ResourceDetailDrawerTrueNASRow = {
|
||||
label: string;
|
||||
value: string;
|
||||
title?: string;
|
||||
tone?: ResourceDetailDrawerTrueNASRowTone;
|
||||
};
|
||||
export type ResourceDetailDrawerTrueNASRow = TrueNASDetailRow;
|
||||
|
||||
export type ResourceDetailDrawerTrueNASSection = {
|
||||
label: string;
|
||||
rows: ResourceDetailDrawerTrueNASRow[];
|
||||
};
|
||||
export type ResourceDetailDrawerTrueNASSection = TrueNASDetailSection;
|
||||
|
||||
const asString = (value?: string | null): string | null => {
|
||||
const trimmed = value?.trim();
|
||||
@@ -105,27 +105,9 @@ const yesNoValue = (value?: boolean): string | null => {
|
||||
return value ? 'Yes' : 'No';
|
||||
};
|
||||
|
||||
const row = (
|
||||
label: string,
|
||||
value: string | null | undefined,
|
||||
options: Pick<ResourceDetailDrawerTrueNASRow, 'title' | 'tone'> = {},
|
||||
): ResourceDetailDrawerTrueNASRow | null => {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) return null;
|
||||
return { label, value: trimmed, ...options };
|
||||
};
|
||||
|
||||
const compactRows = (
|
||||
rows: Array<ResourceDetailDrawerTrueNASRow | null>,
|
||||
): ResourceDetailDrawerTrueNASRow[] =>
|
||||
rows.filter((entry): entry is ResourceDetailDrawerTrueNASRow => Boolean(entry));
|
||||
|
||||
const compactSections = (
|
||||
sections: Array<ResourceDetailDrawerTrueNASSection | null>,
|
||||
): ResourceDetailDrawerTrueNASSection[] =>
|
||||
sections.filter((section): section is ResourceDetailDrawerTrueNASSection =>
|
||||
Boolean(section && section.rows.length > 0),
|
||||
);
|
||||
const row = makeTrueNASDetailRow;
|
||||
const compactRows = compactTrueNASDetailRows;
|
||||
const compactSections = compactTrueNASDetailSections;
|
||||
|
||||
const isTrueNASScopedResource = (resource: Resource): boolean =>
|
||||
resource.platformType === 'truenas' ||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
export type TrueNASDetailTone = 'default' | 'accent' | 'success' | 'warning' | 'danger' | 'muted';
|
||||
|
||||
export type TrueNASDetailRow = {
|
||||
label: string;
|
||||
value: string;
|
||||
title?: string;
|
||||
tone?: TrueNASDetailTone;
|
||||
};
|
||||
|
||||
export type TrueNASDetailSection = {
|
||||
label: string;
|
||||
rows: TrueNASDetailRow[];
|
||||
};
|
||||
|
||||
export const makeTrueNASDetailRow = (
|
||||
label: string,
|
||||
value?: string | null,
|
||||
options: Pick<TrueNASDetailRow, 'title' | 'tone'> = {},
|
||||
): TrueNASDetailRow | null => {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed || trimmed === '-') return null;
|
||||
return { label, value: trimmed, ...options };
|
||||
};
|
||||
|
||||
export const compactTrueNASDetailRows = (
|
||||
rows: Array<TrueNASDetailRow | null>,
|
||||
): TrueNASDetailRow[] => rows.filter((row): row is TrueNASDetailRow => Boolean(row));
|
||||
|
||||
export const compactTrueNASDetailSections = (
|
||||
sections: Array<TrueNASDetailSection | null>,
|
||||
): TrueNASDetailSection[] =>
|
||||
sections.filter((section): section is TrueNASDetailSection =>
|
||||
Boolean(section && section.rows.length > 0),
|
||||
);
|
||||
@@ -33,12 +33,12 @@ import {
|
||||
} from './truenasPageModel';
|
||||
import {
|
||||
TrueNASInlineDetailTable,
|
||||
compactTrueNASInlineDetailRows,
|
||||
compactTrueNASInlineDetailSections,
|
||||
makeTrueNASInlineDetailRow,
|
||||
type TrueNASInlineDetailSection,
|
||||
type TrueNASInlineDetailTone,
|
||||
} from './TrueNASInlineDetailTable';
|
||||
compactTrueNASDetailRows,
|
||||
compactTrueNASDetailSections,
|
||||
makeTrueNASDetailRow,
|
||||
type TrueNASDetailSection,
|
||||
type TrueNASDetailTone,
|
||||
} from '@/components/Infrastructure/TrueNASDetailTable';
|
||||
import type { Resource, ResourceType } from '@/types/resource';
|
||||
import { getAlertFilteredEmptyState } from '@/utils/alertOverviewPresentation';
|
||||
|
||||
@@ -124,8 +124,8 @@ const formatStartedAt = (value: string | undefined): string => {
|
||||
});
|
||||
};
|
||||
|
||||
type AlertDetailTone = TrueNASInlineDetailTone;
|
||||
type AlertDetailSection = TrueNASInlineDetailSection;
|
||||
type AlertDetailTone = TrueNASDetailTone;
|
||||
type AlertDetailSection = TrueNASDetailSection;
|
||||
|
||||
const detailDateTime = (value?: string): string | null => {
|
||||
if (!value) return null;
|
||||
@@ -140,9 +140,9 @@ const detailDateTime = (value?: string): string | null => {
|
||||
});
|
||||
};
|
||||
|
||||
const detailRow = makeTrueNASInlineDetailRow;
|
||||
const compactDetailRows = compactTrueNASInlineDetailRows;
|
||||
const compactDetailSections = compactTrueNASInlineDetailSections;
|
||||
const detailRow = makeTrueNASDetailRow;
|
||||
const compactDetailRows = compactTrueNASDetailRows;
|
||||
const compactDetailSections = compactTrueNASDetailSections;
|
||||
|
||||
const alertTone = (severity: TrueNASIncidentRow['severityBucket']): AlertDetailTone => {
|
||||
if (severity === 'critical') return 'danger';
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
import { For, type Component } from 'solid-js';
|
||||
import { Table, TableBody, TableCell, TableHead, TableRow } from '@/components/shared/Table';
|
||||
|
||||
export type TrueNASInlineDetailTone = 'default' | 'success' | 'warning' | 'danger' | 'muted';
|
||||
|
||||
export type TrueNASInlineDetailRow = {
|
||||
label: string;
|
||||
value: string;
|
||||
title?: string;
|
||||
tone?: TrueNASInlineDetailTone;
|
||||
};
|
||||
|
||||
export type TrueNASInlineDetailSection = {
|
||||
label: string;
|
||||
rows: TrueNASInlineDetailRow[];
|
||||
};
|
||||
|
||||
export const makeTrueNASInlineDetailRow = (
|
||||
label: string,
|
||||
value?: string | null,
|
||||
options: Pick<TrueNASInlineDetailRow, 'title' | 'tone'> = {},
|
||||
): TrueNASInlineDetailRow | null => {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed || trimmed === '-') return null;
|
||||
return { label, value: trimmed, ...options };
|
||||
};
|
||||
|
||||
export const compactTrueNASInlineDetailRows = (
|
||||
rows: Array<TrueNASInlineDetailRow | null>,
|
||||
): TrueNASInlineDetailRow[] => rows.filter((row): row is TrueNASInlineDetailRow => Boolean(row));
|
||||
|
||||
export const compactTrueNASInlineDetailSections = (
|
||||
sections: Array<TrueNASInlineDetailSection | null>,
|
||||
): TrueNASInlineDetailSection[] =>
|
||||
sections.filter((section): section is TrueNASInlineDetailSection =>
|
||||
Boolean(section && section.rows.length > 0),
|
||||
);
|
||||
|
||||
const detailValueToneClass = (tone: TrueNASInlineDetailTone | undefined): string => {
|
||||
if (tone === 'success') return 'text-emerald-700 dark:text-emerald-300';
|
||||
if (tone === 'warning') return 'text-amber-700 dark:text-amber-300';
|
||||
if (tone === 'danger') return 'text-rose-700 dark:text-rose-300';
|
||||
if (tone === 'muted') return 'text-muted';
|
||||
return 'text-base-content';
|
||||
};
|
||||
|
||||
const truenasDetailAttribute = (
|
||||
detailKind: 'alert' | 'protection' | 'service' | undefined,
|
||||
detailFor: string,
|
||||
): Record<string, string> =>
|
||||
detailKind ? { [`data-truenas-${detailKind}-detail-for`]: detailFor } : {};
|
||||
|
||||
export const TrueNASInlineDetailTable: Component<{
|
||||
testId: string;
|
||||
detailFor: string;
|
||||
detailKind?: 'alert' | 'protection' | 'service';
|
||||
title: string;
|
||||
summary: string;
|
||||
sections: TrueNASInlineDetailSection[];
|
||||
onClose: () => void;
|
||||
}> = (props) => (
|
||||
<div
|
||||
class="space-y-3"
|
||||
data-testid={props.testId}
|
||||
data-truenas-inline-detail-for={props.detailFor}
|
||||
{...truenasDetailAttribute(props.detailKind, props.detailFor)}
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-[11px] font-medium uppercase tracking-wide text-base-content">
|
||||
{props.title}
|
||||
</div>
|
||||
<div class="mt-1 text-[10px] text-muted">{props.summary}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center rounded-md border border-border bg-surface px-2.5 py-1 text-[10px] font-medium text-base-content transition-colors hover:bg-base"
|
||||
onClick={props.onClose}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-hidden rounded border border-border bg-surface">
|
||||
<Table class="w-full table-fixed text-[11px]">
|
||||
<TableBody class="divide-y divide-border">
|
||||
<For each={props.sections}>
|
||||
{(section) => (
|
||||
<>
|
||||
<TableRow class="bg-surface-alt">
|
||||
<TableHead
|
||||
colspan={2}
|
||||
class="px-2 py-1 text-left text-[10px] font-semibold uppercase tracking-wide text-muted"
|
||||
>
|
||||
{section.label}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
<For each={section.rows}>
|
||||
{(row) => (
|
||||
<TableRow>
|
||||
<TableCell class="w-[38%] px-2 py-1 align-top text-muted">
|
||||
{row.label}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
class={`px-2 py-1 text-right align-top font-medium ${detailValueToneClass(row.tone)}`}
|
||||
title={row.title ?? row.value}
|
||||
>
|
||||
<span class="block truncate">{row.value}</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</For>
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -46,12 +46,12 @@ import {
|
||||
} from './truenasPageModel';
|
||||
import {
|
||||
TrueNASInlineDetailTable,
|
||||
compactTrueNASInlineDetailRows,
|
||||
compactTrueNASInlineDetailSections,
|
||||
makeTrueNASInlineDetailRow,
|
||||
type TrueNASInlineDetailSection,
|
||||
type TrueNASInlineDetailTone,
|
||||
} from './TrueNASInlineDetailTable';
|
||||
compactTrueNASDetailRows,
|
||||
compactTrueNASDetailSections,
|
||||
makeTrueNASDetailRow,
|
||||
type TrueNASDetailSection,
|
||||
type TrueNASDetailTone,
|
||||
} from '@/components/Infrastructure/TrueNASDetailTable';
|
||||
|
||||
const TRUENAS_PROTECTION_STATUS_OPTIONS: PlatformTableFilterOption<TrueNASProtectionStatusFilter>[] =
|
||||
[
|
||||
@@ -185,8 +185,8 @@ const formatPointTime = (point: RecoveryPoint): string => {
|
||||
const sizeLabel = (point: RecoveryPoint): string =>
|
||||
typeof point.sizeBytes === 'number' && point.sizeBytes > 0 ? formatBytes(point.sizeBytes) : '-';
|
||||
|
||||
type ProtectionDetailTone = TrueNASInlineDetailTone;
|
||||
type ProtectionDetailSection = TrueNASInlineDetailSection;
|
||||
type ProtectionDetailTone = TrueNASDetailTone;
|
||||
type ProtectionDetailSection = TrueNASDetailSection;
|
||||
|
||||
const detailBool = (value?: boolean | null): string | null => {
|
||||
if (value == null) return null;
|
||||
@@ -207,9 +207,9 @@ const detailDateTime = (value?: string | null): string | null => {
|
||||
});
|
||||
};
|
||||
|
||||
const detailRow = makeTrueNASInlineDetailRow;
|
||||
const compactDetailRows = compactTrueNASInlineDetailRows;
|
||||
const compactDetailSections = compactTrueNASInlineDetailSections;
|
||||
const detailRow = makeTrueNASDetailRow;
|
||||
const compactDetailRows = compactTrueNASDetailRows;
|
||||
const compactDetailSections = compactTrueNASDetailSections;
|
||||
|
||||
const outcomeTone = (point: RecoveryPoint): ProtectionDetailTone => {
|
||||
const outcome = normalizeRecoveryOutcome(point.outcome);
|
||||
|
||||
@@ -34,12 +34,12 @@ import {
|
||||
} from './truenasPageModel';
|
||||
import {
|
||||
TrueNASInlineDetailTable,
|
||||
compactTrueNASInlineDetailRows,
|
||||
compactTrueNASInlineDetailSections,
|
||||
makeTrueNASInlineDetailRow,
|
||||
type TrueNASInlineDetailSection,
|
||||
type TrueNASInlineDetailTone,
|
||||
} from './TrueNASInlineDetailTable';
|
||||
compactTrueNASDetailRows,
|
||||
compactTrueNASDetailSections,
|
||||
makeTrueNASDetailRow,
|
||||
type TrueNASDetailSection,
|
||||
type TrueNASDetailTone,
|
||||
} from '@/components/Infrastructure/TrueNASDetailTable';
|
||||
|
||||
const TRUENAS_SERVICE_STATUS_OPTIONS: PlatformTableFilterOption<TrueNASServiceStatusFilter>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
@@ -90,17 +90,17 @@ const serviceStatusVariant = (
|
||||
return 'muted';
|
||||
};
|
||||
|
||||
type ServiceDetailTone = TrueNASInlineDetailTone;
|
||||
type ServiceDetailSection = TrueNASInlineDetailSection;
|
||||
type ServiceDetailTone = TrueNASDetailTone;
|
||||
type ServiceDetailSection = TrueNASDetailSection;
|
||||
|
||||
const detailBool = (value?: boolean): string | null => {
|
||||
if (value === undefined) return null;
|
||||
return value ? 'Enabled' : 'Disabled';
|
||||
};
|
||||
|
||||
const detailRow = makeTrueNASInlineDetailRow;
|
||||
const compactDetailRows = compactTrueNASInlineDetailRows;
|
||||
const compactDetailSections = compactTrueNASInlineDetailSections;
|
||||
const detailRow = makeTrueNASDetailRow;
|
||||
const compactDetailRows = compactTrueNASDetailRows;
|
||||
const compactDetailSections = compactTrueNASDetailSections;
|
||||
|
||||
const serviceTone = (row: TrueNASServiceRow): ServiceDetailTone => {
|
||||
const status = mapTrueNASServiceStatus(row);
|
||||
|
||||
Reference in New Issue
Block a user