Skip to content

Commit 1cf3080

Browse files
committed
Address code review comments + more component fixes
1 parent b3b2533 commit 1cf3080

15 files changed

Lines changed: 96 additions & 127 deletions

File tree

api/src/api/models/submission.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55
from __future__ import annotations
66

7-
from typing import List
7+
from typing import List, Optional
88

99
from sqlalchemy import ForeignKey
1010
from sqlalchemy.dialects import postgresql
@@ -26,20 +26,27 @@ class Submission(BaseModel): # pylint: disable=too-few-public-methods
2626
__tablename__ = 'submission'
2727

2828
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
29-
submission_json = db.Column(postgresql.JSONB(astext_type=db.Text()), nullable=False, server_default='{}')
30-
survey_id = db.Column(db.Integer, ForeignKey('survey.id', ondelete='CASCADE'), nullable=False)
31-
engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE'), nullable=False)
32-
participant_id = db.Column(db.Integer, ForeignKey('participant.id'), nullable=True)
29+
submission_json = db.Column(postgresql.JSONB(
30+
astext_type=db.Text()), nullable=False, server_default='{}')
31+
survey_id = db.Column(db.Integer, ForeignKey(
32+
'survey.id', ondelete='CASCADE'), nullable=False)
33+
engagement_id = db.Column(db.Integer, ForeignKey(
34+
'engagement.id', ondelete='CASCADE'), nullable=False)
35+
participant_id = db.Column(
36+
db.Integer, ForeignKey('participant.id'), nullable=True)
3337
reviewed_by = db.Column(db.String(50))
3438
review_date = db.Column(db.DateTime)
35-
comment_status_id = db.Column(db.Integer, ForeignKey('comment_status.id', ondelete='SET NULL'))
39+
comment_status_id = db.Column(db.Integer, ForeignKey(
40+
'comment_status.id', ondelete='SET NULL'))
3641
has_personal_info = db.Column(db.Boolean, nullable=True)
3742
has_profanity = db.Column(db.Boolean, nullable=True)
3843
rejected_reason_other = db.Column(db.String(500), nullable=True)
3944
has_threat = db.Column(db.Boolean, nullable=True)
4045
notify_email = db.Column(db.Boolean(), default=True)
41-
comments = db.relationship('Comment', backref='submission', cascade='all, delete')
42-
staff_note = db.relationship('StaffNote', backref='submission', cascade='all, delete')
46+
comments = db.relationship(
47+
'Comment', backref='submission', cascade='all, delete')
48+
staff_note = db.relationship(
49+
'StaffNote', backref='submission', cascade='all, delete')
4350

4451
@classmethod
4552
def get_by_survey_id(cls, survey_id) -> List[SubmissionSchema]:
@@ -118,7 +125,7 @@ def update(cls, submission: SubmissionSchema, session=None) -> Submission:
118125
return query.first()
119126

120127
@classmethod
121-
def update_comment_status(cls, submission_id, comment: dict, session=None) -> Submission:
128+
def update_comment_status(cls, submission_id, comment: dict, session=None) -> Optional[Submission]:
122129
"""Update comment status."""
123130
status_id = comment.get('status_id', None)
124131
has_personal_info = comment.get('has_personal_info', None)

