Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
CarsonF committed Dec 20, 2024
2 parents 2f9799f + cec3837 commit 658bf9a
Show file tree
Hide file tree
Showing 25 changed files with 430 additions and 313 deletions.
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ runs:
node-version: 20

- name: Yarn cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: .yarn/cache
key: yarn-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
if: "contains(github.event.pull_request.body, 'API PR: https://')"

- name: Download API Schema
uses: dawidd6/action-download-artifact@v2
uses: dawidd6/action-download-artifact@v7
with:
repo: SeedCompany/cord-api-v3
workflow: lint.yml
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"@react-editor-js/client": "^2.1.0",
"@react-editor-js/core": "^2.1.0",
"@react-editor-js/server": "^2.1.0",
"@seedcompany/common": ">=0.13 <1",
"@seedcompany/common": ">=0.14 <1",
"ahooks": "^3.7.8",
"body-parser": "^1.20.2",
"compression": "^1.7.4",
Expand Down
111 changes: 111 additions & 0 deletions src/components/PnpValidation/PnPExtractionProblems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Error, Feedback, Warning } from '@mui/icons-material';
import { Stack, Typography } from '@mui/material';
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem2 as TreeItem } from '@mui/x-tree-view/TreeItem2';
import { groupToMapBy } from '@seedcompany/common';
import Markdown, { MarkdownToJSX } from 'markdown-to-jsx';
import { memo } from 'react';
import { PnpProblemSeverity as Severity } from '~/api/schema.graphql';
import { InlineCode } from '../Debug';
import { FormattedNumber } from '../Formatters';
import { Link } from '../Routing';
import { PnpProblemFragment as Problem } from './pnpExtractionResult.graphql';

export interface ProblemTreeProps {
problems: readonly Problem[];
}
export const ProblemTree = memo(function ProblemTree({
problems,
}: ProblemTreeProps) {
return (
<SimpleTreeView itemChildrenIndentation={6 * 8}>
<ProblemList groupIndex={1} problems={problems} />
</SimpleTreeView>
);
});

export const ProblemList = memo(function ProblemList({
problems,
groupIndex,
}: ProblemTreeProps & {
groupIndex: number;
}) {
return [...groupToMapBy(problems, (p) => p.groups[groupIndex])].map(
([group, problems]) => {
const { severity } = problems[0];
if (group && (groupIndex === 1 || problems.length > 1)) {
const currentNode =
groupIndex === 1 ? (
<Stack direction="row" gap={1} alignItems="center">
<SeverityIcon severity={severity} />
<div>
(<FormattedNumber value={problems.length} />){' '}
<Markdown options={mdOptions}>{group}</Markdown>
<Doc problem={problems[0]} />
</div>
</Stack>
) : (
<Markdown options={mdOptions}>{group}</Markdown>
);

return (
<TreeItem key={group} itemId={group} label={currentNode}>
<ProblemList problems={problems} groupIndex={groupIndex + 1} />
</TreeItem>
);
} else {
return problems.map((problem) => (
<TreeItem
key={problem.id}
itemId={problem.id}
label={
<Stack direction="row" gap={1} alignItems="center">
{groupIndex === 1 && <SeverityIcon severity={severity} />}
<div>
<Markdown options={mdOptions}>{problem.message}</Markdown>
{groupIndex === 1 && <Doc problem={problem} />}
</div>
</Stack>
}
/>
));
}
}
);
});

const mdOptions: MarkdownToJSX.Options = {
forceInline: true,
overrides: {
code: InlineCode,
},
};

function Doc({ problem }: { problem: Problem }) {
if (!problem.documentation) {
return null;
}
return (
<Typography color="text.secondary" sx={{ mt: 1 }}>
For more information see the PnP Troubleshooting{' '}
<Link external to={problem.documentation} target="_blank">
Guide
</Link>
</Typography>
);
}