api/src/api/resources/survey.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,21 @@ def get():
8787
exclude_hidden=args.get('exclude_hidden', False, bool),
8888
exclude_template=args.get('exclude_template', False, bool),
8989
search_text=args.get('search_text', '', str),
90-
is_unlinked=args.get('is_unlinked', default=False, type=lambda v: v.lower() == 'true'),
91-
is_linked=args.get('is_linked', default=False, type=lambda v: v.lower() == 'true'),
92-
is_hidden=args.get('is_hidden', default=False, type=lambda v: v.lower() == 'true'),
93-
is_template=args.get('is_template', default=False, type=lambda v: v.lower() == 'true'),
94-
created_date_from=args.get('created_date_from', None, type=str),
90+
is_unlinked=args.get(
91+
'is_unlinked', default=False, type=lambda v: v.lower() == 'true'),
92+
is_linked=args.get('is_linked', default=False,
93+
type=lambda v: v.lower() == 'true'),
94+
is_hidden=args.get('is_hidden', default=False,
95+
type=lambda v: v.lower() == 'true'),
96+
is_template=args.get(
97+
'is_template', default=False, type=lambda v: v.lower() == 'true'),
98+
created_date_from=args.get(
99+
'created_date_from', None, type=str),
95100
created_date_to=args.get('created_date_to', None, type=str),
96-
published_date_from=args.get('published_date_from', None, type=str),
97-
published_date_to=args.get('published_date_to', None, type=str),
101+
published_date_from=args.get(
102+
'published_date_from', None, type=str),
103+
published_date_to=args.get(
104+
'published_date_to', None, type=str),
98105
)
99106

100107
survey_records = SurveyService()\

web/src/components/Form/MultiPageForm.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const MultiPageForm = ({
1818
}: FormSubmitterProps) => {
1919
const [currentPage, setCurrentPage] = useState(0);
2020
const simpleFileOptions = createSimpleFileOptions({ verificationToken });
21-
console.log(savedForm);
2221
const handleScrollUp = () => {
2322
globalThis.scrollTo({
2423
top: 100,

web/src/components/common/Navigation/Breadcrumb.tsx

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Breadcrumbs } from '@mui/material';
2-
import React, { useEffect, useMemo } from 'react';
2+
import React, { useMemo } from 'react';
33
import { BodyText } from '../Typography';
44
import { Link } from '.';
55
import { UIMatch, useMatches } from 'react-router';
@@ -81,7 +81,6 @@ export interface UIMatchWithCrumb extends UIMatch<unknown, UIRouteHandle> {}
8181
export const AutoBreadcrumbs: React.FC<{ smallScreenOnly?: boolean }> = ({ smallScreenOnly }) => {
8282
const matches = (useMatches() as UIMatchWithCrumb[]).filter((match) => match.handle?.crumb);
8383
const matchKey = matches.map((m) => m.pathname).join('|');
84-
const [resolvedCrumbs, setResolvedCrumbs] = React.useState<Record<string, BreadcrumbProps>>({});
8584

8685
const crumbs = useMemo(() => {
8786
return matches.map((match) => {
@@ -96,40 +95,20 @@ export const AutoBreadcrumbs: React.FC<{ smallScreenOnly?: boolean }> = ({ small
9695
});
9796
}, [matchKey]); // Recompute only when matches change
9897

99-
useEffect(() => {
100-
let cancelled = false;
101-
102-
const setNewCrumbs = (
103-
resolvedCrumb: BreadcrumbProps,
104-
previousCrumbs: Record<string, BreadcrumbProps>,
105-
pathname: string,
106-
) => {
107-
const previousCrumb = previousCrumbs[pathname];
108-
109-
// Avoid unnecessary re-renders if the crumb did not actually change.
110-
if (previousCrumb?.name === resolvedCrumb?.name && previousCrumb?.link === resolvedCrumb?.link) {
111-
return previousCrumbs;
112-
}
113-
114-
return {
115-
...previousCrumbs,
116-
[pathname]: resolvedCrumb,
117-
};
118-
};
119-
120-
crumbs.forEach(async (unresolvedCrumb, index) => {
121-
const pathname = matches[index]?.pathname;
122-
if (!pathname) return;
123-
124-
const resolvedCrumb = await unresolvedCrumb;
125-
if (cancelled) return;
126-
setResolvedCrumbs((previousCrumbs) => setNewCrumbs(resolvedCrumb, previousCrumbs, pathname));
127-
});
128-
129-
return () => {
130-
cancelled = true;
131-
};
132-
}, [crumbs, matches]);
98+
const resolvedCrumbs = crumbs.map((unresolvedCrumb) => {
99+
if (unresolvedCrumb instanceof Promise) {
100+
return React.use(unresolvedCrumb);
101+
}
102+
return unresolvedCrumb;
103+
});
104+
105+
const crumbMap = Object.create(null);
106+
resolvedCrumbs.forEach((crumb, index) => {
107+
const pathname = matches[index]?.pathname;
108+
if (pathname) {
109+
crumbMap[pathname] = crumb;
110+
}
111+
});
133112

134113
return (
135114
<Breadcrumbs
@@ -140,7 +119,7 @@ export const AutoBreadcrumbs: React.FC<{ smallScreenOnly?: boolean }> = ({ small
140119
}}
141120
>
142121
{matches.map((match, index) => {
143-
const resolvedCrumb = resolvedCrumbs[match.pathname];
122+
const resolvedCrumb = crumbMap[match.pathname];
144123
if (!resolvedCrumb) return null;
145124
const name = resolvedCrumb?.name;
146125
const link = index < matches.length - 1 ? (resolvedCrumb?.link ?? match.pathname) : undefined;

web/src/components/engagement/admin/EngagementLoaderAdmin.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { redirect, Params } from 'react-router';
1+
import { redirect, LoaderFunctionArgs } from 'react-router';
22
import { getAvailableTranslationLanguages, getEngagement, getEngagementBySlug } from 'services/engagementService';
33
import { getWidgets } from 'services/widgetService';
44
import { getEngagementMetadata, getMetadataTaxa } from 'services/engagementMetadataService';
@@ -23,7 +23,7 @@ export type EngagementLoaderAdminData = {
2323
hasDefaultLanguageTranslation: Promise<boolean>;
2424
};
2525

26-
export const engagementLoaderAdmin = async ({ params }: { params: Params<string> }) => {
26+
export const engagementLoaderAdmin = async ({ params }: LoaderFunctionArgs) => {
2727
const { slug: slugParam, engagementId } = params;
2828
const defaultLanguageCode = AppConfig.language.defaultLanguageId.toLowerCase();
2929
const engagement = (slugParam ? getEngagementBySlug(slugParam) : getEngagement(Number(engagementId))).then(

web/src/components/engagement/admin/create/authoring/authoringLoader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Engagement } from 'models/engagement';
22
import { EngagementDetailsTab } from 'models/engagementDetailsTab';
33
import { SuggestedEngagement } from 'models/suggestedEngagement';
44
import { Survey } from 'models/survey';
5-
import { Params } from 'react-router';
5+
import { LoaderFunctionArgs } from 'react-router';
66
import { getDetailsTabs } from 'services/engagementDetailsTabService';
77
import { getEngagement, getEngagements } from 'services/engagementService';
88
import { getSurveysPage } from 'services/surveyService';
@@ -23,7 +23,7 @@ export type AuthoringLoaderData = {
2323
suggestions: Promise<SuggestedEngagement[]>;
2424
};
2525

26-
const authoringLoader = async ({ params }: { params: Params<string> }) => {
26+
const authoringLoader = async ({ params }: LoaderFunctionArgs) => {
2727
const { engagementId, tenantId, languageCode } = params;
2828
const id = Number(engagementId);
2929
const tId = Number(tenantId);

web/src/components/engagement/preview/engagementPreviewLoader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Params } from 'react-router';
1+
import { LoaderFunctionArgs } from 'react-router';
22
import { getAvailableTranslationLanguages, getEngagement } from 'services/engagementService';
33
import { getWidgets } from 'services/widgetService';
44
import { getEngagementMetadata, getMetadataTaxa } from 'services/engagementMetadataService';
@@ -40,7 +40,7 @@ export type EngagementPreviewLoaderData = {
4040
* Loads all necessary data for previewing an engagement.
4141
* Similar to the public engagement loader but uses engagement ID instead of slug.
4242
*/
43-
export const engagementPreviewLoader = async ({ params }: { params: Params<string> }) => {
43+
export const engagementPreviewLoader = async ({ params }: LoaderFunctionArgs) => {
4444
const { engagementId, languageCode } = params;
4545

4646
if (!engagementId) {

web/src/components/engagement/public/view/EngagementLoaderPublic.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Params } from 'react-router';
1+
import { LoaderFunctionArgs } from 'react-router';
22
import { getAvailableTranslationLanguages, getEngagement, getEngagementBySlug } from 'services/engagementService';
33
import { getWidgets } from 'services/widgetService';
44
import { getEngagementMetadata, getMetadataTaxa } from 'services/engagementMetadataService';
@@ -36,7 +36,7 @@ export type AwaitedEngagementLoaderPublicData = {
3636
translationBundle: TranslationBundle;
3737
};
3838

39-
export const engagementLoaderPublic = async ({ params }: { params: Params<string> }) => {
39+
export const engagementLoaderPublic = async ({ params }: LoaderFunctionArgs) => {
4040
const { slug: slugParam, engagementId, language } = params;
4141
const defaultLanguageCode = AppConfig.language.defaultLanguageId.toLowerCase();
4242
const activeLanguageCode = (language ?? defaultLanguageCode).toLowerCase();

web/src/components/survey/building/SurveyLoader.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ export type SurveyLoaderData = {
1919
};
2020

2121
export const SurveyLoader = async ({ params, pattern }: LoaderFunctionArgs) => {
22-
const { surveyId, token, engagementId: engagementIdParam, slug: slugParam } = params;
22+
const { surveyId: surveyIdParam, token, engagementId: engagementIdParam, slug: slugParam } = params;
23+
const surveyId = Number(surveyIdParam);
2324
const engagementId = Number(engagementIdParam);
24-
if (Number.isNaN(Number(surveyId)) && !Number.isNaN(engagementId) && !slugParam)
25-
throw new Error('Invalid survey ID');
25+
if (Number.isNaN(surveyId) && !Number.isNaN(engagementId) && !slugParam) throw new Error('Invalid survey ID');
2626
const shouldHaveToken = !pattern.startsWith('/manage'); //non-admin users should have a token
2727
if (shouldHaveToken && !token) {
2828
throw new Error('Missing verification token');
@@ -34,18 +34,18 @@ export const SurveyLoader = async ({ params, pattern }: LoaderFunctionArgs) => {
3434
const engagement = slugParam ? getEngagementBySlug(slugParam) : getEngagement(engagementId);
3535
const survey = engagement.then((eng) => {
3636
if (!eng) throw new Error('Engagement not found for slug: ' + slugParam);
37-
if (surveyId && !Number.isNaN(Number(surveyId))) {
38-
return getSurvey(Number(surveyId));
37+
if (surveyId && !Number.isNaN(surveyId)) {
38+
return getSurvey(surveyId);
3939
}
4040
if (eng.surveys && eng.surveys.length > 0) {
4141
return eng.surveys[0];
4242
}
4343
throw new Error('No survey found for engagement with slug: ' + slugParam);
4444
});
4545
return { engagement, survey };
46-
} else if (surveyId && !Number.isNaN(Number(surveyId))) {
46+
} else if (surveyId && !Number.isNaN(surveyId)) {
4747
// If we are accessing the survey directly via the survey ID
48-
const survey = getSurvey(Number(surveyId));
48+
const survey = getSurvey(surveyId);
4949
const engagement = survey.then((surveyData) => {
5050
if (!surveyData.engagement_id) {
5151
throw new Error('Survey is missing engagement ID');

web/src/components/survey/listing/Surveys.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ const Surveys = () => {
454454
}}
455455
/>
456456
}
457-
fullWidth={isMediumScreen ? true : false}
457+
fullWidth={isMediumScreen}
458458
>
459459
Advanced Search
460460
</Button>

0 commit comments

Comments
 (0)