const SeverityIcon = ({ severity }: { severity: Severity }) => {
if (severity === 'Error') {
return <Error color="error" />;
}
if (severity === 'Warning') {
return <Warning color="warning" />;
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (severity === 'Notice') {
return <Feedback color="info" />;
}

return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogProps,
DialogTitle,
} from '@mui/material';
Expand All @@ -12,12 +11,8 @@ export const PnPExtractionResultDialog = ({
...props
}: DialogProps) => (
<Dialog {...props} aria-labelledby="result-dialog-title" maxWidth="md">
<DialogTitle id="result-dialog-title" sx={{ pb: 0 }}>
Problems
</DialogTitle>
<DialogContent dividers sx={{ p: 0, borderTop: 'none' }}>
{children}
</DialogContent>
<DialogTitle id="result-dialog-title">Problems</DialogTitle>
{children}
<DialogActions>
<Button
onClick={() => props.onClose?.({}, 'backdropClick')}
Expand Down
55 changes: 55 additions & 0 deletions src/components/PnpValidation/PnpValidation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { DialogContent, IconButtonProps } from '@mui/material';
import { cmpBy } from '@seedcompany/common';
import { ComponentType, useMemo } from 'react';
import { PnpProblemSeverity as Severity } from '~/api/schema.graphql';
import { ChildrenProp } from '../../common';
import { useDialog } from '../Dialog';
import { ProblemTree, ProblemTreeProps } from './PnPExtractionProblems';
import {
PnpExtractionResultFragment,
PnpExtractionResultFragment as Result,
} from './pnpExtractionResult.graphql';
import { PnPExtractionResultDialog } from './PnpExtractionResultDialog';
import { PnPValidationIcon } from './PnpValidationIcon';

export const PnPValidation = ({
result,
slots,
}: {
result: PnpExtractionResultFragment;
slots?: {
dialogContent?: ComponentType<ChildrenProp>;
problems?: ComponentType<ProblemTreeProps>;
};
} & Pick<IconButtonProps, 'size'>) => {
const [dialog, open] = useDialog();

const problems = useMemo(() => problemsToShow(result), [result]);

const Content = slots?.dialogContent ?? DialogContent;
const ProblemList = slots?.problems ?? ProblemTree;

return (
<>
<PnPValidationIcon problems={problems} size="small" onClick={open} />
<PnPExtractionResultDialog fullWidth {...dialog}>
<Content>
<ProblemList problems={problems} />
</Content>
</PnPExtractionResultDialog>
</>
);
};

const problemsToShow = (result: Result) => {
// Temporarily filter out problems with "Notice" severity
const filteredProblems = result.problems.filter(
(problem) => problem.severity !== 'Notice'
);

return filteredProblems.toSorted(
cmpBy((problem) => priority.indexOf(problem.severity))
);
};

const priority = ['Error', 'Warning', 'Notice'] satisfies Severity[];
49 changes: 49 additions & 0 deletions src/components/PnpValidation/PnpValidationIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Check, Error, Warning } from '@mui/icons-material';
import { Badge, IconButton, IconButtonProps, Tooltip } from '@mui/material';
import { groupToMapBy } from '@seedcompany/common';
import { PnpProblemSeverity as Severity } from '~/api/schema.graphql';
import { useNumberFormatter } from '../Formatters';
import { PnpProblemFragment as Problem } from './pnpExtractionResult.graphql';

export const PnPValidationIcon = ({
problems,
...props
}: {
problems: readonly Problem[];
} & IconButtonProps) => {
const bySev = groupToMapBy(problems, (p) => p.severity);

const formatNumber = useNumberFormatter();
const count = (sev: Severity) => formatNumber(bySev.get(sev)!.length);

const { Icon, color, badge, title } = bySev.has('Error')
? {
Icon: Error,
color: 'error' as const,
badge: count('Error'),
title: `${count('Error')} error(s)`,
}
: bySev.has('Warning')
? {
Icon: Warning,
color: 'warning' as const,
badge: count('Warning'),
title: `${count('Warning')} warning(s)`,
}
: {
Icon: Check,
color: 'success' as const,
badge: undefined,
title: 'Validation Passed!',
};

return (
<Tooltip title={title}>
<Badge badgeContent={badge} max={Infinity}>
<IconButton color={color} {...props}>
<Icon />
</IconButton>
</Badge>
</Tooltip>
);
};
32 changes: 32 additions & 0 deletions src/components/PnpValidation/ProblemsWithSheetsAsTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TabContext, TabList, TabPanel } from '@mui/lab';
import { Box, Tab } from '@mui/material';
import { groupToMapBy } from '@seedcompany/common';
import { memo, ReactNode, useState } from 'react';
import { ProblemTreeProps } from './PnPExtractionProblems';

export const ProblemsWithSheetsAsTabs = memo(function ProblemsWithSheetsAsTabs({
problems,
children,
}: ProblemTreeProps & {
children: (props: ProblemTreeProps & { sheet: string }) => ReactNode;
}) {
const bySheet = groupToMapBy(problems, (p) => p.groups[0]!);
const [tab, setTab] = useState(bySheet.keys().next().value);

return (
<TabContext value={tab}>
<Box sx={{ pl: 1, borderBottom: 1, borderColor: 'divider' }}>
<TabList onChange={(_, next) => setTab(next)}>
{[...bySheet.keys()].toSorted().map((sheet) => (
<Tab key={sheet} value={sheet} label={sheet} />
))}
</TabList>
</Box>
{[...bySheet].map(([sheet, problems]) => (
<TabPanel key={sheet} value={sheet} sx={{ p: 1 }}>
{children({ sheet, problems })}
</TabPanel>
))}
</TabContext>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ fragment pnpProblem on PnpProblem {
severity
message
groups
documentation
}
14 changes: 14 additions & 0 deletions src/components/files/FileActions/PreviewIconButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Preview as PreviewIcon } from '@mui/icons-material';
import { IconButton, Tooltip } from '@mui/material';
import { NonDirectoryActionItem as File, useFileActions } from '../FileActions';

export const PreviewIconButton = ({ file }: { file: File }) => {
const { openFilePreview } = useFileActions();
return (
<Tooltip title="Preview">
<IconButton onClick={() => openFilePreview(file)} size="small">
<PreviewIcon />
</IconButton>
</Tooltip>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,21 @@ export const EditEngagementDialog = ({
await updateEngagement({
variables: { input },
update: (cache) => {
// Invalidate progress reports if engagement date range changes
if (engagement.__typename === 'LanguageEngagement') {
const dirty = form.getState().dirtyFields;

// Invalidate progress reports if engagement date range changes
if (
'engagement.startDateOverride' in dirty ||
'engagement.endDateOverride' in dirty
) {
invalidateProps(cache, engagement, 'progressReports');
}

const language = engagement.language.value;
if (language && 'engagement.firstScripture' in dirty) {
invalidateProps(cache, language, 'firstScripture');
}
}
},
});
Expand Down
3 changes: 3 additions & 0 deletions src/scenes/Engagement/Files/UploadEngagementFiles.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ mutation UploadLanguageEngagementPnp(
products {
...ProductList
}
pnpExtractionResult {
...pnpExtractionResult
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ fragment InternshipEngagementDetail on InternshipEngagement {
canRead
canEdit
}
growthPlan {
canRead
canEdit
value {
...FileNodeInfo
}
}
countryOfOrigin {
canRead
canEdit
Expand Down
Loading

0 comments on commit 658bf9a

Please sign in to comment.